From c4f60a189b81906fa00297f066e57f725c81550a Mon Sep 17 00:00:00 2001 From: Frederic Beaudoin Date: Fri, 6 Sep 2024 07:55:07 -0400 Subject: [PATCH 1/5] Add commerce product wrapper https://coveord.atlassian.net/browse/KIT-3268 --- .../atomic-commerce-product-wrapper.tsx | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 packages/atomic/storybookUtils/commerce/atomic-commerce-product-wrapper.tsx diff --git a/packages/atomic/storybookUtils/commerce/atomic-commerce-product-wrapper.tsx b/packages/atomic/storybookUtils/commerce/atomic-commerce-product-wrapper.tsx new file mode 100644 index 00000000000..9c171f571df --- /dev/null +++ b/packages/atomic/storybookUtils/commerce/atomic-commerce-product-wrapper.tsx @@ -0,0 +1,66 @@ +import {CommerceEngineConfiguration} from '@coveo/headless/commerce'; +import {Decorator} from '@storybook/web-components'; +import {render} from 'lit-html'; +import {html} from 'lit-html/static.js'; +import {unsafeHTML} from 'lit/directives/unsafe-html.js'; + +interface Request extends RequestInit { + url: string; +} + +const preprocessRequestForOneResult = (r: Request) => { + if ( + (r.headers as unknown as Record)['Content-Type'] === + 'application/json' + ) { + const bodyParsed = JSON.parse(r.body as string); + bodyParsed.perPage = 1; + r.body = JSON.stringify(bodyParsed); + } + return r; +}; + +export const wrapInProduct = ( + engineConfig?: Partial +): { + decorator: Decorator; + engineConfig: Partial; +} => ({ + decorator: (story) => { + const tempResultTemplate = document.createElement('div'); + render(html`${story()}`, tempResultTemplate); + tempResultTemplate.replaceChildren( + ...Array.from(tempResultTemplate.children) + ); + return html` +
+ + + ${unsafeHTML( + `` + )} + + +
Template
+
+ +
${unsafeHTML(tempResultTemplate.innerHTML)}
+ `; + }, + engineConfig: { + preprocessRequest: preprocessRequestForOneResult, + ...engineConfig, + }, +}); From e7759dc04113fd642cab4b03da61ef5b12b4e9fc Mon Sep 17 00:00:00 2001 From: Frederic Beaudoin Date: Fri, 6 Sep 2024 07:55:42 -0400 Subject: [PATCH 2/5] Add default product image story https://coveord.atlassian.net/browse/KIT-3268 --- .../atomic-product-image.new.stories.tsx | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.new.stories.tsx diff --git a/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.new.stories.tsx b/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.new.stories.tsx new file mode 100644 index 00000000000..8282f9ce4a0 --- /dev/null +++ b/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.new.stories.tsx @@ -0,0 +1,27 @@ +import {Meta, StoryObj as Story} from '@storybook/web-components'; +import {wrapInProduct} from '../../../../../storybookUtils/commerce/atomic-commerce-product-wrapper'; +import {wrapInCommerceInterface} from '../../../../../storybookUtils/commerce/commerce-interface-wrapper'; +import {parameters} from '../../../../../storybookUtils/common/common-meta-parameters'; +import {renderComponent} from '../../../../../storybookUtils/common/render-component'; + +const {decorator: resultDecorator, engineConfig} = wrapInProduct(); +const {decorator: searchInterfaceDecorator, play} = wrapInCommerceInterface({ + engineConfig, + type: 'search', +}); + +const meta: Meta = { + component: 'atomic-product-image', + title: 'Atomic-Commerce/Product Template Components/ProductImage', + id: 'atomic-product-image', + render: renderComponent, + decorators: [resultDecorator, searchInterfaceDecorator], + parameters, + play, +}; + +export default meta; + +export const Default: Story = { + name: 'atomic-product-image', +}; From ae2780eb20754952bdafed31cca23b4aed82057a Mon Sep 17 00:00:00 2001 From: Frederic Beaudoin Date: Fri, 6 Sep 2024 07:56:11 -0400 Subject: [PATCH 3/5] Add setImages function https://coveord.atlassian.net/browse/KIT-3268 --- .../atomic-product-image/e2e/fixture.ts | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/e2e/fixture.ts b/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/e2e/fixture.ts index ca73e2a7976..ff7d1f91b90 100644 --- a/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/e2e/fixture.ts +++ b/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/e2e/fixture.ts @@ -1,4 +1,6 @@ -import {test as base} from '@playwright/test'; +import {SortBy} from '@coveo/headless/commerce'; +import {CommerceSuccessResponse} from '@coveo/headless/dist/definitions/api/commerce/common/response'; +import {test as base, Page} from '@playwright/test'; import { AxeFixture, makeAxeBuilder, @@ -16,3 +18,52 @@ export const test = base.extend({ }, }); export {expect} from '@playwright/test'; + +export async function setImages(page: Page, urls: string[]) { + await page.route('**/v2/search', async (route) => { + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + facets: [], + pagination: { + page: 0, + perPage: 1, + totalEntries: 1, + totalPages: 1, + }, + products: [ + { + additionalFields: {}, + children: [], + clickUri: '', + ec_brand: '', + ec_category: [], + ec_color: '', + ec_description: '', + ec_gender: '', + ec_images: urls, + ec_in_stock: true, + ec_item_group_id: '', + ec_listing: '', + ec_name: 'name', + ec_price: 0, + ec_product_id: '', + ec_promo_price: 0, + ec_rating: 0, + ec_shortdesc: '', + ec_thumbnails: urls, + permanentid: 'permanentid', + totalNumberOfChildren: 0, + }, + ], + responseId: '', + sort: { + appliedSort: {sortCriteria: SortBy.Relevance}, + availableSorts: [], + }, + triggers: [], + } as CommerceSuccessResponse), + }); + }); +} From 269ac35da87a1ea225d2b6d7e57b962910696ef4 Mon Sep 17 00:00:00 2001 From: Frederic Beaudoin Date: Fri, 6 Sep 2024 12:49:38 -0400 Subject: [PATCH 4/5] Tag @alpha https://coveord.atlassian.net/browse/KIT-3268 --- .../atomic-product-image/atomic-product-image.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.tsx b/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.tsx index ad5941153fc..94be915b12f 100644 --- a/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.tsx +++ b/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.tsx @@ -16,7 +16,7 @@ type Image = { /** * The `atomic-product-image` component renders an image from a product field. - * @internal + * @alpha */ @Component({ tag: 'atomic-product-image', From 839bbb3e23c7d51a5890b7d0eda0483882f2ee20 Mon Sep 17 00:00:00 2001 From: Frederic Beaudoin Date: Fri, 6 Sep 2024 12:49:58 -0400 Subject: [PATCH 5/5] Add stories https://coveord.atlassian.net/browse/KIT-3268 --- .../atomic-product-image.new.stories.tsx | 60 +++++++++++++++++-- .../atomic-commerce-product-wrapper.tsx | 44 +++++--------- 2 files changed, 69 insertions(+), 35 deletions(-) diff --git a/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.new.stories.tsx b/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.new.stories.tsx index 8282f9ce4a0..9180d682dd4 100644 --- a/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.new.stories.tsx +++ b/packages/atomic/src/components/commerce/product-template-components/atomic-product-image/atomic-product-image.new.stories.tsx @@ -1,13 +1,17 @@ +/* eslint-disable @cspell/spellchecker */ import {Meta, StoryObj as Story} from '@storybook/web-components'; import {wrapInProduct} from '../../../../../storybookUtils/commerce/atomic-commerce-product-wrapper'; -import {wrapInCommerceInterface} from '../../../../../storybookUtils/commerce/commerce-interface-wrapper'; +import { + playExecuteFirstSearch, + wrapInCommerceInterface, +} from '../../../../../storybookUtils/commerce/commerce-interface-wrapper'; import {parameters} from '../../../../../storybookUtils/common/common-meta-parameters'; import {renderComponent} from '../../../../../storybookUtils/common/render-component'; -const {decorator: resultDecorator, engineConfig} = wrapInProduct(); -const {decorator: searchInterfaceDecorator, play} = wrapInCommerceInterface({ +const {decorator: productDecorator, engineConfig} = wrapInProduct(); +const {decorator: commerceInterfaceDecorator, play} = wrapInCommerceInterface({ + skipFirstSearch: true, engineConfig, - type: 'search', }); const meta: Meta = { @@ -15,7 +19,7 @@ const meta: Meta = { title: 'Atomic-Commerce/Product Template Components/ProductImage', id: 'atomic-product-image', render: renderComponent, - decorators: [resultDecorator, searchInterfaceDecorator], + decorators: [productDecorator, commerceInterfaceDecorator], parameters, play, }; @@ -24,4 +28,50 @@ export default meta; export const Default: Story = { name: 'atomic-product-image', + play: async (context) => { + await play(context); + await playExecuteFirstSearch(context); + }, +}; + +const {play: playWithMultipleImages} = wrapInCommerceInterface({ + engineConfig: { + ...engineConfig, + preprocessRequest: (r) => { + const parsed = JSON.parse(r.body as string); + parsed.query = 'https://sports.barca.group/pdp/SP00003_00001'; + r.body = JSON.stringify(parsed); + return r; + }, + }, +}); + +export const WithMultipleImages: Story = { + name: 'With multiple images', + play: async (context) => { + await playWithMultipleImages(context); + }, +}; + +export const WithNoImage: Story = { + name: 'With no image', + args: { + 'attributes-field': 'ec_invalid_image_field', + }, + play: async (context) => { + await play(context); + await playExecuteFirstSearch(context); + }, +}; + +export const WitCustomFallbackImage: Story = { + name: 'With custom fallback', + args: { + 'attributes-field': 'ec_invalid_image_field', + 'attributes-fallback': 'https://sports.barca.group/logos/barca.svg', + }, + play: async (context) => { + await play(context); + await playExecuteFirstSearch(context); + }, }; diff --git a/packages/atomic/storybookUtils/commerce/atomic-commerce-product-wrapper.tsx b/packages/atomic/storybookUtils/commerce/atomic-commerce-product-wrapper.tsx index 9c171f571df..714f9f34272 100644 --- a/packages/atomic/storybookUtils/commerce/atomic-commerce-product-wrapper.tsx +++ b/packages/atomic/storybookUtils/commerce/atomic-commerce-product-wrapper.tsx @@ -4,22 +4,6 @@ import {render} from 'lit-html'; import {html} from 'lit-html/static.js'; import {unsafeHTML} from 'lit/directives/unsafe-html.js'; -interface Request extends RequestInit { - url: string; -} - -const preprocessRequestForOneResult = (r: Request) => { - if ( - (r.headers as unknown as Record)['Content-Type'] === - 'application/json' - ) { - const bodyParsed = JSON.parse(r.body as string); - bodyParsed.perPage = 1; - r.body = JSON.stringify(bodyParsed); - } - return r; -}; - export const wrapInProduct = ( engineConfig?: Partial ): { @@ -35,32 +19,32 @@ export const wrapInProduct = ( return html`
- + ${unsafeHTML( `` )} -
Template
+
+ Product template +
- +
${unsafeHTML(tempResultTemplate.innerHTML)}
`; }, engineConfig: { - preprocessRequest: preprocessRequestForOneResult, + preprocessRequest: (r) => { + const parsed = JSON.parse(r.body as string); + parsed.query = 'SP04970_00007'; + r.body = JSON.stringify(parsed); + return r; + }, ...engineConfig, }, });