Skip to content

Commit

Permalink
feat(atomic): use "product" instead of "result" in commerce components (
Browse files Browse the repository at this point in the history
#4139)

Adding translation for query-summary and load-more components.

Let me know if you think creating new components instead would make more
sense

https://coveord.atlassian.net/browse/KIT-3348

---------

Co-authored-by: Frederic Beaudoin <[email protected]>
  • Loading branch information
y-lakhdar and fbeaudoincoveo authored Jul 8, 2024
1 parent dff3be4 commit e63610e
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@ export class AtomicLoadMoreProducts {
from={this.lastProduct}
to={this.paginationState.totalEntries}
i18n={i18n}
label="showing-products-of-load-more"
/>
<LoadMoreProgressBar
from={this.lastProduct}
to={this.paginationState.totalEntries}
/>
<LoadMoreButton
i18n={i18n}
label={'load-more-products'}
moreAvailable={this.lastProduct < this.paginationState.totalEntries}
onClick={() => this.onClick()}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ test('should be A11y compliant', async ({loadMore, makeAxeBuilder}) => {
expect(accessibilityResults.violations).toEqual([]);
});

test('should display a recap of the amount of results', async ({loadMore}) => {
test('should display a recap of the amount of products', async ({loadMore}) => {
await await expect(loadMore.summary()).toBeVisible();
});

Expand All @@ -31,17 +31,17 @@ test('should display a progress value between 1 and 100', async ({

test.describe('after clicking the load more button', () => {
test('should load more products', async ({loadMore, products}) => {
await expect(products.results).toHaveCount(48);
await expect(products.products).toHaveCount(48);
await loadMore.button.click();
const allProducts = await products.results.all();
const allProducts = await products.products.all();
await Promise.all(
allProducts.map(async (product) => product.waitFor({state: 'attached'}))
);
await expect(products.results).toHaveCount(96);
await expect(products.products).toHaveCount(96);
});
});

test.describe('when theres no results', () => {
test.describe('when there are no products', () => {
test('should be hidden', async ({searchBox, loadMore}) => {
await loadMore.button.waitFor({state: 'visible'});
await searchBox.searchInput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class LoadMoreProductsPageObject extends BasePageObject<'atomic-commerce-

summary({index, total}: {index?: number; total?: number} = {}) {
return this.page.getByText(
new RegExp(`Showing ${index ?? '\\d+'} of ${total ?? '\\d+'} results`)
new RegExp(`Showing ${index ?? '\\d+'} of ${total ?? '\\d+'} products`)
);
}

Expand All @@ -19,7 +19,7 @@ export class LoadMoreProductsPageObject extends BasePageObject<'atomic-commerce-
}

get button() {
return this.page.getByText('Load more results');
return this.page.getByText('Load more products');
}

get progressBar() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import type {Meta, StoryObj as Story} from '@storybook/web-components';
import {html} from 'lit/static-html.js';

const {decorator, play} = wrapInCommerceInterface({
skipFirstSearch: true,
skipFirstSearch: false,
});

const noResultsEngineConfig: Partial<CommerceEngineConfiguration> = {
const noProductsEngineConfig: Partial<CommerceEngineConfiguration> = {
preprocessRequest: (r) => {
const parsed = JSON.parse(r.body as string);
// eslint-disable-next-line @cspell/spellchecker
Expand All @@ -19,7 +19,7 @@ const noResultsEngineConfig: Partial<CommerceEngineConfiguration> = {
},
};

const fixedNumberOfResults = (
const fixedNumberOfProducts = (
perPage: number
): Partial<CommerceEngineConfiguration> => ({
preprocessRequest: (r) => {
Expand All @@ -30,14 +30,18 @@ const fixedNumberOfResults = (
},
});

const {play: playNoresults} = wrapInCommerceInterface({
const {play: playNoInitialSearch} = wrapInCommerceInterface({
skipFirstSearch: true,
});

const {play: playNoProducts} = wrapInCommerceInterface({
skipFirstSearch: false,
engineConfig: noResultsEngineConfig,
engineConfig: noProductsEngineConfig,
});

const {play: playFixedNumberOfResults} = wrapInCommerceInterface({
const {play: playFixedNumberOfProducts} = wrapInCommerceInterface({
skipFirstSearch: false,
engineConfig: fixedNumberOfResults(27),
engineConfig: fixedNumberOfProducts(27),
});

const meta: Meta = {
Expand All @@ -56,21 +60,30 @@ export const Default: Story = {
name: 'atomic-query-summary',
};

export const NoResults: Story = {
name: 'No Results',
export const NoInitialSearch: Story = {
name: 'No Initial Search',
tags: ['test'],
decorators: [(story) => story()],
play: async (context) => {
await playNoInitialSearch(context);
},
};

export const NoProducts: Story = {
name: 'No Products',
tags: ['test'],
decorators: [(story) => story()],
play: async (context) => {
await playNoresults(context);
await playNoProducts(context);
},
};

export const FixedNumberOfResults: Story = {
name: 'Fixed Number of Results',
export const FixedNumberOfProducts: Story = {
name: 'Fixed Number of Products',
tags: ['test'],
decorators: [(story) => story()],
play: async (context) => {
await playFixedNumberOfResults(context);
await playFixedNumberOfProducts(context);
},
};

Expand All @@ -92,6 +105,6 @@ export const WithSearchBox: Story = {
</atomic-commerce-layout>`,
],
play: async (context) => {
await play(context);
await playNoInitialSearch(context);
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import {LocalizedString} from '../../../utils/jsx-utils';
import {QuerySummaryContainer} from '../../common/query-summary/container';
import {QuerySummaryGuard} from '../../common/query-summary/guard';
import {getQuerySummaryI18nParameters} from '../../common/query-summary/utils';
import {getProductQuerySummaryI18nParameters} from '../../common/query-summary/utils';
import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface';

/**
Expand Down Expand Up @@ -74,7 +74,7 @@ export class AtomicQuerySummary
} = this.listingOrSearchSummaryState;

const {i18nKey, highlights, ariaLiveMessage} =
getQuerySummaryI18nParameters({
getProductQuerySummaryI18nParameters({
first: firstProduct,
last: lastProduct,
query: this.isSearch(this.listingOrSearchSummaryState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ test.describe('after searching for kayak', () => {
});

test('should not display duration by default', async ({querySummary}) => {
const textRegex = /^Results 1-[\d,]+ of [\d,]+ for kayak$/;
const textRegex = /^Products 1-[\d,]+ of [\d,]+ for kayak$/;
await expect(querySummary.text(textRegex)).toBeVisible();
});
});

test.describe('when search yields no results', () => {
test.describe('when search yields no products', () => {
test.beforeEach(async ({querySummary}) => {
await querySummary.load({story: 'no-results'});
});
Expand All @@ -34,18 +34,18 @@ test.describe('when search yields no results', () => {
});
});

test.describe('when search yields 27 results', () => {
test.describe('when search yields multiple products', () => {
test.beforeEach(async ({querySummary}) => {
await querySummary.load({story: 'fixed-number-of-results'});
});

test('screen readers should read out', async ({querySummary}) => {
const textRegex = /Results 1-27 of [\d,]+/;
const textRegex = /Products 1-27 of [\d,]+/;
await expect(querySummary.ariaLive(textRegex)).toBeVisible();

Check failure on line 44 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[chromium] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:42:7 › when search yields multiple products › screen readers should read out

2) [chromium] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:42:7 › when search yields multiple products › screen readers should read out Error: Timed out 20000ms waiting for expect(locator).toBeVisible() Locator: getByRole('status').filter({ hasText: /Products 1-27 of [\d,]+/ }) Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByRole('status').filter({ hasText: /Products 1-27 of [\d,]+/ }) 42 | test('screen readers should read out', async ({querySummary}) => { 43 | const textRegex = /Products 1-27 of [\d,]+/; > 44 | await expect(querySummary.ariaLive(textRegex)).toBeVisible(); | ^ 45 | }); 46 | }); 47 | at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:44:52

Check failure on line 44 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[chromium] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:42:7 › when search yields multiple products › screen readers should read out

2) [chromium] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:42:7 › when search yields multiple products › screen readers should read out Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 20000ms waiting for expect(locator).toBeVisible() Locator: getByRole('status').filter({ hasText: /Products 1-27 of [\d,]+/ }) Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByRole('status').filter({ hasText: /Products 1-27 of [\d,]+/ }) 42 | test('screen readers should read out', async ({querySummary}) => { 43 | const textRegex = /Products 1-27 of [\d,]+/; > 44 | await expect(querySummary.ariaLive(textRegex)).toBeVisible(); | ^ 45 | }); 46 | }); 47 | at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:44:52

Check failure on line 44 in packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts

View workflow job for this annotation

GitHub Actions / Run Playwright tests for Atomic

[chromium] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:42:7 › when search yields multiple products › screen readers should read out

2) [chromium] › components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:42:7 › when search yields multiple products › screen readers should read out Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: Timed out 20000ms waiting for expect(locator).toBeVisible() Locator: getByRole('status').filter({ hasText: /Products 1-27 of [\d,]+/ }) Expected: visible Received: <element(s) not found> Call log: - expect.toBeVisible with timeout 20000ms - waiting for getByRole('status').filter({ hasText: /Products 1-27 of [\d,]+/ }) 42 | test('screen readers should read out', async ({querySummary}) => { 43 | const textRegex = /Products 1-27 of [\d,]+/; > 44 | await expect(querySummary.ariaLive(textRegex)).toBeVisible(); | ^ 45 | }); 46 | }); 47 | at /home/runner/work/ui-kit/ui-kit/packages/atomic/src/components/commerce/atomic-commerce-query-summary/e2e/atomic-commerce-query-summary.e2e.ts:44:52
});
});

test.describe('when a query yield a single result', () => {
test.describe('when a query yield a single product', () => {
test.beforeEach(async ({querySummary, searchBox}) => {
await querySummary.load({story: 'with-search-box'});
await searchBox.hydrated.waitFor();
Expand All @@ -54,12 +54,12 @@ test.describe('when a query yield a single result', () => {
});

test('should display message', async ({querySummary}) => {
const textRegex = /^Result 1 of 1 for @ec_product_id=SP03730_00007$/;
const textRegex = /^Product 1 of 1 for @ec_product_id=SP03730_00007$/;
await expect(querySummary.text(textRegex)).toBeVisible();
});

test('screen readers should read out', async ({querySummary}) => {
const textRegex = /^Result 1 of 1 for @ec_product_id=SP03730_00007$/;
const textRegex = /^Product 1 of 1 for @ec_product_id=SP03730_00007$/;
await expect(querySummary.ariaLive(textRegex)).toBeVisible();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class ProductsPageObject {
return this.page.locator('atomic-commerce-product-list[class*="hydrated"]');
}

get results() {
get products() {
return this.page.locator('[part="result-list"] atomic-product');
}
}
4 changes: 3 additions & 1 deletion packages/atomic/src/components/common/load-more/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ interface LoadMoreButtonProps {
i18n: i18n;
onClick: () => void;
moreAvailable: boolean;
label?: 'load-more-results' | 'load-more-products';
}

export const LoadMoreButton: FunctionalComponent<LoadMoreButtonProps> = ({
i18n,
onClick,
moreAvailable,
label,
}) => {
if (!moreAvailable) {
return;
}
return (
<Button
style="primary"
text={i18n.t('load-more-results')}
text={i18n.t(label || 'load-more-results')}
part="load-more-results-button"
class="font-bold my-2 p-3"
onClick={() => onClick()}
Expand Down
4 changes: 3 additions & 1 deletion packages/atomic/src/components/common/load-more/summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@ interface LoadMoreSummaryProps {
i18n: i18n;
from: number;
to: number;
label?: 'showing-results-of-load-more' | 'showing-products-of-load-more';
}

export const LoadMoreSummary: FunctionalComponent<LoadMoreSummaryProps> = ({
i18n,
from,
to,
label,
}) => {
const wrapHighlight = (content: string) => {
return `<span class="font-bold text-on-background" part="highlight">${content}</span>`;
};

const locale = i18n.language;
const content = i18n.t('showing-results-of-load-more', {
const content = i18n.t(label || 'showing-results-of-load-more', {
interpolation: {escapeValue: false},
last: wrapHighlight(from.toLocaleString(locale)),
total: wrapHighlight(to.toLocaleString(locale)),
Expand Down
39 changes: 28 additions & 11 deletions packages/atomic/src/components/common/query-summary/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ interface i18nKeyProps {
i18n: i18n;
}

interface QuerySummaryLabels {
loadingStatus: string;
itemsForQuery: string;
allItems: string;
}

const WrapHighlight: FunctionalComponent<{part?: string}> = (
props,
children
Expand All @@ -24,16 +30,27 @@ const WrapHighlight: FunctionalComponent<{part?: string}> = (
);
};

export const getQuerySummaryI18nParameters = ({
first,
last,
query,
total,
isLoading,
i18n,
}: i18nKeyProps) => {
const i18nKey =
query !== '' ? 'showing-results-of-with-query' : 'showing-results-of';
export const getQuerySummaryI18nParameters = (props: i18nKeyProps) => {
return getQuerySummaryData(props, {
loadingStatus: 'loading-results',
itemsForQuery: 'showing-results-of-with-query',
allItems: 'showing-results-of',
});
};

export const getProductQuerySummaryI18nParameters = (props: i18nKeyProps) => {
return getQuerySummaryData(props, {
loadingStatus: 'loading-products',
itemsForQuery: 'showing-products-of-with-query',
allItems: 'showing-products-of',
});
};

const getQuerySummaryData = (
{first, last, query, total, isLoading, i18n}: i18nKeyProps,
{allItems, itemsForQuery, loadingStatus}: QuerySummaryLabels
) => {
const i18nKey = query !== '' ? itemsForQuery : allItems;

const params = {
first: first.toLocaleString(),
Expand All @@ -51,7 +68,7 @@ export const getQuerySummaryI18nParameters = ({
};

const ariaLiveMessage = isLoading
? i18n.t('loading-results')
? i18n.t(loadingStatus)
: i18n.t(i18nKey, params);

return {i18nKey, highlights, ariaLiveMessage};
Expand Down
Loading

0 comments on commit e63610e

Please sign in to comment.