diff --git a/package-lock.json b/package-lock.json
index 617d860da0b..6eec2d90ecf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -57445,6 +57445,7 @@
"@storybook/core-server": "8.1.5",
"@storybook/jest": "^0.2.3",
"@storybook/manager-api": "8.1.5",
+ "@storybook/router": "8.1.6",
"@storybook/test": "8.1.5",
"@storybook/test-runner": "0.18.2",
"@storybook/testing-library": "^0.2.2",
@@ -58552,6 +58553,34 @@
"win32"
]
},
+ "packages/atomic/node_modules/@storybook/client-logger": {
+ "version": "8.1.6",
+ "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-8.1.6.tgz",
+ "integrity": "sha512-QfSoUxS1rmrBzO7o99og9g+Gkm7sTmU5ZOpTkjszjlRqfV6/77eUnUOzUikej4LqPLmlJV5fqGuvoP0aNVksDw==",
+ "dev": true,
+ "dependencies": {
+ "@storybook/global": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/storybook"
+ }
+ },
+ "packages/atomic/node_modules/@storybook/router": {
+ "version": "8.1.6",
+ "resolved": "https://registry.npmjs.org/@storybook/router/-/router-8.1.6.tgz",
+ "integrity": "sha512-tvuhB2uXHEKK640Epm1SqVzPhQ9lXYfF7FX6FleJgVYEvZpJpNTD4RojedQoLI6SUUSXNy1Vs2QV26VM0XIPHQ==",
+ "dev": true,
+ "dependencies": {
+ "@storybook/client-logger": "8.1.6",
+ "memoizerific": "^1.11.3",
+ "qs": "^6.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/storybook"
+ }
+ },
"packages/atomic/node_modules/@types/node": {
"version": "18.19.33",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz",
diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts
index 152c04d402f..497f267f3ab 100644
--- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts
+++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts
@@ -11,6 +11,7 @@ AtomicAutomaticFacetGenerator,
AtomicBreadbox,
AtomicCategoryFacet,
AtomicColorFacet,
+AtomicCommerceFacets,
AtomicCommerceLoadMoreProducts,
AtomicCommerceSearchBox,
AtomicComponentError,
@@ -110,6 +111,7 @@ AtomicAutomaticFacetGenerator,
AtomicBreadbox,
AtomicCategoryFacet,
AtomicColorFacet,
+AtomicCommerceFacets,
AtomicCommerceLoadMoreProducts,
AtomicCommerceSearchBox,
AtomicComponentError,
diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts
index 661919c87e8..21f348cd36f 100644
--- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts
+++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts
@@ -138,6 +138,28 @@ export class AtomicColorFacet {
export declare interface AtomicColorFacet extends Components.AtomicColorFacet {}
+@ProxyCmp({
+ inputs: ['collapseFacetsAfter']
+})
+@Component({
+ selector: 'atomic-commerce-facets',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ template: '',
+ // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
+ inputs: ['collapseFacetsAfter'],
+})
+export class AtomicCommerceFacets {
+ protected el: HTMLElement;
+ constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
+ c.detach();
+ this.el = r.nativeElement;
+ }
+}
+
+
+export declare interface AtomicCommerceFacets extends Components.AtomicCommerceFacets {}
+
+
@ProxyCmp({
})
@Component({
diff --git a/packages/atomic/package.json b/packages/atomic/package.json
index f1c1c9dec4d..66b1ad352b7 100644
--- a/packages/atomic/package.json
+++ b/packages/atomic/package.json
@@ -87,6 +87,7 @@
"@storybook/core-server": "8.1.5",
"@storybook/jest": "^0.2.3",
"@storybook/manager-api": "8.1.5",
+ "@storybook/router": "8.1.6",
"@storybook/test": "8.1.5",
"@storybook/test-runner": "0.18.2",
"@storybook/testing-library": "^0.2.2",
diff --git a/packages/atomic/playwrightUtils/base-fixture.ts b/packages/atomic/playwright-utils/base-fixture.ts
similarity index 100%
rename from packages/atomic/playwrightUtils/base-fixture.ts
rename to packages/atomic/playwright-utils/base-fixture.ts
diff --git a/packages/atomic/playwright-utils/base-page-object.ts b/packages/atomic/playwright-utils/base-page-object.ts
new file mode 100644
index 00000000000..17663a8af7d
--- /dev/null
+++ b/packages/atomic/playwright-utils/base-page-object.ts
@@ -0,0 +1,42 @@
+import type {Page} from '@playwright/test';
+import {buildArgsParam} from '@storybook/router';
+import {JSX} from '../dist/types/components';
+
+export class BasePageObject<
+ TagName extends keyof JSX.IntrinsicElements,
+ Component = JSX.IntrinsicElements[TagName],
+> {
+ constructor(
+ public page: Page,
+ public tag: TagName
+ ) {}
+
+ get hydrated() {
+ return this.page.locator(`${this.tag}[class*="hydrated"]`);
+ }
+
+ get urlRoot() {
+ return 'http://localhost:4400/iframe.html';
+ }
+
+ async load(args?: Component, story: string = 'default') {
+ if (args) {
+ await this.page.goto(
+ `${this.urlRoot}?id=${this.tag}--${story}&args=${buildArgsParam(undefined, this.camelToKebab(args))}`
+ );
+ } else {
+ await this.page.goto(`${this.urlRoot}?id=${this.tag}--${story}`);
+ }
+ }
+
+ private camelToKebab(args: Component) {
+ const toKebab: Record = {};
+ Object.entries(args as Record).forEach(([key, value]) => {
+ toKebab[
+ `attributes-${key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()}`
+ ] = value;
+ });
+
+ return toKebab;
+ }
+}
diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts
index 5e5ee24d848..5116f92a6df 100644
--- a/packages/atomic/src/components.d.ts
+++ b/packages/atomic/src/components.d.ts
@@ -314,6 +314,7 @@ export namespace Components {
/**
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
+ * @alpha
*/
interface AtomicCommerceFacets {
/**
@@ -3503,6 +3504,7 @@ declare global {
/**
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
+ * @alpha
*/
interface HTMLAtomicCommerceFacetsElement extends Components.AtomicCommerceFacets, HTMLStencilElement {
}
@@ -5726,6 +5728,7 @@ declare namespace LocalJSX {
/**
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
+ * @alpha
*/
interface AtomicCommerceFacets {
/**
@@ -8793,6 +8796,7 @@ declare module "@stencil/core" {
/**
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
+ * @alpha
*/
"atomic-commerce-facets": LocalJSX.AtomicCommerceFacets & JSXBase.HTMLAttributes;
"atomic-commerce-interface": LocalJSX.AtomicCommerceInterface & JSXBase.HTMLAttributes;
diff --git a/packages/atomic/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.tsx b/packages/atomic/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.tsx
index 0da28c7368e..afacd4e1b23 100644
--- a/packages/atomic/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.tsx
+++ b/packages/atomic/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.tsx
@@ -79,7 +79,6 @@ export class AtomicCommerceInterface
private summary!: SearchSummary | ListingSummary;
private context!: Context;
private unsubscribeUrlManager: Unsubscribe = () => {};
- private unsubscribeSearchStatus: Unsubscribe = () => {};
private unsubscribeSummary: Unsubscribe = () => {};
private initialized = false;
private store: AtomicCommerceStore;
@@ -226,7 +225,6 @@ export class AtomicCommerceInterface
public disconnectedCallback() {
this.unsubscribeUrlManager();
- this.unsubscribeSearchStatus();
this.unsubscribeSummary();
window.removeEventListener('hashchange', this.onHashChange);
}
@@ -373,15 +371,6 @@ export class AtomicCommerceInterface
this.type === 'product-listing'
? buildProductListing(this.engine!)
: buildSearch(this.engine!);
-
- this.unsubscribeSearchStatus = this.searchOrListing.subscribe(() => {
- if (
- !this.searchOrListing.state.isLoading &&
- this.store.hasLoadingFlag(FirstSearchExecutedFlag)
- ) {
- this.store.unsetLoadingFlag(FirstSearchExecutedFlag);
- }
- });
}
private initSummary() {
@@ -406,6 +395,10 @@ export class AtomicCommerceInterface
firstSearchExecutedSelector,
firstSearchExecuted
);
+
+ if (firstSearchExecuted) {
+ this.store.unsetLoadingFlag(FirstSearchExecutedFlag);
+ }
});
}
diff --git a/packages/atomic/src/components/commerce/atomic-commerce-load-more-products/e2e/page-object.ts b/packages/atomic/src/components/commerce/atomic-commerce-load-more-products/e2e/page-object.ts
index b00512014d4..21fe40afe45 100644
--- a/packages/atomic/src/components/commerce/atomic-commerce-load-more-products/e2e/page-object.ts
+++ b/packages/atomic/src/components/commerce/atomic-commerce-load-more-products/e2e/page-object.ts
@@ -1,9 +1,9 @@
import type {Page} from '@playwright/test';
+import {BasePageObject} from '../../../../../playwright-utils/base-page-object';
-export class AtomicCommerceLoadMoreProductsLocators {
- private page: Page;
+export class LoadMoreProductsPageObject extends BasePageObject<'atomic-commerce-load-more-products'> {
constructor(page: Page) {
- this.page = page;
+ super(page, 'atomic-commerce-load-more-products');
}
summary({index, total}: {index?: number; total?: number} = {}) {
diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/atomic-commerce-search-box.e2e.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/atomic-commerce-search-box.e2e.ts
index f7ad243c2e3..3e34756ba45 100644
--- a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/atomic-commerce-search-box.e2e.ts
+++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/atomic-commerce-search-box.e2e.ts
@@ -1,10 +1,8 @@
import {test, expect} from './fixture';
test.describe('default', () => {
- test.beforeEach(async ({page}) => {
- await page.goto(
- 'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--default&viewMode=story&args=attributes-suggestion-timeout:5000'
- );
+ test.beforeEach(async ({searchBox}) => {
+ await searchBox.load({suggestionTimeout: 5000});
});
test('should have an enabled search button', async ({searchBox}) => {
@@ -50,10 +48,8 @@ test.describe('default', () => {
});
test.describe('with instant results & query suggestions', () => {
- test.beforeEach(async ({page}) => {
- await page.goto(
- 'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--rich-search-box&viewMode=story&args=attributes-suggestion-timeout:5000'
- );
+ test.beforeEach(async ({searchBox}) => {
+ await searchBox.load({suggestionTimeout: 5000}, 'rich-search-box');
});
test.describe('after clicking the searchbox input', () => {
@@ -82,10 +78,12 @@ test.describe('with instant results & query suggestions', () => {
});
test.describe('with disable-search=true and minimum-query-length=1', () => {
- test.beforeEach(async ({page}) => {
- await page.goto(
- 'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--default&viewMode=story&args=attributes-disable-search:!true;attributes-minimum-query-length:1;attributes-suggestion-timeout:5000'
- );
+ test.beforeEach(async ({searchBox}) => {
+ await searchBox.load({
+ suggestionTimeout: 5000,
+ disableSearch: true,
+ minimumQueryLength: 1,
+ });
});
const testCases = () => {
@@ -124,10 +122,8 @@ test.describe('with disable-search=true and minimum-query-length=1', () => {
});
test.describe('with minimum-query-length=4', () => {
- test.beforeEach(async ({page}) => {
- await page.goto(
- 'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--default&viewMode=story&args=attributes-minimum-query-length:4;attributes-suggestion-timeout:5000'
- );
+ test.beforeEach(async ({searchBox}) => {
+ await searchBox.load({minimumQueryLength: 4, suggestionTimeout: 5000});
});
const testCases = () => {
@@ -180,9 +176,10 @@ test.describe('with minimum-query-length=4', () => {
});
test.describe('with a facet & clear-filters set to true', () => {
- test.beforeEach(async ({page}) => {
- await page.goto(
- 'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--in-page&args=attributes-clear-filters:!true;attributes-suggestion-timeout:5000'
+ test.beforeEach(async ({searchBox}) => {
+ await searchBox.load(
+ {clearFilters: true, suggestionTimeout: 5000},
+ 'in-page'
);
});
@@ -198,9 +195,10 @@ test.describe('with a facet & clear-filters set to true', () => {
});
test.describe('with a facet & clear-filters set to false', () => {
- test.beforeEach(async ({page}) => {
- await page.goto(
- 'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--in-page&args=attributes-clear-filters:!false;attributes-suggestion-timeout:5000'
+ test.beforeEach(async ({searchBox}) => {
+ await searchBox.load(
+ {clearFilters: false, suggestionTimeout: 5000},
+ 'in-page'
);
});
@@ -214,21 +212,3 @@ test.describe('with a facet & clear-filters set to false', () => {
await expect(facets.clearFilters()).toBeVisible();
});
});
-
-test.describe('with enable-query-syntax=true', () => {
- test.beforeEach(async ({page}) => {
- await page.goto(
- 'http://localhost:4400/iframe.html?id=atomic-commerce-search-box--in-page&viewMode=story&args=attributes-enable-query-syntax:!true;attributes-suggestion-timeout:5000'
- );
- });
-
- test('should use query syntax', async ({loadMore, searchBox, page}) => {
- await loadMore.loadMoreButton.waitFor({state: 'visible'});
- await searchBox.searchInput
- // eslint-disable-next-line @cspell/spellchecker
- .fill('@urihash=bzo5fpM1vf8Xñds1');
- await searchBox.submitButton.click();
- await expect(loadMore.summary({total: 1})).toBeVisible();
- await expect(page.getByText('WiLife Life Jacket WiLife')).toBeVisible();
- });
-});
diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/fixture.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/fixture.ts
index feb9519eb06..aa62c3a1ef4 100644
--- a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/fixture.ts
+++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/fixture.ts
@@ -1,28 +1,28 @@
+import {test as base} from '@playwright/test';
import {
AxeFixture,
makeAxeBuilder,
-} from '@coveo/atomic/playwrightUtils/base-fixture';
-import {test as base} from '@playwright/test';
-import {AtomicCommerceLoadMoreProductsLocators as LoadMore} from '../../atomic-commerce-load-more-products/e2e/page-object';
-import {AtomicCommerceFacetsLocators as Facets} from '../../facets/atomic-commerce-facets/e2e/page-object';
-import {AtomicCommerceSearchBoxLocators as SearchBox} from './page-object';
+} from '../../../../../playwright-utils/base-fixture';
+import {LoadMoreProductsPageObject} from '../../atomic-commerce-load-more-products/e2e/page-object';
+import {FacetsPageObject} from '../../facets/atomic-commerce-facets/e2e/page-object';
+import {SearchBoxPageObject} from './page-object';
type MyFixtures = {
- searchBox: SearchBox;
- facets: Facets;
- loadMore: LoadMore;
+ searchBox: SearchBoxPageObject;
+ facets: FacetsPageObject;
+ loadMore: LoadMoreProductsPageObject;
};
export const test = base.extend({
makeAxeBuilder,
searchBox: async ({page}, use) => {
- await use(new SearchBox(page));
+ await use(new SearchBoxPageObject(page));
},
facets: async ({page}, use) => {
- await use(new Facets(page));
+ await use(new FacetsPageObject(page));
},
loadMore: async ({page}, use) => {
- await use(new LoadMore(page));
+ await use(new LoadMoreProductsPageObject(page));
},
});
export {expect} from '@playwright/test';
diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/page-object.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/page-object.ts
index dc868c75652..21e15b6fd77 100644
--- a/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/page-object.ts
+++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/e2e/page-object.ts
@@ -1,9 +1,9 @@
import type {Page} from '@playwright/test';
+import {BasePageObject} from '../../../../../playwright-utils/base-page-object';
-export class AtomicCommerceSearchBoxLocators {
- private page: Page;
+export class SearchBoxPageObject extends BasePageObject<'atomic-commerce-search-box'> {
constructor(page: Page) {
- this.page = page;
+ super(page, 'atomic-commerce-search-box');
}
get submitButton() {
@@ -38,10 +38,6 @@ export class AtomicCommerceSearchBoxLocators {
);
}
- get hydrated() {
- return this.page.locator('atomic-commerce-search-box[class*="hydrated"]');
- }
-
private listSideAffix(listSide?: 'Left' | 'Right') {
return listSide ? ` In ${listSide} list\\.` : '';
}
diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.new.stories.tsx b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.new.stories.tsx
new file mode 100644
index 00000000000..0a3773c1a34
--- /dev/null
+++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.new.stories.tsx
@@ -0,0 +1,72 @@
+import {
+ playExecuteFirstSearch,
+ wrapInCommerceInterface,
+} from '@coveo/atomic/storybookUtils/commerce-interface-wrapper';
+import {parameters} from '@coveo/atomic/storybookUtils/common-meta-parameters';
+import {renderComponent} from '@coveo/atomic/storybookUtils/render-component';
+import type {Meta, StoryObj as Story} from '@storybook/web-components';
+import {html} from 'lit/static-html.js';
+
+const {decorator, play} = wrapInCommerceInterface({skipFirstSearch: true});
+
+const meta: Meta = {
+ component: 'atomic-commerce-facets',
+ title: 'Atomic-Commerce/Facets',
+ id: 'atomic-commerce-facets',
+ render: renderComponent,
+ decorators: [decorator],
+ parameters,
+ play,
+};
+
+export default meta;
+
+export const Default: Story = {
+ name: 'atomic-commerce-facets',
+ play: async (context) => {
+ await play(context);
+ await playExecuteFirstSearch(context);
+ },
+};
+
+export const InPage: Story = {
+ name: 'In a page',
+ decorators: [
+ (story) =>
+ html`
+
+
+ ${story()}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `,
+ ],
+ play: async (context) => {
+ await play(context);
+ await playExecuteFirstSearch(context);
+ },
+};
+
+export const LoadingState: Story = {
+ name: 'Loading state',
+ play: async (context) => {
+ await play(context);
+ },
+};
diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.tsx b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.tsx
index ebe5aca1e85..dfd22b4e06f 100644
--- a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.tsx
+++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/atomic-commerce-facets.tsx
@@ -26,7 +26,7 @@ import {CommerceBindings as Bindings} from '../../atomic-commerce-interface/atom
* The `atomic-commerce-facets` component automatically renders commerce facets based on the Commerce API response.
* Unlike regular facets, which require explicit definition and request in the query, the `atomic-commerce-facets` component dynamically generates facets.
*
- * @internal
+ * @alpha
*/
@Component({
tag: 'atomic-commerce-facets',
diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/atomic-commerce-facets.e2e.ts b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/atomic-commerce-facets.e2e.ts
new file mode 100644
index 00000000000..c4719114b91
--- /dev/null
+++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/atomic-commerce-facets.e2e.ts
@@ -0,0 +1,43 @@
+import {test, expect} from './fixture';
+
+test.describe('default', async () => {
+ test('should be A11y compliant', async ({facets, makeAxeBuilder}) => {
+ await facets.load();
+ await facets.hydrated.waitFor();
+ const accessibilityResults = await makeAxeBuilder().analyze();
+
+ expect(accessibilityResults.violations).toEqual([]);
+ });
+
+ test('should display facets', async ({facets}) => {
+ await facets.load();
+ await expect(facets.standardFacets.first()).toBeVisible();
+ await expect(facets.numericFacets.first()).toBeVisible();
+ await expect(facets.categoryFacets.first()).toBeVisible();
+ });
+
+ test('should collapse facets when set to 1', async ({facets}) => {
+ await facets.load({
+ collapseFacetsAfter: 1,
+ });
+ await expect(facets.expandedFacets).toHaveCount(1);
+ await expect(facets.collapsedFacets).toHaveCount(4);
+ });
+
+ test('should disable collapse facets when set to -1', async ({facets}) => {
+ await facets.load({
+ collapseFacetsAfter: -1,
+ });
+ await expect(facets.collapsedFacets).toHaveCount(0);
+ await expect(facets.expandedFacets).toHaveCount(5);
+ });
+});
+
+test.describe('loading state', async () => {
+ test('should display placeholder equal to the collapse facet after property', async ({
+ facets,
+ }) => {
+ await facets.load({collapseFacetsAfter: 5}, 'loading-state');
+ await expect(facets.placeholders).toHaveCount(5);
+ });
+});
diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/fixture.ts b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/fixture.ts
new file mode 100644
index 00000000000..52a2273ebc5
--- /dev/null
+++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/fixture.ts
@@ -0,0 +1,18 @@
+import {test as base} from '@playwright/test';
+import {
+ AxeFixture,
+ makeAxeBuilder,
+} from '../../../../../../playwright-utils/base-fixture';
+import {FacetsPageObject} from './page-object';
+
+interface TestFixture {
+ facets: FacetsPageObject;
+}
+
+export const test = base.extend({
+ makeAxeBuilder,
+ facets: async ({page}, use) => {
+ await use(new FacetsPageObject(page));
+ },
+});
+export {expect} from '@playwright/test';
diff --git a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/page-object.ts b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/page-object.ts
index 27cabbd831b..6c345bb30e2 100644
--- a/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/page-object.ts
+++ b/packages/atomic/src/components/commerce/facets/atomic-commerce-facets/e2e/page-object.ts
@@ -1,15 +1,41 @@
import type {Page} from '@playwright/test';
+import {BasePageObject} from '../../../../../../playwright-utils/base-page-object';
-export class AtomicCommerceFacetsLocators {
- private page: Page;
+export class FacetsPageObject extends BasePageObject<'atomic-commerce-facets'> {
constructor(page: Page) {
- this.page = page;
+ super(page, 'atomic-commerce-facets');
+ }
+ get standardFacets() {
+ return this.page
+ .getByText('Brand')
+ .or(this.page.getByText('Color'))
+ .or(this.page.getByText('Size'));
+ }
+
+ get numericFacets() {
+ return this.page.getByText('Price');
+ }
+
+ get categoryFacets() {
+ return this.page.getByText('Category');
+ }
+
+ get collapsedFacets() {
+ return this.page.getByRole('button', {expanded: false});
+ }
+
+ get expandedFacets() {
+ return this.page.getByRole('button', {expanded: true});
}
get inclusionFilters() {
return this.page.getByLabel(/Inclusion filter/);
}
+ get placeholders() {
+ return this.page.locator('[part="placeholder"]');
+ }
+
clearFilters(numberOfFilters?: number) {
return this.page.getByLabel(
new RegExp(`Clear ${numberOfFilters ?? '\\d'} filter for`)
diff --git a/packages/atomic/tsconfig.json b/packages/atomic/tsconfig.json
index bf0a1ed5230..88a30e3ee65 100644
--- a/packages/atomic/tsconfig.json
+++ b/packages/atomic/tsconfig.json
@@ -26,6 +26,7 @@
"src/external-builds",
"**/*.stories.tsx",
"**/*.stories.ts",
- "**/*.stories.js"
+ "**/*.stories.js",
+ "**/e2e/**/*"
]
}