From 800c67d6ac3a0962ba8a8f4db35a163a70e3f190 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:22:01 -0500 Subject: [PATCH 1/9] no more get() and set() --- .../atomic-commerce-interface.tsx | 2 +- ...omic-commerce-recommendation-interface.tsx | 2 +- .../common/facets/timeframe-facet-common.tsx | 2 +- .../components/common/interface/bindings.ts | 18 -------- .../src/components/common/interface/store.ts | 44 ++++++++++--------- .../atomic-insight-interface.tsx | 2 +- .../atomic-insight-numeric-facet.tsx | 2 +- .../atomic-recs-interface.tsx | 2 +- .../atomic-search-interface.tsx | 4 +- .../atomic-sort-dropdown.tsx | 33 +++++++------- .../atomic-numeric-facet.tsx | 2 +- 11 files changed, 48 insertions(+), 65 deletions(-) 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 aefa3ea87e7..46777f8b463 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 @@ -220,7 +220,7 @@ export class AtomicCommerceInterface @Watch('iconAssetsPath') public updateIconAssetsPath() { - this.store.set('iconAssetsPath', this.iconAssetsPath); + this.store.state.iconAssetsPath = this.iconAssetsPath; } public disconnectedCallback() { diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx index 366908ace16..0a0e3493539 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx @@ -168,7 +168,7 @@ export class AtomicCommerceRecommendationInterface @Watch('iconAssetsPath') public updateIconAssetsPath() { - this.store.set('iconAssetsPath', this.iconAssetsPath); + this.store.state.iconAssetsPath = this.iconAssetsPath; } @Listen('atomic/initializeComponent') diff --git a/packages/atomic/src/components/common/facets/timeframe-facet-common.tsx b/packages/atomic/src/components/common/facets/timeframe-facet-common.tsx index e6f8e82717a..85844c91eae 100644 --- a/packages/atomic/src/components/common/facets/timeframe-facet-common.tsx +++ b/packages/atomic/src/components/common/facets/timeframe-facet-common.tsx @@ -106,7 +106,7 @@ export class TimeframeFacetCommon { return this.props.facetId; } - if (this.props.bindings.store.get('dateFacets')[this.props.field]) { + if (this.props.bindings.store.state.dateFacets[this.props.field]) { return randomID(`${this.props.field}_`); } diff --git a/packages/atomic/src/components/common/interface/bindings.ts b/packages/atomic/src/components/common/interface/bindings.ts index edea41b8716..590c9f98294 100644 --- a/packages/atomic/src/components/common/interface/bindings.ts +++ b/packages/atomic/src/components/common/interface/bindings.ts @@ -6,24 +6,6 @@ import {i18n} from 'i18next'; import {InsightEngine} from '../../insight'; import {AtomicCommonStore, AtomicCommonStoreData} from './store'; -export interface CommonStencilStore { - state: StoreData; - - get: ( - propName: PropName - ) => StoreData[PropName]; - - set: ( - propName: PropName, - value: StoreData[PropName] - ) => void; - - onChange: ( - propName: PropName, - cb: (newValue: StoreData[PropName]) => void - ) => () => void; -} - /** * Bindings passed from an interface to its children components. */ diff --git a/packages/atomic/src/components/common/interface/store.ts b/packages/atomic/src/components/common/interface/store.ts index b9a19212df5..e845a2c8c06 100644 --- a/packages/atomic/src/components/common/interface/store.ts +++ b/packages/atomic/src/components/common/interface/store.ts @@ -7,7 +7,15 @@ import { FacetValueFormat, } from '../facets/facet-common-store'; import {DateFacetValue, NumericFacetValue} from '../types'; -import {AnyEngineType, CommonStencilStore} from './bindings'; +import {AnyEngineType} from './bindings'; + +interface CommonStencilStore { + state: StoreData; + onChange: ( + propName: PropName, + cb: (newValue: StoreData[PropName]) => void + ) => () => void; +} export interface ResultListInfo { focusOnNextNewResult(): void; @@ -79,39 +87,36 @@ export function createAtomicCommonStore< }, getIconAssetsPath() { - return stencilStore.get('iconAssetsPath'); + return stencilStore.state.iconAssetsPath; }, setLoadingFlag(loadingFlag: string) { - const flags = stencilStore.get('loadingFlags'); - stencilStore.set('loadingFlags', flags.concat(loadingFlag)); + const flags = stencilStore.state.loadingFlags; + stencilStore.state.loadingFlags = flags.concat(loadingFlag); }, unsetLoadingFlag(loadingFlag: string) { - const flags = stencilStore.get('loadingFlags'); - stencilStore.set( - 'loadingFlags', - flags.filter((value) => value !== loadingFlag) + const flags = stencilStore.state.loadingFlags; + stencilStore.state.loadingFlags = flags.filter( + (value) => value !== loadingFlag ); }, hasLoadingFlag(loadingFlag: string) { - return stencilStore.get('loadingFlags').indexOf(loadingFlag) !== -1; + return stencilStore.state.loadingFlags.indexOf(loadingFlag) !== -1; }, registerResultList(data: ResultListInfo) { - stencilStore.set('resultList', data); + stencilStore.state.resultList = data; }, addFieldsToInclude(fields) { - stencilStore.set('fieldsToInclude', [ - ...stencilStore.get('fieldsToInclude'), - ...fields, - ]); + const currentFields = stencilStore.state.fieldsToInclude; + stencilStore.state.fieldsToInclude = [...currentFields, ...fields]; }, waitUntilAppLoaded(callback: () => void) { - if (!stencilStore.get('loadingFlags').length) { + if (!stencilStore.state.loadingFlags.length) { callback(); } else { stencilStore.onChange('loadingFlags', (flags) => { @@ -123,10 +128,9 @@ export function createAtomicCommonStore< }, isAppLoaded() { - return !stencilStore.get('loadingFlags').length; + return !stencilStore.state.loadingFlags.length; }, - // eslint-disable-next-line @typescript-eslint/no-unused-vars getUniqueIDFromEngine(_engine: AnyEngineType): string { throw new Error( 'getUniqueIDFromEngine not implemented at the common store level.' @@ -134,9 +138,9 @@ export function createAtomicCommonStore< }, getFacetElements() { - return stencilStore - .get('facetElements') - .filter((element) => isInDocument(element)); + return stencilStore.state.facetElements.filter((element) => + isInDocument(element) + ); }, }; } diff --git a/packages/atomic/src/components/insight/atomic-insight-interface/atomic-insight-interface.tsx b/packages/atomic/src/components/insight/atomic-insight-interface/atomic-insight-interface.tsx index da81f2bf6cf..8f8435c7974 100644 --- a/packages/atomic/src/components/insight/atomic-insight-interface/atomic-insight-interface.tsx +++ b/packages/atomic/src/components/insight/atomic-insight-interface/atomic-insight-interface.tsx @@ -202,7 +202,7 @@ export class AtomicInsightInterface @Watch('iconAssetsPath') public updateIconAssetsPath() { - this.store.set('iconAssetsPath', this.iconAssetsPath); + this.store.state.iconAssetsPath = this.iconAssetsPath; } @Listen('atomic/initializeComponent') diff --git a/packages/atomic/src/components/insight/atomic-insight-numeric-facet/atomic-insight-numeric-facet.tsx b/packages/atomic/src/components/insight/atomic-insight-numeric-facet/atomic-insight-numeric-facet.tsx index beaa926550e..10376a64311 100644 --- a/packages/atomic/src/components/insight/atomic-insight-numeric-facet/atomic-insight-numeric-facet.tsx +++ b/packages/atomic/src/components/insight/atomic-insight-numeric-facet/atomic-insight-numeric-facet.tsx @@ -458,7 +458,7 @@ export class AtomicInsightNumericFacet return; } - if (this.bindings.store.get('numericFacets')[this.field]) { + if (this.bindings.store.state.numericFacets[this.field]) { this.facetId = randomID(`${this.field}_`); } diff --git a/packages/atomic/src/components/recommendations/atomic-recs-interface/atomic-recs-interface.tsx b/packages/atomic/src/components/recommendations/atomic-recs-interface/atomic-recs-interface.tsx index 9d12726eb6b..2ca85f9996c 100644 --- a/packages/atomic/src/components/recommendations/atomic-recs-interface/atomic-recs-interface.tsx +++ b/packages/atomic/src/components/recommendations/atomic-recs-interface/atomic-recs-interface.tsx @@ -232,7 +232,7 @@ export class AtomicRecsInterface @Watch('iconAssetsPath') public updateIconAssetsPath() { - this.store.set('iconAssetsPath', this.iconAssetsPath); + this.store.state.iconAssetsPath = this.iconAssetsPath; } @Listen('atomic/initializeComponent') diff --git a/packages/atomic/src/components/search/atomic-search-interface/atomic-search-interface.tsx b/packages/atomic/src/components/search/atomic-search-interface/atomic-search-interface.tsx index 062483fcd46..d9ad275241b 100644 --- a/packages/atomic/src/components/search/atomic-search-interface/atomic-search-interface.tsx +++ b/packages/atomic/src/components/search/atomic-search-interface/atomic-search-interface.tsx @@ -286,7 +286,7 @@ export class AtomicSearchInterface @Watch('iconAssetsPath') public updateIconAssetsPath() { - this.store.set('iconAssetsPath', this.iconAssetsPath); + this.store.state.iconAssetsPath = this.iconAssetsPath; } public disconnectedCallback() { @@ -420,7 +420,7 @@ export class AtomicSearchInterface 'atomic-search-layout' )?.mobileBreakpoint; if (breakpoint) { - this.store.set('mobileBreakpoint', breakpoint); + this.store.state.mobileBreakpoint = breakpoint; } } diff --git a/packages/atomic/src/components/search/atomic-sort-dropdown/atomic-sort-dropdown.tsx b/packages/atomic/src/components/search/atomic-sort-dropdown/atomic-sort-dropdown.tsx index e6ca809684d..12b1e336617 100644 --- a/packages/atomic/src/components/search/atomic-sort-dropdown/atomic-sort-dropdown.tsx +++ b/packages/atomic/src/components/search/atomic-sort-dropdown/atomic-sort-dropdown.tsx @@ -84,25 +84,22 @@ export class AtomicSortDropdown implements InitializableComponent { return; } - this.bindings.store.set( - 'sortOptions', - sortExpressionElements.map( - ({expression, label, tabsIncluded, tabsExcluded}) => { - new Schema({ - label: new StringValue({emptyAllowed: false, required: true}), - }).validate({label}); + this.bindings.store.state.sortOptions = sortExpressionElements.map( + ({expression, label, tabsIncluded, tabsExcluded}) => { + new Schema({ + label: new StringValue({emptyAllowed: false, required: true}), + }).validate({label}); - return { - tabs: { - included: tabsIncluded, - excluded: tabsExcluded, - }, - criteria: parseCriterionExpression(expression), - expression, - label, - }; - } - ) + return { + tabs: { + included: tabsIncluded, + excluded: tabsExcluded, + }, + criteria: parseCriterionExpression(expression), + expression, + label, + }; + } ); } diff --git a/packages/atomic/src/components/search/facets/atomic-numeric-facet/atomic-numeric-facet.tsx b/packages/atomic/src/components/search/facets/atomic-numeric-facet/atomic-numeric-facet.tsx index 1f4ee6bfcd6..22af28df62d 100644 --- a/packages/atomic/src/components/search/facets/atomic-numeric-facet/atomic-numeric-facet.tsx +++ b/packages/atomic/src/components/search/facets/atomic-numeric-facet/atomic-numeric-facet.tsx @@ -525,7 +525,7 @@ export class AtomicNumericFacet implements InitializableComponent { return; } - if (this.bindings.store.get('numericFacets')[this.field]) { + if (this.bindings.store.state.numericFacets[this.field]) { this.facetId = randomID(`${this.field}_`); } From 49bf2e4ceec79ad12d05e52e7f9fe0ea1e8f2771 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:01:42 -0500 Subject: [PATCH 2/9] migrate commerce store --- .../atomic-commerce-interface.tsx | 8 +- .../atomic-commerce-interface/store.ts | 99 +++++++++---------- .../common/atomic-icon/atomic-icon.tsx | 2 +- .../common/facets/timeframe-facet-common.tsx | 5 +- .../components/common/interface/bindings.ts | 7 +- .../src/components/common/interface/store.ts | 12 ++- .../common/item-list/item-list-common.tsx | 3 +- .../atomic/src/utils/accessibility-utils.tsx | 3 + 8 files changed, 74 insertions(+), 65 deletions(-) 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 46777f8b463..89ee6aa254e 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 @@ -45,14 +45,14 @@ import { noProductsSelector, } from '../atomic-commerce-layout/commerce-layout'; import {getAnalyticsConfig} from './analytics-config'; -import {AtomicCommerceStore, createAtomicCommerceStore} from './store'; +import {CommerceStore, createCommerceStore} from './store'; const FirstRequestExecutedFlag = 'firstRequestExecuted'; export type CommerceInitializationOptions = CommerceEngineConfiguration; export type CommerceBindings = CommonBindings< CommerceEngine, - AtomicCommerceStore, + CommerceStore, HTMLAtomicCommerceInterfaceElement > & NonceBindings; @@ -81,7 +81,7 @@ export class AtomicCommerceInterface private unsubscribeUrlManager: Unsubscribe = () => {}; private unsubscribeSummary: Unsubscribe = () => {}; private initialized = false; - private store: AtomicCommerceStore; + private store: CommerceStore; private commonInterfaceHelper: CommonAtomicInterfaceHelper; @Element() public host!: HTMLAtomicCommerceInterfaceElement; @@ -178,7 +178,7 @@ export class AtomicCommerceInterface this, 'CoveoAtomic' ); - this.store = createAtomicCommerceStore(this.type); + this.store = createCommerceStore(this.type); } public connectedCallback() { diff --git a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts index d2e89acab1f..4933928526c 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts @@ -1,78 +1,66 @@ import { CommerceEngine, Selectors, - NumericFacetValue, - DateFacetValue, - SortCriterion, ChildProduct, } from '@coveo/headless/commerce'; +import {createStore} from '@stencil/store'; import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; -import { - FacetInfo, - FacetStore, - FacetValueFormat, -} from '../../common/facets/facet-common-store'; -import { - createAtomicCommonStore, - AtomicCommonStoreData, - AtomicCommonStore, -} from '../../common/interface/store'; +import {CommonStore, ResultListInfo} from '../../common/interface/store'; import {makeDesktopQuery} from '../../search/atomic-layout/search-layout'; -export interface SortDropdownOption { - expression: string; - criteria: SortCriterion[]; - label: string; -} - -export interface AtomicStoreData extends AtomicCommonStoreData { - facets: FacetStore; - numericFacets: FacetStore>; - dateFacets: FacetStore>; - categoryFacets: FacetStore; - sortOptions: SortDropdownOption[]; +interface Data { mobileBreakpoint: string; - currentQuickviewPosition: number; activeProductChild: ChildProduct | undefined; + // why is this undefined I don't like + resultList?: ResultListInfo; + iconAssetsPath: string; + loadingFlags: string[]; } -export interface AtomicCommerceStore - extends AtomicCommonStore { +export type CommerceStore = CommonStore & { + hasLoadingFlag(loadingFlag: string): boolean; + unsetLoadingFlag(loadingFlag: string): void; + setLoadingFlag(flag: string): void; + isAppLoaded(): boolean; isMobile(): boolean; -} + getUniqueIDFromEngine(engine: CommerceEngine): string; + registerResultList(data: ResultListInfo): void; +}; -export interface FacetInfoMap { - [facetId: string]: - | FacetInfo - | (FacetInfo & FacetValueFormat) - | (FacetInfo & FacetValueFormat); -} - -export function createAtomicCommerceStore( +export function createCommerceStore( type: 'search' | 'product-listing' -): AtomicCommerceStore { - const commonStore = createAtomicCommonStore({ +): CommerceStore { + const store = createStore({ loadingFlags: [], - facets: {}, - numericFacets: {}, - dateFacets: {}, - categoryFacets: {}, - facetElements: [], - sortOptions: [], - iconAssetsPath: '', mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, - fieldsToInclude: [], - currentQuickviewPosition: -1, activeProductChild: undefined, - }); + iconAssetsPath: '', + }) as CommonStore; return { - ...commonStore, + ...store, + + hasLoadingFlag(loadingFlag: string) { + return store.state.loadingFlags.indexOf(loadingFlag) !== -1; + }, + + unsetLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + }, + + setLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.concat(loadingFlag); + }, + + isAppLoaded() { + return !store.state.loadingFlags.length; + }, isMobile() { - return !window.matchMedia( - makeDesktopQuery(commonStore.state.mobileBreakpoint) - ).matches; + return !window.matchMedia(makeDesktopQuery(store.state.mobileBreakpoint)) + .matches; }, getUniqueIDFromEngine(engine: CommerceEngine): string { @@ -87,5 +75,10 @@ export function createAtomicCommerceStore( ); } }, + + // This is not necessary, we could just do store.state.resultList = data; + registerResultList(data: ResultListInfo) { + store.state.resultList = data; + }, }; } diff --git a/packages/atomic/src/components/common/atomic-icon/atomic-icon.tsx b/packages/atomic/src/components/common/atomic-icon/atomic-icon.tsx index ffcd6d8f5d1..f8d41377cf5 100644 --- a/packages/atomic/src/components/common/atomic-icon/atomic-icon.tsx +++ b/packages/atomic/src/components/common/atomic-icon/atomic-icon.tsx @@ -94,7 +94,7 @@ export class AtomicIcon implements InitializableComponent { private async getIcon() { const url = parseAssetURL( this.icon, - this.bindings.store.getIconAssetsPath() + this.bindings.store.state.iconAssetsPath ); const svg = url ? await this.fetchIcon(url) : this.icon; diff --git a/packages/atomic/src/components/common/facets/timeframe-facet-common.tsx b/packages/atomic/src/components/common/facets/timeframe-facet-common.tsx index 85844c91eae..038cc66df08 100644 --- a/packages/atomic/src/components/common/facets/timeframe-facet-common.tsx +++ b/packages/atomic/src/components/common/facets/timeframe-facet-common.tsx @@ -3,8 +3,9 @@ import {FocusTargetController} from '../../../utils/accessibility-utils'; import {parseDate} from '../../../utils/date-utils'; import {getFieldValueCaption} from '../../../utils/field-utils'; import {randomID} from '../../../utils/utils'; +import {InsightBindings} from '../../insight/atomic-insight-interface/atomic-insight-interface'; +import {Bindings as SearchBindings} from '../../search/atomic-search-interface/atomic-search-interface'; import {Hidden} from '../hidden'; -import {AnyBindings} from '../interface/bindings'; import { DateFacet, DateFacetValue, @@ -38,7 +39,7 @@ export interface Timeframe { interface TimeframeFacetCommonOptions { facetId?: string; host: HTMLElement; - bindings: AnyBindings; + bindings: SearchBindings | InsightBindings; label: string; field: string; headingLevel: number; diff --git a/packages/atomic/src/components/common/interface/bindings.ts b/packages/atomic/src/components/common/interface/bindings.ts index 590c9f98294..c25c2b4e45d 100644 --- a/packages/atomic/src/components/common/interface/bindings.ts +++ b/packages/atomic/src/components/common/interface/bindings.ts @@ -3,15 +3,17 @@ import {CommerceEngine} from '@coveo/headless/commerce'; import type {RecommendationEngine} from '@coveo/headless/recommendation'; import {HTMLStencilElement} from '@stencil/core/internal'; import {i18n} from 'i18next'; +import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; import {InsightEngine} from '../../insight'; import {AtomicCommonStore, AtomicCommonStoreData} from './store'; +type AnyStore = CommerceStore; /** * Bindings passed from an interface to its children components. */ export interface CommonBindings< Engine extends AnyEngineType, - Store extends AtomicCommonStore, + Store extends AtomicCommonStore | AnyStore, InterfaceElement extends HTMLStencilElement, > { /** @@ -49,7 +51,8 @@ export interface NonceBindings { export type AnyBindings = CommonBindings< AnyEngineType, - AtomicCommonStore, + // Instead of AtomicCommonStoreData, it should follow AnyengineType and to CommerceStore| SearchStore | ... + AtomicCommonStore | CommerceStore, HTMLStencilElement >; diff --git a/packages/atomic/src/components/common/interface/store.ts b/packages/atomic/src/components/common/interface/store.ts index e845a2c8c06..78f84e7efc8 100644 --- a/packages/atomic/src/components/common/interface/store.ts +++ b/packages/atomic/src/components/common/interface/store.ts @@ -9,7 +9,15 @@ import { import {DateFacetValue, NumericFacetValue} from '../types'; import {AnyEngineType} from './bindings'; -interface CommonStencilStore { +export interface CommonStencilStore { + state: StoreData; + onChange: ( + propName: PropName, + cb: (newValue: StoreData[PropName]) => void + ) => () => void; +} + +export interface CommonStore { state: StoreData; onChange: ( propName: PropName, @@ -31,7 +39,7 @@ export type AtomicCommonStoreData = { iconAssetsPath: string; fieldsToInclude: string[]; facetElements: HTMLElement[]; - resultList?: ResultListInfo; + resultList: ResultListInfo; }; export interface AtomicCommonStore diff --git a/packages/atomic/src/components/common/item-list/item-list-common.tsx b/packages/atomic/src/components/common/item-list/item-list-common.tsx index 44b8deea11a..ca02bad2e5c 100644 --- a/packages/atomic/src/components/common/item-list/item-list-common.tsx +++ b/packages/atomic/src/components/common/item-list/item-list-common.tsx @@ -4,6 +4,7 @@ import { } from '../../../utils/accessibility-utils'; import {updateBreakpoints} from '../../../utils/replace-breakpoint'; import {defer, once} from '../../../utils/utils'; +import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; import {AnyItem} from '../interface/item'; import {AtomicCommonStore, AtomicCommonStoreData} from '../interface/store'; import { @@ -22,7 +23,7 @@ export type ItemRenderingFunction = | undefined; export interface ItemListCommonProps { - store: AtomicCommonStore; + store: AtomicCommonStore | CommerceStore; loadingFlag: string; host: HTMLElement; nextNewItemTarget: FocusTargetController; diff --git a/packages/atomic/src/utils/accessibility-utils.tsx b/packages/atomic/src/utils/accessibility-utils.tsx index 45b7807d8fa..7339d51b355 100644 --- a/packages/atomic/src/utils/accessibility-utils.tsx +++ b/packages/atomic/src/utils/accessibility-utils.tsx @@ -77,6 +77,7 @@ export class FocusTargetController { public focusAfterSearch() { this.lastSearchId = this.bindings.store.getUniqueIDFromEngine( + //@ts-expect-error normal for now, should go away this.bindings.engine ); this.doFocusAfterSearch = true; @@ -90,6 +91,7 @@ export class FocusTargetController { public disableForCurrentSearch() { if ( + //@ts-expect-error normal for now, should go away this.bindings.store.getUniqueIDFromEngine(this.bindings.engine) !== this.lastSearchId ) { @@ -108,6 +110,7 @@ export class FocusTargetController { } if ( this.doFocusAfterSearch && + //@ts-expect-error normal for now, should go away this.bindings.store.getUniqueIDFromEngine(this.bindings.engine) !== this.lastSearchId ) { From bf8e05be63a24c1b687e72ac34e15617d9b576b9 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:27:59 -0500 Subject: [PATCH 3/9] commerce recommendation store in progress --- packages/atomic/src/components.d.ts | 12 +++- .../atomic-commerce-interface/store.ts | 1 + ...omic-commerce-recommendation-interface.tsx | 8 +-- .../store.ts | 69 ++++++++++--------- .../atomic-commerce-recommendation-list.tsx | 2 +- .../atomic-product/atomic-product.tsx | 7 +- .../components/common/interface/bindings.ts | 5 +- .../src/components/common/interface/store.ts | 2 +- .../common/item-list/item-list-common.tsx | 6 +- 9 files changed, 66 insertions(+), 46 deletions(-) diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index e9b4419bb33..b6fd650f5dd 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -28,6 +28,8 @@ import { InsightResultActionClickedEvent } from "./components/insight/atomic-ins import { InsightResultAttachToCaseEvent } from "./components/insight/atomic-insight-result-attach-to-case-action/atomic-insight-result-attach-to-case-action"; import { Section } from "./components/common/atomic-layout-section/sections"; import { AtomicCommonStore, AtomicCommonStoreData } from "./components/common/interface/store"; +import { CommerceStore } from "./components/commerce/atomic-commerce-interface/store"; +import { CommerceRecommendationStore } from "./components/commerce/atomic-commerce-recommendation-interface/store"; import { SelectChildProductEventArgs } from "./components/commerce/product-template-components/atomic-product-children/atomic-product-children"; import { TruncateAfter } from "./components/common/expandable-text/expandable-text"; import { RecommendationEngine } from "@coveo/headless/recommendation"; @@ -60,6 +62,8 @@ export { InsightResultActionClickedEvent } from "./components/insight/atomic-ins export { InsightResultAttachToCaseEvent } from "./components/insight/atomic-insight-result-attach-to-case-action/atomic-insight-result-attach-to-case-action"; export { Section } from "./components/common/atomic-layout-section/sections"; export { AtomicCommonStore, AtomicCommonStoreData } from "./components/common/interface/store"; +export { CommerceStore } from "./components/commerce/atomic-commerce-interface/store"; +export { CommerceRecommendationStore } from "./components/commerce/atomic-commerce-recommendation-interface/store"; export { SelectChildProductEventArgs } from "./components/commerce/product-template-components/atomic-product-children/atomic-product-children"; export { TruncateAfter } from "./components/common/expandable-text/expandable-text"; export { RecommendationEngine } from "@coveo/headless/recommendation"; @@ -2052,7 +2056,9 @@ export namespace Components { * Global Atomic state. * @alpha */ - "store"?: AtomicCommonStore; + "store"?: | AtomicCommonStore + | CommerceStore + | CommerceRecommendationStore; } /** * @alpha The `atomic-product-children` component renders a section that allows the user to select a nested product (e.g., a color variant of a given product). @@ -8185,7 +8191,9 @@ declare namespace LocalJSX { * Global Atomic state. * @alpha */ - "store"?: AtomicCommonStore; + "store"?: | AtomicCommonStore + | CommerceStore + | CommerceRecommendationStore; } /** * @alpha The `atomic-product-children` component renders a section that allows the user to select a nested product (e.g., a color variant of a given product). diff --git a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts index 4933928526c..1d0ab42717b 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts @@ -9,6 +9,7 @@ import {CommonStore, ResultListInfo} from '../../common/interface/store'; import {makeDesktopQuery} from '../../search/atomic-layout/search-layout'; interface Data { + //IMPORTANT does mobileBreakpoint work in commerce-layout ?? it does not get updated... mobileBreakpoint: string; activeProductChild: ChildProduct | undefined; // why is this undefined I don't like diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx index 0a0e3493539..e15f407e7dd 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface.tsx @@ -23,14 +23,14 @@ import { CommonAtomicInterfaceHelper, } from '../../common/interface/interface-common'; import { - AtomicCommerceRecommendationStore, - createAtomicCommerceRecommendationStore, + CommerceRecommendationStore, + createCommerceRecommendationStore, } from './store'; export type CommerceInitializationOptions = CommerceEngineConfiguration; export type CommerceBindings = CommonBindings< CommerceEngine, - AtomicCommerceRecommendationStore, + CommerceRecommendationStore, HTMLAtomicCommerceInterfaceElement > & NonceBindings; @@ -48,7 +48,7 @@ export type CommerceBindings = CommonBindings< export class AtomicCommerceRecommendationInterface implements BaseAtomicInterface { - private store = createAtomicCommerceRecommendationStore(); + private store = createCommerceRecommendationStore(); private commonInterfaceHelper: CommonAtomicInterfaceHelper; @Element() public host!: HTMLAtomicCommerceInterfaceElement; diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts index 371ae7bd9b2..f8a441e57a4 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts @@ -1,45 +1,46 @@ -import {ChildProduct} from '@coveo/headless/commerce'; -import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; -import { - AtomicCommonStore, - AtomicCommonStoreData, - createAtomicCommonStore, -} from '../../common/interface/store'; -import {makeDesktopQuery} from '../atomic-commerce-layout/commerce-layout'; - -export interface AtomicStoreData extends AtomicCommonStoreData { - mobileBreakpoint: string; - currentQuickviewPosition: number; - activeProductChild: ChildProduct | undefined; -} +import {createStore} from '@stencil/store'; +import {CommonStore, ResultListInfo} from '../../common/interface/store'; -export interface AtomicCommerceRecommendationStore - extends AtomicCommonStore { - isMobile(): boolean; +interface Data { + iconAssetsPath: string; + loadingFlags: string[]; + resultList?: ResultListInfo; } -export function createAtomicCommerceRecommendationStore(): AtomicCommerceRecommendationStore { - const commonStore = createAtomicCommonStore({ +export type CommerceRecommendationStore = CommonStore & { + isAppLoaded(): boolean; + unsetLoadingFlag(loadingFlag: string): void; + setLoadingFlag(flag: string): void; + registerResultList(data: ResultListInfo): void; +}; + +export function createCommerceRecommendationStore(): CommerceRecommendationStore { + const store = createStore({ loadingFlags: [], - facets: {}, - numericFacets: {}, - dateFacets: {}, - categoryFacets: {}, - facetElements: [], iconAssetsPath: '', - mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, - fieldsToInclude: [], - currentQuickviewPosition: -1, - activeProductChild: undefined, - }); + }) as CommonStore; return { - ...commonStore, + ...store, + + isAppLoaded() { + return !store.state.loadingFlags.length; + }, + + unsetLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + }, + + setLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.concat(loadingFlag); + }, + + // This is not necessary, we could just do store.state.resultList = data; - isMobile() { - return !window.matchMedia( - makeDesktopQuery(commonStore.state.mobileBreakpoint) - ).matches; + registerResultList(data: ResultListInfo) { + store.state.resultList = data; }, }; } diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-list/atomic-commerce-recommendation-list.tsx b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-list/atomic-commerce-recommendation-list.tsx index 7c523df9b90..4f93123394c 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-list/atomic-commerce-recommendation-list.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-list/atomic-commerce-recommendation-list.tsx @@ -40,7 +40,7 @@ import { ItemDisplayImageSize, getItemListDisplayClasses, } from '../../common/layout/display-options'; -import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface'; +import {CommerceBindings} from '../atomic-commerce-recommendation-interface/atomic-commerce-recommendation-interface'; import {ProductTemplateProvider} from '../product-list/product-template-provider'; import {SelectChildProductEventArgs} from '../product-template-components/atomic-product-children/atomic-product-children'; diff --git a/packages/atomic/src/components/commerce/atomic-product/atomic-product.tsx b/packages/atomic/src/components/commerce/atomic-product/atomic-product.tsx index 58360280be6..4d36c635faa 100644 --- a/packages/atomic/src/components/commerce/atomic-product/atomic-product.tsx +++ b/packages/atomic/src/components/commerce/atomic-product/atomic-product.tsx @@ -17,6 +17,8 @@ import { ItemDisplayImageSize, ItemDisplayLayout, } from '../../common/layout/display-options'; +import {CommerceStore} from '../atomic-commerce-interface/store'; +import {CommerceRecommendationStore} from '../atomic-commerce-recommendation-interface/store'; import { InteractiveProductContextEvent, ProductContextEvent, @@ -56,7 +58,10 @@ export class AtomicProduct { * Global Atomic state. * @alpha */ - @Prop() store?: AtomicCommonStore; + @Prop() store?: + | AtomicCommonStore + | CommerceStore + | CommerceRecommendationStore; /** * The product content to display. diff --git a/packages/atomic/src/components/common/interface/bindings.ts b/packages/atomic/src/components/common/interface/bindings.ts index c25c2b4e45d..11557e3d036 100644 --- a/packages/atomic/src/components/common/interface/bindings.ts +++ b/packages/atomic/src/components/common/interface/bindings.ts @@ -4,10 +4,11 @@ import type {RecommendationEngine} from '@coveo/headless/recommendation'; import {HTMLStencilElement} from '@stencil/core/internal'; import {i18n} from 'i18next'; import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; +import {CommerceRecommendationStore} from '../../commerce/atomic-commerce-recommendation-interface/store'; import {InsightEngine} from '../../insight'; import {AtomicCommonStore, AtomicCommonStoreData} from './store'; -type AnyStore = CommerceStore; +type AnyStore = CommerceStore | CommerceRecommendationStore; /** * Bindings passed from an interface to its children components. */ @@ -52,7 +53,7 @@ export interface NonceBindings { export type AnyBindings = CommonBindings< AnyEngineType, // Instead of AtomicCommonStoreData, it should follow AnyengineType and to CommerceStore| SearchStore | ... - AtomicCommonStore | CommerceStore, + AtomicCommonStore | AnyStore, HTMLStencilElement >; diff --git a/packages/atomic/src/components/common/interface/store.ts b/packages/atomic/src/components/common/interface/store.ts index 78f84e7efc8..d82ca7d5212 100644 --- a/packages/atomic/src/components/common/interface/store.ts +++ b/packages/atomic/src/components/common/interface/store.ts @@ -39,7 +39,7 @@ export type AtomicCommonStoreData = { iconAssetsPath: string; fieldsToInclude: string[]; facetElements: HTMLElement[]; - resultList: ResultListInfo; + resultList?: ResultListInfo; }; export interface AtomicCommonStore diff --git a/packages/atomic/src/components/common/item-list/item-list-common.tsx b/packages/atomic/src/components/common/item-list/item-list-common.tsx index ca02bad2e5c..c986f6976f4 100644 --- a/packages/atomic/src/components/common/item-list/item-list-common.tsx +++ b/packages/atomic/src/components/common/item-list/item-list-common.tsx @@ -5,6 +5,7 @@ import { import {updateBreakpoints} from '../../../utils/replace-breakpoint'; import {defer, once} from '../../../utils/utils'; import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; +import {CommerceRecommendationStore} from '../../commerce/atomic-commerce-recommendation-interface/store'; import {AnyItem} from '../interface/item'; import {AtomicCommonStore, AtomicCommonStoreData} from '../interface/store'; import { @@ -23,7 +24,10 @@ export type ItemRenderingFunction = | undefined; export interface ItemListCommonProps { - store: AtomicCommonStore | CommerceStore; + store: + | AtomicCommonStore + | CommerceStore + | CommerceRecommendationStore; loadingFlag: string; host: HTMLElement; nextNewItemTarget: FocusTargetController; From 2955e5c43429fbd5df9b61ea5e78267f94715149 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:41:28 -0500 Subject: [PATCH 4/9] recs store --- .../store.ts | 6 +- .../components/common/interface/bindings.ts | 4 +- .../common/item-list/item-list-common.tsx | 4 +- .../atomic-recs-interface.tsx | 6 +- .../atomic-recs-interface/store.ts | 72 +++++++++++++------ .../atomic-recs-result/atomic-recs-result.tsx | 4 +- 6 files changed, 65 insertions(+), 31 deletions(-) diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts index f8a441e57a4..1cde834e143 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts @@ -12,6 +12,7 @@ export type CommerceRecommendationStore = CommonStore & { unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; registerResultList(data: ResultListInfo): void; + // getUniqueIDFromEngine(engine: CommerceEngine): string; }; export function createCommerceRecommendationStore(): CommerceRecommendationStore { @@ -38,9 +39,12 @@ export function createCommerceRecommendationStore(): CommerceRecommendationStore }, // This is not necessary, we could just do store.state.resultList = data; - registerResultList(data: ResultListInfo) { store.state.resultList = data; }, + + // getUniqueIDFromEngine(engine: CommerceEngine): string { + // return null; + // }, }; } diff --git a/packages/atomic/src/components/common/interface/bindings.ts b/packages/atomic/src/components/common/interface/bindings.ts index 11557e3d036..e6bd0394419 100644 --- a/packages/atomic/src/components/common/interface/bindings.ts +++ b/packages/atomic/src/components/common/interface/bindings.ts @@ -6,9 +6,9 @@ import {i18n} from 'i18next'; import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; import {CommerceRecommendationStore} from '../../commerce/atomic-commerce-recommendation-interface/store'; import {InsightEngine} from '../../insight'; +import {RecsStore} from '../../recommendations/atomic-recs-interface/store'; import {AtomicCommonStore, AtomicCommonStoreData} from './store'; -type AnyStore = CommerceStore | CommerceRecommendationStore; /** * Bindings passed from an interface to its children components. */ @@ -62,3 +62,5 @@ export type AnyEngineType = | RecommendationEngine | InsightEngine | CommerceEngine; + +type AnyStore = CommerceStore | CommerceRecommendationStore | RecsStore; diff --git a/packages/atomic/src/components/common/item-list/item-list-common.tsx b/packages/atomic/src/components/common/item-list/item-list-common.tsx index c986f6976f4..9bdcc4692ff 100644 --- a/packages/atomic/src/components/common/item-list/item-list-common.tsx +++ b/packages/atomic/src/components/common/item-list/item-list-common.tsx @@ -6,6 +6,7 @@ import {updateBreakpoints} from '../../../utils/replace-breakpoint'; import {defer, once} from '../../../utils/utils'; import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; import {CommerceRecommendationStore} from '../../commerce/atomic-commerce-recommendation-interface/store'; +import {RecsStore} from '../../recommendations/atomic-recs-interface/store'; import {AnyItem} from '../interface/item'; import {AtomicCommonStore, AtomicCommonStoreData} from '../interface/store'; import { @@ -27,7 +28,8 @@ export interface ItemListCommonProps { store: | AtomicCommonStore | CommerceStore - | CommerceRecommendationStore; + | CommerceRecommendationStore + | RecsStore; loadingFlag: string; host: HTMLElement; nextNewItemTarget: FocusTargetController; diff --git a/packages/atomic/src/components/recommendations/atomic-recs-interface/atomic-recs-interface.tsx b/packages/atomic/src/components/recommendations/atomic-recs-interface/atomic-recs-interface.tsx index 2ca85f9996c..91b8da68a12 100644 --- a/packages/atomic/src/components/recommendations/atomic-recs-interface/atomic-recs-interface.tsx +++ b/packages/atomic/src/components/recommendations/atomic-recs-interface/atomic-recs-interface.tsx @@ -29,13 +29,13 @@ import { mismatchedInterfaceAndEnginePropError, } from '../../common/interface/interface-common'; import {getAnalyticsConfig} from './analytics-config'; -import {createAtomicRecsStore, AtomicRecsStore} from './store'; +import {createRecsStore, RecsStore} from './store'; const FirstRecommendationExecutedFlag = 'firstRecommendationExecuted'; export type RecsInitializationOptions = RecommendationEngineConfiguration; export type RecsBindings = CommonBindings< RecommendationEngine, - AtomicRecsStore, + RecsStore, HTMLAtomicRecsInterfaceElement >; @@ -51,7 +51,7 @@ export type RecsBindings = CommonBindings< export class AtomicRecsInterface implements BaseAtomicInterface { - private store = createAtomicRecsStore(); + private store = createRecsStore(); private commonInterfaceHelper: CommonAtomicInterfaceHelper; private initialized = false; diff --git a/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts b/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts index 2c5764542bd..40662e80b6a 100644 --- a/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts +++ b/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts @@ -1,29 +1,55 @@ -import {RecommendationEngine} from '@coveo/headless/recommendation'; -import { - AtomicCommonStore, - AtomicCommonStoreData, - createAtomicCommonStore, -} from '../../common/interface/store'; - -export interface AtomicRecsStoreData extends AtomicCommonStoreData {} -export interface AtomicRecsStore - extends AtomicCommonStore {} - -export function createAtomicRecsStore(): AtomicRecsStore { - const commonStore = createAtomicCommonStore({ +import {createStore} from '@stencil/store'; +import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; +import {CommonStore, ResultListInfo} from '../../common/interface/store'; + +interface Data { + //IMPORTANT does mobileBreakpoint work in commerce-layout ?? it does not get updated... + mobileBreakpoint: string; + loadingFlags: string[]; + // activeProductChild: ChildProduct | undefined; + // why is this undefined I don't like + // resultList?: ResultListInfo; + iconAssetsPath: string; + resultList?: ResultListInfo; + + // loadingFlags: string[]; +} + +export type RecsStore = CommonStore & { + // hasLoadingFlag(loadingFlag: string): boolean; + unsetLoadingFlag(loadingFlag: string): void; + setLoadingFlag(flag: string): void; + isAppLoaded(): boolean; + registerResultList(data: ResultListInfo): void; +}; + +export function createRecsStore(): RecsStore { + const store = createStore({ loadingFlags: [], - facets: {}, - numericFacets: {}, - dateFacets: {}, - categoryFacets: {}, + mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, iconAssetsPath: '', - facetElements: [], - fieldsToInclude: [], - }); + }) as CommonStore; + return { - ...commonStore, - getUniqueIDFromEngine(engine: RecommendationEngine): string { - return engine.state.recommendation.searchUid; + ...store, + + unsetLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + }, + + setLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.concat(loadingFlag); + }, + + isAppLoaded() { + return !store.state.loadingFlags.length; + }, + + // This is not necessary, we could just do store.state.resultList = data; + registerResultList(data: ResultListInfo) { + store.state.resultList = data; }, }; } diff --git a/packages/atomic/src/components/recommendations/atomic-recs-result/atomic-recs-result.tsx b/packages/atomic/src/components/recommendations/atomic-recs-result/atomic-recs-result.tsx index 1b0f4136a3f..249dc1732a1 100644 --- a/packages/atomic/src/components/recommendations/atomic-recs-result/atomic-recs-result.tsx +++ b/packages/atomic/src/components/recommendations/atomic-recs-result/atomic-recs-result.tsx @@ -17,7 +17,7 @@ import { ItemDisplayImageSize, ItemDisplayLayout, } from '../../common/layout/display-options'; -import {AtomicRecsStore} from '../atomic-recs-interface/store'; +import {RecsStore} from '../atomic-recs-interface/store'; /** * The `atomic-recs-result` component is used internally by the `atomic-recs-list` component. @@ -61,7 +61,7 @@ export class AtomicRecsResult { * Global Atomic state. * @internal */ - @Prop() store?: AtomicRecsStore; + @Prop() store?: RecsStore; /** * The result content to display. From d2d64a881c9349b2d453be4acde0db74e666edd0 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:32:23 -0500 Subject: [PATCH 5/9] insight store --- .../components/common/interface/bindings.ts | 7 +- .../common/item-list/item-list-common.tsx | 4 +- .../atomic-insight-interface.tsx | 6 +- .../insight/atomic-insight-interface/store.ts | 141 +++++++++++------- .../atomic-insight-result.tsx | 4 +- .../atomic/src/utils/accessibility-utils.tsx | 2 +- 6 files changed, 106 insertions(+), 58 deletions(-) diff --git a/packages/atomic/src/components/common/interface/bindings.ts b/packages/atomic/src/components/common/interface/bindings.ts index e6bd0394419..bd891cc0692 100644 --- a/packages/atomic/src/components/common/interface/bindings.ts +++ b/packages/atomic/src/components/common/interface/bindings.ts @@ -6,6 +6,7 @@ import {i18n} from 'i18next'; import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; import {CommerceRecommendationStore} from '../../commerce/atomic-commerce-recommendation-interface/store'; import {InsightEngine} from '../../insight'; +import {InsightStore} from '../../insight/atomic-insight-interface/store'; import {RecsStore} from '../../recommendations/atomic-recs-interface/store'; import {AtomicCommonStore, AtomicCommonStoreData} from './store'; @@ -63,4 +64,8 @@ export type AnyEngineType = | InsightEngine | CommerceEngine; -type AnyStore = CommerceStore | CommerceRecommendationStore | RecsStore; +type AnyStore = + | CommerceStore + | CommerceRecommendationStore + | RecsStore + | InsightStore; diff --git a/packages/atomic/src/components/common/item-list/item-list-common.tsx b/packages/atomic/src/components/common/item-list/item-list-common.tsx index 9bdcc4692ff..9c04f166acf 100644 --- a/packages/atomic/src/components/common/item-list/item-list-common.tsx +++ b/packages/atomic/src/components/common/item-list/item-list-common.tsx @@ -6,6 +6,7 @@ import {updateBreakpoints} from '../../../utils/replace-breakpoint'; import {defer, once} from '../../../utils/utils'; import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; import {CommerceRecommendationStore} from '../../commerce/atomic-commerce-recommendation-interface/store'; +import {InsightStore} from '../../insight/atomic-insight-interface/store'; import {RecsStore} from '../../recommendations/atomic-recs-interface/store'; import {AnyItem} from '../interface/item'; import {AtomicCommonStore, AtomicCommonStoreData} from '../interface/store'; @@ -29,7 +30,8 @@ export interface ItemListCommonProps { | AtomicCommonStore | CommerceStore | CommerceRecommendationStore - | RecsStore; + | RecsStore + | InsightStore; loadingFlag: string; host: HTMLElement; nextNewItemTarget: FocusTargetController; diff --git a/packages/atomic/src/components/insight/atomic-insight-interface/atomic-insight-interface.tsx b/packages/atomic/src/components/insight/atomic-insight-interface/atomic-insight-interface.tsx index 8f8435c7974..566b984d9a5 100644 --- a/packages/atomic/src/components/insight/atomic-insight-interface/atomic-insight-interface.tsx +++ b/packages/atomic/src/components/insight/atomic-insight-interface/atomic-insight-interface.tsx @@ -26,13 +26,13 @@ import { CommonAtomicInterfaceHelper, } from '../../common/interface/interface-common'; import {getAnalyticsConfig} from './analytics-config'; -import {AtomicInsightStore, createAtomicInsightStore} from './store'; +import {createInsightStore, InsightStore} from './store'; const FirstInsightRequestExecutedFlag = 'firstInsightRequestExecuted'; export type InsightInitializationOptions = InsightEngineConfiguration; export type InsightBindings = CommonBindings< InsightEngine, - AtomicInsightStore, + InsightStore, HTMLAtomicInsightInterfaceElement > & NonceBindings; @@ -128,7 +128,7 @@ export class AtomicInsightInterface @Element() public host!: HTMLAtomicInsightInterfaceElement; - private store = createAtomicInsightStore(); + private store = createInsightStore(); private commonInterfaceHelper: CommonAtomicInterfaceHelper; public constructor() { diff --git a/packages/atomic/src/components/insight/atomic-insight-interface/store.ts b/packages/atomic/src/components/insight/atomic-insight-interface/store.ts index 04192588305..784833fd853 100644 --- a/packages/atomic/src/components/insight/atomic-insight-interface/store.ts +++ b/packages/atomic/src/components/insight/atomic-insight-interface/store.ts @@ -1,75 +1,116 @@ -import { - InsightDateFacetValue, - InsightEngine, - InsightNumericFacetValue, -} from '..'; +import {InsightEngine} from '@coveo/headless/insight'; +import {createStore} from '@stencil/store'; import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; +import {isInDocument} from '../../../utils/utils'; import { FacetInfo, FacetStore, + FacetType, FacetValueFormat, } from '../../common/facets/facet-common-store'; -import { - AtomicCommonStore, - AtomicCommonStoreData, - createAtomicCommonStore, -} from '../../common/interface/store'; -import {makeDesktopQuery} from '../../search/atomic-layout/search-layout'; +import {CommonStore, ResultListInfo} from '../../common/interface/store'; +import {DateFacetValue, NumericFacetValue} from '../../common/types'; -export interface AtomicInsightStoreData extends AtomicCommonStoreData { - fieldsToInclude: string[]; +interface Data { + mobileBreakpoint: string; + loadingFlags: string[]; facets: FacetStore; - numericFacets: FacetStore< - FacetInfo & FacetValueFormat - >; - dateFacets: FacetStore>; + numericFacets: FacetStore>; + dateFacets: FacetStore>; categoryFacets: FacetStore; - mobileBreakpoint: string; - currentQuickviewPosition: number; + iconAssetsPath: string; + facetElements: HTMLElement[]; + resultList?: ResultListInfo; + fieldsToInclude: string[]; } -export interface FacetInfoMap { - [facetId: string]: - | FacetInfo - | (FacetInfo & FacetValueFormat) - | (FacetInfo & FacetValueFormat); -} +export type InsightStore = CommonStore & { + registerFacet( + facetType: T, + data: Data[T][U] & {facetId: U; element: HTMLElement} + ): void; + setLoadingFlag(flag: string): void; + unsetLoadingFlag(loadingFlag: string): void; + isAppLoaded(): boolean; + getFacetElements(): HTMLElement[]; + waitUntilAppLoaded(callback: () => void): void; + registerResultList(data: ResultListInfo): void; + getUniqueIDFromEngine(engine: InsightEngine): string; +}; -export interface AtomicInsightStore - extends AtomicCommonStore { - getAllFacets(): FacetInfoMap; - isMobile(): boolean; -} +export const isRefineModalFacet = 'is-refine-modal'; -export function createAtomicInsightStore(): AtomicInsightStore { - const commonStore = createAtomicCommonStore({ +export function createInsightStore(): InsightStore { + const store = createStore({ + mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, + loadingFlags: [], + iconAssetsPath: '', facets: {}, numericFacets: {}, dateFacets: {}, categoryFacets: {}, - loadingFlags: [], - iconAssetsPath: '', - fieldsToInclude: [], facetElements: [], - mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, - currentQuickviewPosition: -1, - }); + fieldsToInclude: [], + }) as CommonStore; + + const clearExistingFacetElement = (facetType: FacetType, facetId: string) => { + if (store.state[facetType][facetId]) { + store.state.facetElements = store.state.facetElements.filter( + (facetElement) => facetElement.getAttribute('facet-id') !== facetId + ); + } + }; + return { - ...commonStore, + ...store, + + registerFacet( + facetType: T, + data: Data[T][U] & {facetId: U; element: HTMLElement} + ) { + if (data.element.getAttribute(isRefineModalFacet) !== null) { + return; + } + + clearExistingFacetElement(facetType, data.facetId); + store.state.facetElements.push(data.element); + store.state[facetType][data.facetId] = data; + }, + + setLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.concat(loadingFlag); + }, + + unsetLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + }, + + isAppLoaded() { + return !store.state.loadingFlags.length; + }, + + getFacetElements() { + return store.state.facetElements.filter((element) => + isInDocument(element) + ); + }, - getAllFacets() { - return { - ...commonStore.state.facets, - ...commonStore.state.dateFacets, - ...commonStore.state.categoryFacets, - ...commonStore.state.numericFacets, - }; + waitUntilAppLoaded(callback: () => void) { + if (!store.state.loadingFlags.length) { + callback(); + } else { + store.onChange('loadingFlags', (flags) => { + if (!flags.length) { + callback(); + } + }); + } }, - isMobile() { - return !window.matchMedia( - makeDesktopQuery(commonStore.state.mobileBreakpoint) - ).matches; + registerResultList(data: ResultListInfo) { + store.state.resultList = data; }, getUniqueIDFromEngine(engine: InsightEngine): string { diff --git a/packages/atomic/src/components/insight/atomic-insight-result/atomic-insight-result.tsx b/packages/atomic/src/components/insight/atomic-insight-result/atomic-insight-result.tsx index acbb909e8fb..b1b3d1f0404 100644 --- a/packages/atomic/src/components/insight/atomic-insight-result/atomic-insight-result.tsx +++ b/packages/atomic/src/components/insight/atomic-insight-result/atomic-insight-result.tsx @@ -12,7 +12,7 @@ import { ItemDisplayDensity, ItemDisplayImageSize, } from '../../common/layout/display-options'; -import {AtomicInsightStore} from '../atomic-insight-interface/store'; +import {InsightStore} from '../atomic-insight-interface/store'; /** * @internal @@ -46,7 +46,7 @@ export class AtomicInsightResult { * Global Atomic state. * @internal */ - @Prop() store?: AtomicInsightStore; + @Prop() store?: InsightStore; /** * The result content to display. diff --git a/packages/atomic/src/utils/accessibility-utils.tsx b/packages/atomic/src/utils/accessibility-utils.tsx index 7339d51b355..543cdbeaeab 100644 --- a/packages/atomic/src/utils/accessibility-utils.tsx +++ b/packages/atomic/src/utils/accessibility-utils.tsx @@ -76,8 +76,8 @@ export class FocusTargetController { } public focusAfterSearch() { + //@ts-expect-error normal for now, should go away this.lastSearchId = this.bindings.store.getUniqueIDFromEngine( - //@ts-expect-error normal for now, should go away this.bindings.engine ); this.doFocusAfterSearch = true; From 0a566866617f95a792df999a6e15d64690e48cb8 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:16:52 -0500 Subject: [PATCH 6/9] search + remove common store --- .../atomic-product/atomic-product.tsx | 9 +- .../components/common/interface/bindings.ts | 9 +- .../src/components/common/interface/store.ts | 142 ------------------ .../common/item-list/item-list-common.tsx | 6 +- .../atomic-facet-manager.tsx | 1 + .../atomic-refine-modal.tsx | 6 +- .../search/atomic-result/atomic-result.tsx | 7 +- .../analytics-config.spec.ts | 6 +- .../analytics-config.ts | 10 +- .../atomic-search-interface.tsx | 6 +- .../search/atomic-search-interface/store.ts | 142 ++++++++++++++---- .../result-template-decorators.spec.tsx | 4 +- .../src/utils/initialization-utils.spec.ts | 6 +- 13 files changed, 142 insertions(+), 212 deletions(-) diff --git a/packages/atomic/src/components/commerce/atomic-product/atomic-product.tsx b/packages/atomic/src/components/commerce/atomic-product/atomic-product.tsx index 4d36c635faa..7deb3de0af0 100644 --- a/packages/atomic/src/components/commerce/atomic-product/atomic-product.tsx +++ b/packages/atomic/src/components/commerce/atomic-product/atomic-product.tsx @@ -2,10 +2,6 @@ import {Product, InteractiveProduct} from '@coveo/headless/commerce'; import {Component, h, Prop, Element, Listen, Host} from '@stencil/core'; import {parentNodeToString} from '../../../utils/dom-utils'; import {applyFocusVisiblePolyfill} from '../../../utils/initialization-utils'; -import { - AtomicCommonStore, - AtomicCommonStoreData, -} from '../../common/interface/store'; import {DisplayConfig} from '../../common/item-list/item-decorators'; import { ItemRenderingFunction, @@ -58,10 +54,7 @@ export class AtomicProduct { * Global Atomic state. * @alpha */ - @Prop() store?: - | AtomicCommonStore - | CommerceStore - | CommerceRecommendationStore; + @Prop() store?: CommerceStore | CommerceRecommendationStore; /** * The product content to display. diff --git a/packages/atomic/src/components/common/interface/bindings.ts b/packages/atomic/src/components/common/interface/bindings.ts index bd891cc0692..4f5f6ca21bf 100644 --- a/packages/atomic/src/components/common/interface/bindings.ts +++ b/packages/atomic/src/components/common/interface/bindings.ts @@ -8,14 +8,14 @@ import {CommerceRecommendationStore} from '../../commerce/atomic-commerce-recomm import {InsightEngine} from '../../insight'; import {InsightStore} from '../../insight/atomic-insight-interface/store'; import {RecsStore} from '../../recommendations/atomic-recs-interface/store'; -import {AtomicCommonStore, AtomicCommonStoreData} from './store'; +import {SearchStore} from '../../search/atomic-search-interface/store'; /** * Bindings passed from an interface to its children components. */ export interface CommonBindings< Engine extends AnyEngineType, - Store extends AtomicCommonStore | AnyStore, + Store extends AnyStore, InterfaceElement extends HTMLStencilElement, > { /** @@ -54,7 +54,7 @@ export interface NonceBindings { export type AnyBindings = CommonBindings< AnyEngineType, // Instead of AtomicCommonStoreData, it should follow AnyengineType and to CommerceStore| SearchStore | ... - AtomicCommonStore | AnyStore, + AnyStore, HTMLStencilElement >; @@ -68,4 +68,5 @@ type AnyStore = | CommerceStore | CommerceRecommendationStore | RecsStore - | InsightStore; + | InsightStore + | SearchStore; diff --git a/packages/atomic/src/components/common/interface/store.ts b/packages/atomic/src/components/common/interface/store.ts index d82ca7d5212..565c764d8ad 100644 --- a/packages/atomic/src/components/common/interface/store.ts +++ b/packages/atomic/src/components/common/interface/store.ts @@ -1,22 +1,3 @@ -import {createStore} from '@stencil/store'; -import {isInDocument} from '../../../utils/utils'; -import { - FacetInfo, - FacetStore, - FacetType, - FacetValueFormat, -} from '../facets/facet-common-store'; -import {DateFacetValue, NumericFacetValue} from '../types'; -import {AnyEngineType} from './bindings'; - -export interface CommonStencilStore { - state: StoreData; - onChange: ( - propName: PropName, - cb: (newValue: StoreData[PropName]) => void - ) => () => void; -} - export interface CommonStore { state: StoreData; onChange: ( @@ -29,126 +10,3 @@ export interface ResultListInfo { focusOnNextNewResult(): void; focusOnFirstResultAfterNextSearch(): Promise; } - -export type AtomicCommonStoreData = { - facets: FacetStore; - numericFacets: FacetStore>; - dateFacets: FacetStore>; - categoryFacets: FacetStore; - loadingFlags: string[]; - iconAssetsPath: string; - fieldsToInclude: string[]; - facetElements: HTMLElement[]; - resultList?: ResultListInfo; -}; - -export interface AtomicCommonStore - extends CommonStencilStore { - getIconAssetsPath(): string; - setLoadingFlag(flag: string): void; - unsetLoadingFlag(loadingFlag: string): void; - hasLoadingFlag(loadingFlag: string): boolean; - waitUntilAppLoaded(callback: () => void): void; - isAppLoaded(): boolean; - getUniqueIDFromEngine(engine: AnyEngineType): string; - getFacetElements(): HTMLElement[]; - registerFacet( - facetType: T, - data: StoreData[T][U] & {facetId: U; element: HTMLElement} - ): void; - registerResultList(data: ResultListInfo): void; - addFieldsToInclude(fields: string[]): void; -} - -export const isRefineModalFacet = 'is-refine-modal'; - -export function createAtomicCommonStore< - StoreData extends AtomicCommonStoreData, ->(initialStoreData: StoreData): AtomicCommonStore { - const stencilStore = createStore( - initialStoreData - ) as CommonStencilStore; - - const clearExistingFacetElement = (facetType: FacetType, facetId: string) => { - if (stencilStore.state[facetType][facetId]) { - stencilStore.state.facetElements = - stencilStore.state.facetElements.filter( - (facetElement) => facetElement.getAttribute('facet-id') !== facetId - ); - } - }; - - return { - ...stencilStore, - - registerFacet( - facetType: T, - data: StoreData[T][U] & {facetId: U; element: HTMLElement} - ) { - if (data.element.getAttribute(isRefineModalFacet) !== null) { - return; - } - - clearExistingFacetElement(facetType, data.facetId); - stencilStore.state.facetElements.push(data.element); - stencilStore.state[facetType][data.facetId] = data; - }, - - getIconAssetsPath() { - return stencilStore.state.iconAssetsPath; - }, - - setLoadingFlag(loadingFlag: string) { - const flags = stencilStore.state.loadingFlags; - stencilStore.state.loadingFlags = flags.concat(loadingFlag); - }, - - unsetLoadingFlag(loadingFlag: string) { - const flags = stencilStore.state.loadingFlags; - stencilStore.state.loadingFlags = flags.filter( - (value) => value !== loadingFlag - ); - }, - - hasLoadingFlag(loadingFlag: string) { - return stencilStore.state.loadingFlags.indexOf(loadingFlag) !== -1; - }, - - registerResultList(data: ResultListInfo) { - stencilStore.state.resultList = data; - }, - - addFieldsToInclude(fields) { - const currentFields = stencilStore.state.fieldsToInclude; - stencilStore.state.fieldsToInclude = [...currentFields, ...fields]; - }, - - waitUntilAppLoaded(callback: () => void) { - if (!stencilStore.state.loadingFlags.length) { - callback(); - } else { - stencilStore.onChange('loadingFlags', (flags) => { - if (!flags.length) { - callback(); - } - }); - } - }, - - isAppLoaded() { - return !stencilStore.state.loadingFlags.length; - }, - - getUniqueIDFromEngine(_engine: AnyEngineType): string { - throw new Error( - 'getUniqueIDFromEngine not implemented at the common store level.' - ); - }, - - getFacetElements() { - return stencilStore.state.facetElements.filter((element) => - isInDocument(element) - ); - }, - }; -} diff --git a/packages/atomic/src/components/common/item-list/item-list-common.tsx b/packages/atomic/src/components/common/item-list/item-list-common.tsx index 9c04f166acf..7e55a743810 100644 --- a/packages/atomic/src/components/common/item-list/item-list-common.tsx +++ b/packages/atomic/src/components/common/item-list/item-list-common.tsx @@ -8,8 +8,8 @@ import {CommerceStore} from '../../commerce/atomic-commerce-interface/store'; import {CommerceRecommendationStore} from '../../commerce/atomic-commerce-recommendation-interface/store'; import {InsightStore} from '../../insight/atomic-insight-interface/store'; import {RecsStore} from '../../recommendations/atomic-recs-interface/store'; +import {SearchStore} from '../../search/atomic-search-interface/store'; import {AnyItem} from '../interface/item'; -import {AtomicCommonStore, AtomicCommonStoreData} from '../interface/store'; import { ItemDisplayDensity, ItemDisplayImageSize, @@ -27,11 +27,11 @@ export type ItemRenderingFunction = export interface ItemListCommonProps { store: - | AtomicCommonStore | CommerceStore | CommerceRecommendationStore | RecsStore - | InsightStore; + | InsightStore + | SearchStore; loadingFlag: string; host: HTMLElement; nextNewItemTarget: FocusTargetController; diff --git a/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx b/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx index 347205361da..882e8460a2e 100644 --- a/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx +++ b/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx @@ -64,6 +64,7 @@ export class AtomicFacetManager implements InitializableComponent { const {visibleFacets, invisibleFacets} = sortFacetVisibility( sortedFacets, + //Is this needed for insight ??? this.bindings.store.getAllFacets() ); diff --git a/packages/atomic/src/components/search/atomic-refine-modal/atomic-refine-modal.tsx b/packages/atomic/src/components/search/atomic-refine-modal/atomic-refine-modal.tsx index 3f04edf7eb8..9674ba3c72e 100644 --- a/packages/atomic/src/components/search/atomic-refine-modal/atomic-refine-modal.tsx +++ b/packages/atomic/src/components/search/atomic-refine-modal/atomic-refine-modal.tsx @@ -34,7 +34,6 @@ import { collapseFacetsAfter, } from '../../common/facets/facet-common'; import {popoverClass} from '../../common/facets/popover/popover-type'; -import {isRefineModalFacet} from '../../common/interface/store'; import {RefineModalBody} from '../../common/refine-modal/body'; import { RefineModalFiltersClearButton, @@ -43,7 +42,10 @@ import { import {RefineModal} from '../../common/refine-modal/modal'; import {RefineModalSortSection} from '../../common/refine-modal/sort'; import {Bindings} from '../atomic-search-interface/atomic-search-interface'; -import {SortDropdownOption} from '../atomic-search-interface/store'; +import { + isRefineModalFacet, + SortDropdownOption, +} from '../atomic-search-interface/store'; /** * The `atomic-refine-modal` is automatically created as a child of the `atomic-search-interface` when the `atomic-refine-toggle` is initialized. diff --git a/packages/atomic/src/components/search/atomic-result/atomic-result.tsx b/packages/atomic/src/components/search/atomic-result/atomic-result.tsx index ccd632b6976..ba3451351db 100644 --- a/packages/atomic/src/components/search/atomic-result/atomic-result.tsx +++ b/packages/atomic/src/components/search/atomic-result/atomic-result.tsx @@ -2,10 +2,6 @@ import {FoldedResult, InteractiveResult, Result} from '@coveo/headless'; import {Component, h, Prop, Element, Listen, Host} from '@stencil/core'; import {parentNodeToString} from '../../../utils/dom-utils'; import {applyFocusVisiblePolyfill} from '../../../utils/initialization-utils'; -import { - AtomicCommonStore, - AtomicCommonStoreData, -} from '../../common/interface/store'; import {DisplayConfig} from '../../common/item-list/item-decorators'; import { ItemRenderingFunction, @@ -17,6 +13,7 @@ import { ItemDisplayImageSize, ItemDisplayLayout, } from '../../common/layout/display-options'; +import {SearchStore} from '../atomic-search-interface/store'; import { InteractiveResultContextEvent, ResultContextEvent, @@ -55,7 +52,7 @@ export class AtomicResult { * Global Atomic state. * @internal */ - @Prop() store?: AtomicCommonStore; + @Prop() store?: SearchStore; /** * The result content to display. diff --git a/packages/atomic/src/components/search/atomic-search-interface/analytics-config.spec.ts b/packages/atomic/src/components/search/atomic-search-interface/analytics-config.spec.ts index cf53768ac93..a2c05aa80ae 100644 --- a/packages/atomic/src/components/search/atomic-search-interface/analytics-config.spec.ts +++ b/packages/atomic/src/components/search/atomic-search-interface/analytics-config.spec.ts @@ -4,13 +4,13 @@ import { SearchEngineConfiguration, } from '@coveo/headless'; import {getAnalyticsConfig} from './analytics-config'; -import {createAtomicStore} from './store'; +import {createSearchStore} from './store'; jest.mock('../../../global/environment'); describe('analyticsConfig', () => { let config: SearchEngineConfiguration; - let store: ReturnType; + let store: ReturnType; const originalReferrer = document.referrer; const setReferrer = (value: string) => { Object.defineProperty(document, 'referrer', {value, configurable: true}); @@ -42,7 +42,7 @@ describe('analyticsConfig', () => { ])('$describeName', ({getSearchEngineConfig}) => { beforeEach(() => { config = getSearchEngineConfig(); - store = createAtomicStore(); + store = createSearchStore(); }); afterEach(() => { diff --git a/packages/atomic/src/components/search/atomic-search-interface/analytics-config.ts b/packages/atomic/src/components/search/atomic-search-interface/analytics-config.ts index d83197827c6..1273139d3b9 100644 --- a/packages/atomic/src/components/search/atomic-search-interface/analytics-config.ts +++ b/packages/atomic/src/components/search/atomic-search-interface/analytics-config.ts @@ -10,12 +10,12 @@ import { augmentAnalyticsConfigWithAtomicVersion, getNextAnalyticsConfig, } from '../../common/interface/analytics-config'; -import {createAtomicStore} from './store'; +import {createSearchStore} from './store'; export function getAnalyticsConfig( searchEngineConfig: SearchEngineConfiguration, enabled: boolean, - store: ReturnType + store: ReturnType ): AnalyticsConfiguration { switch (searchEngineConfig.analytics?.analyticsMode) { case 'next': @@ -29,7 +29,7 @@ export function getAnalyticsConfig( function getLegacyAnalyticsConfig( searchEngineConfig: SearchEngineConfiguration, enabled: boolean, - store: ReturnType + store: ReturnType ): AnalyticsConfiguration { const analyticsClientMiddleware = ( event: string, @@ -64,7 +64,7 @@ function getLegacyAnalyticsConfig( function augmentAnalytics( event: string, payload: AnalyticsPayload, - store: ReturnType, + store: ReturnType, config: SearchEngineConfiguration ) { let result = augmentWithExternalMiddleware(event, payload, config); @@ -75,7 +75,7 @@ function augmentAnalytics( function augmentAnalyticsWithFacetTitles( payload: AnalyticsPayload, - store: ReturnType + store: ReturnType ) { const allFacets = store.getAllFacets(); const getAtomicFacetLabelOrOriginalTitle = ( diff --git a/packages/atomic/src/components/search/atomic-search-interface/atomic-search-interface.tsx b/packages/atomic/src/components/search/atomic-search-interface/atomic-search-interface.tsx index d9ad275241b..304b58493ad 100644 --- a/packages/atomic/src/components/search/atomic-search-interface/atomic-search-interface.tsx +++ b/packages/atomic/src/components/search/atomic-search-interface/atomic-search-interface.tsx @@ -44,13 +44,13 @@ import { noResultsSelector, } from '../atomic-layout/search-layout'; import {getAnalyticsConfig} from './analytics-config'; -import {AtomicStore, createAtomicStore} from './store'; +import {createSearchStore, SearchStore} from './store'; const FirstSearchExecutedFlag = 'firstSearchExecuted'; export type InitializationOptions = SearchEngineConfiguration; export type Bindings = CommonBindings< SearchEngine, - AtomicStore, + SearchStore, HTMLAtomicSearchInterfaceElement > & NonceBindings; @@ -72,7 +72,7 @@ export class AtomicSearchInterface private unsubscribeUrlManager: Unsubscribe = () => {}; private unsubscribeSearchStatus: Unsubscribe = () => {}; private initialized = false; - private store = createAtomicStore(); + private store = createSearchStore(); private commonInterfaceHelper: CommonAtomicInterfaceHelper; @Element() public host!: HTMLAtomicSearchInterfaceElement; diff --git a/packages/atomic/src/components/search/atomic-search-interface/store.ts b/packages/atomic/src/components/search/atomic-search-interface/store.ts index 8e5c9697245..4ce348fbe6e 100644 --- a/packages/atomic/src/components/search/atomic-search-interface/store.ts +++ b/packages/atomic/src/components/search/atomic-search-interface/store.ts @@ -4,19 +4,19 @@ import { SortCriterion, SearchEngine, } from '@coveo/headless'; +import {createStore} from '@stencil/store'; import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; +import {isInDocument} from '../../../utils/utils'; import { FacetInfo, FacetStore, + FacetType, FacetValueFormat, } from '../../common/facets/facet-common-store'; -import { - createAtomicCommonStore, - AtomicCommonStoreData, - AtomicCommonStore, -} from '../../common/interface/store'; +import {CommonStore, ResultListInfo} from '../../common/interface/store'; import {makeDesktopQuery} from '../atomic-layout/search-layout'; +// what does this have to do with store... export interface SortDropdownOption { tabs: {included: string[] | string; excluded: string[] | string}; expression: string; @@ -24,64 +24,142 @@ export interface SortDropdownOption { label: string; } -export interface AtomicStoreData extends AtomicCommonStoreData { +interface FacetInfoMap { + [facetId: string]: + | FacetInfo + | (FacetInfo & FacetValueFormat) + | (FacetInfo & FacetValueFormat); +} + +interface Data { + mobileBreakpoint: string; facets: FacetStore; numericFacets: FacetStore>; dateFacets: FacetStore>; categoryFacets: FacetStore; + resultList?: ResultListInfo; + loadingFlags: string[]; + facetElements: HTMLElement[]; + iconAssetsPath: string; + fieldsToInclude: string[]; sortOptions: SortDropdownOption[]; - mobileBreakpoint: string; - currentQuickviewPosition: number; } -export interface AtomicStore extends AtomicCommonStore { +export type SearchStore = CommonStore & { + hasLoadingFlag(loadingFlag: string): boolean; + registerFacet( + facetType: T, + data: Data[T][U] & {facetId: U; element: HTMLElement} + ): void; + isAppLoaded(): boolean; getAllFacets(): FacetInfoMap; - + getFacetElements(): HTMLElement[]; + setLoadingFlag(flag: string): void; + unsetLoadingFlag(loadingFlag: string): void; isMobile(): boolean; -} + waitUntilAppLoaded(callback: () => void): void; + getUniqueIDFromEngine(engine: SearchEngine): string; + addFieldsToInclude(fields: string[]): void; +}; -export interface FacetInfoMap { - [facetId: string]: - | FacetInfo - | (FacetInfo & FacetValueFormat) - | (FacetInfo & FacetValueFormat); -} +export const isRefineModalFacet = 'is-refine-modal'; -export function createAtomicStore(): AtomicStore { - const commonStore = createAtomicCommonStore({ +export function createSearchStore(): SearchStore { + const store = createStore({ + iconAssetsPath: '', loadingFlags: [], + mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, facets: {}, numericFacets: {}, dateFacets: {}, categoryFacets: {}, facetElements: [], - sortOptions: [], - iconAssetsPath: '', - mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, fieldsToInclude: [], - currentQuickviewPosition: -1, - }); + sortOptions: [], + }) as CommonStore; + + const clearExistingFacetElement = (facetType: FacetType, facetId: string) => { + if (store.state[facetType][facetId]) { + store.state.facetElements = store.state.facetElements.filter( + (facetElement) => facetElement.getAttribute('facet-id') !== facetId + ); + } + }; return { - ...commonStore, + ...store, + + registerFacet( + facetType: T, + data: Data[T][U] & {facetId: U; element: HTMLElement} + ) { + if (data.element.getAttribute(isRefineModalFacet) !== null) { + return; + } + + clearExistingFacetElement(facetType, data.facetId); + store.state.facetElements.push(data.element); + store.state[facetType][data.facetId] = data; + }, + + isAppLoaded() { + return !store.state.loadingFlags.length; + }, getAllFacets() { return { - ...commonStore.state.facets, - ...commonStore.state.dateFacets, - ...commonStore.state.categoryFacets, - ...commonStore.state.numericFacets, + ...store.state.facets, + ...store.state.dateFacets, + ...store.state.categoryFacets, + ...store.state.numericFacets, }; }, + getFacetElements() { + return store.state.facetElements.filter((element) => + isInDocument(element) + ); + }, + + setLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.concat(loadingFlag); + }, + + unsetLoadingFlag(loadingFlag: string) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + }, + isMobile() { - return !window.matchMedia( - makeDesktopQuery(commonStore.state.mobileBreakpoint) - ).matches; + return !window.matchMedia(makeDesktopQuery(store.state.mobileBreakpoint)) + .matches; + }, + + waitUntilAppLoaded(callback: () => void) { + if (!store.state.loadingFlags.length) { + callback(); + } else { + store.onChange('loadingFlags', (flags) => { + if (!flags.length) { + callback(); + } + }); + } }, + //This does not makes sense in the store... should just be regular functions getUniqueIDFromEngine(engine: SearchEngine): string { return engine.state.search.response.searchUid; }, + + addFieldsToInclude(fields) { + const currentFields = store.state.fieldsToInclude; + store.state.fieldsToInclude = [...currentFields, ...fields]; + }, + + hasLoadingFlag(loadingFlag: string) { + return store.state.loadingFlags.indexOf(loadingFlag) !== -1; + }, }; } diff --git a/packages/atomic/src/components/search/result-template-components/result-template-decorators.spec.tsx b/packages/atomic/src/components/search/result-template-components/result-template-decorators.spec.tsx index 6f779cbd307..275db18e8c2 100644 --- a/packages/atomic/src/components/search/result-template-components/result-template-decorators.spec.tsx +++ b/packages/atomic/src/components/search/result-template-components/result-template-decorators.spec.tsx @@ -8,7 +8,7 @@ import {newSpecPage, SpecPage} from '@stencil/core/testing'; import {MissingParentError} from '../../common/item-list/item-decorators'; import {AtomicResult} from '../atomic-result/atomic-result'; import {AtomicSearchInterface} from '../atomic-search-interface/atomic-search-interface'; -import {createAtomicStore} from '../atomic-search-interface/store'; +import {createSearchStore} from '../atomic-search-interface/store'; import {AtomicResultFieldsList} from './atomic-result-fields-list/atomic-result-fields-list'; import {resultContext} from './result-template-decorators'; @@ -75,7 +75,7 @@ describe('resultContext method', () => { interactiveResult={buildInteractiveResult(engine, { options: {result: mockResult}, })} - store={createAtomicStore()} + store={createSearchStore()} > ), }); diff --git a/packages/atomic/src/utils/initialization-utils.spec.ts b/packages/atomic/src/utils/initialization-utils.spec.ts index 583c16516d3..eb11888b005 100644 --- a/packages/atomic/src/utils/initialization-utils.spec.ts +++ b/packages/atomic/src/utils/initialization-utils.spec.ts @@ -6,7 +6,7 @@ import { AtomicSearchInterface, Bindings, } from '../components/search/atomic-search-interface/atomic-search-interface'; -import {createAtomicStore} from '../components/search/atomic-search-interface/store'; +import {createSearchStore} from '../components/search/atomic-search-interface/store'; import { BindStateToController, InitializableComponent, @@ -112,7 +112,7 @@ describe('InitializeBindings decorator', () => { }, }), i18n: i18next, - store: createAtomicStore(), + store: createSearchStore(), interfaceElement: document.createElement('atomic-search-interface'), createScriptElement: jest.fn(), createStyleElement: jest.fn(), @@ -139,7 +139,7 @@ describe('BindStateToController decorator', () => { }, }), i18n: i18next, - store: createAtomicStore(), + store: createSearchStore(), interfaceElement: document.createElement('atomic-search-interface'), createScriptElement: jest.fn(), createStyleElement: jest.fn(), From c486e23c1acbd779e3e3a97cce9a50e1d1c87ddc Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:33:43 -0500 Subject: [PATCH 7/9] clean up --- packages/atomic/src/components.d.ts | 32 ++++++++----------- .../atomic-commerce-interface/store.ts | 13 -------- .../store.ts | 6 ---- .../common/item-list/item-list-common.tsx | 2 +- .../components/common/refine-modal/modal.tsx | 2 +- .../insight/atomic-insight-interface/store.ts | 5 --- .../atomic-recs-interface/store.ts | 13 -------- .../search/atomic-search-interface/store.ts | 1 - .../atomic/src/utils/accessibility-utils.tsx | 4 +-- 9 files changed, 18 insertions(+), 60 deletions(-) diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index b6fd650f5dd..59cfb6db51e 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -23,11 +23,10 @@ import { NumberInputType } from "./components/common/facets/facet-number-input/n import { NumericFilter, NumericFilterState, RelativeDateUnit } from "./components/common/types"; import { InsightEngine, FacetSortCriterion as InsightFacetSortCriterion, FoldedResult as InsightFoldedResult, InteractiveResult as InsightInteractiveResult, LogLevel as InsightLogLevel, RangeFacetRangeAlgorithm as InsightRangeFacetRangeAlgorithm, RangeFacetSortCriterion as InsightRangeFacetSortCriterion, Result as InsightResult, ResultTemplate as InsightResultTemplate, ResultTemplateCondition as InsightResultTemplateCondition, UserAction as IUserAction } from "./components/insight"; import { InsightInitializationOptions } from "./components/insight/atomic-insight-interface/atomic-insight-interface"; -import { AtomicInsightStore } from "./components/insight/atomic-insight-interface/store"; +import { InsightStore } from "./components/insight/atomic-insight-interface/store"; import { InsightResultActionClickedEvent } from "./components/insight/atomic-insight-result-action/atomic-insight-result-action"; import { InsightResultAttachToCaseEvent } from "./components/insight/atomic-insight-result-attach-to-case-action/atomic-insight-result-attach-to-case-action"; import { Section } from "./components/common/atomic-layout-section/sections"; -import { AtomicCommonStore, AtomicCommonStoreData } from "./components/common/interface/store"; import { CommerceStore } from "./components/commerce/atomic-commerce-interface/store"; import { CommerceRecommendationStore } from "./components/commerce/atomic-commerce-recommendation-interface/store"; import { SelectChildProductEventArgs } from "./components/commerce/product-template-components/atomic-product-children/atomic-product-children"; @@ -35,8 +34,9 @@ import { TruncateAfter } from "./components/common/expandable-text/expandable-te import { RecommendationEngine } from "@coveo/headless/recommendation"; import { InteractiveResult as RecsInteractiveResult, LogLevel as RecsLogLevel, Result as RecsResult, ResultTemplate as RecsResultTemplate, ResultTemplateCondition as RecsResultTemplateCondition } from "./components/recommendations"; import { RecsInitializationOptions } from "./components/recommendations/atomic-recs-interface/atomic-recs-interface"; -import { AtomicRecsStore } from "./components/recommendations/atomic-recs-interface/store"; +import { RecsStore } from "./components/recommendations/atomic-recs-interface/store"; import { Bindings as Bindings1 } from "./components/search/atomic-search-interface/atomic-search-interface"; +import { SearchStore } from "./components/search/atomic-search-interface/store"; import { AriaLabelGenerator as AriaLabelGenerator1 } from "./components/search/search-box-suggestions/atomic-search-box-instant-results/atomic-search-box-instant-results"; import { InitializationOptions } from "./components/search/atomic-search-interface/atomic-search-interface"; export { AutomaticFacet, CategoryFacetSortCriterion, DateFilterRange, DateRangeRequest, FacetResultsMustMatch, FacetSortCriterion, FoldedResult, GeneratedAnswer, GeneratedAnswerCitation, InlineLink, InteractiveCitation, InteractiveResult, LogLevel as LogLevel1, RangeFacetRangeAlgorithm, RangeFacetSortCriterion, Result, ResultTemplate, ResultTemplateCondition, SearchEngine, SearchStatus } from "@coveo/headless"; @@ -57,11 +57,10 @@ export { NumberInputType } from "./components/common/facets/facet-number-input/n export { NumericFilter, NumericFilterState, RelativeDateUnit } from "./components/common/types"; export { InsightEngine, FacetSortCriterion as InsightFacetSortCriterion, FoldedResult as InsightFoldedResult, InteractiveResult as InsightInteractiveResult, LogLevel as InsightLogLevel, RangeFacetRangeAlgorithm as InsightRangeFacetRangeAlgorithm, RangeFacetSortCriterion as InsightRangeFacetSortCriterion, Result as InsightResult, ResultTemplate as InsightResultTemplate, ResultTemplateCondition as InsightResultTemplateCondition, UserAction as IUserAction } from "./components/insight"; export { InsightInitializationOptions } from "./components/insight/atomic-insight-interface/atomic-insight-interface"; -export { AtomicInsightStore } from "./components/insight/atomic-insight-interface/store"; +export { InsightStore } from "./components/insight/atomic-insight-interface/store"; export { InsightResultActionClickedEvent } from "./components/insight/atomic-insight-result-action/atomic-insight-result-action"; export { InsightResultAttachToCaseEvent } from "./components/insight/atomic-insight-result-attach-to-case-action/atomic-insight-result-attach-to-case-action"; export { Section } from "./components/common/atomic-layout-section/sections"; -export { AtomicCommonStore, AtomicCommonStoreData } from "./components/common/interface/store"; export { CommerceStore } from "./components/commerce/atomic-commerce-interface/store"; export { CommerceRecommendationStore } from "./components/commerce/atomic-commerce-recommendation-interface/store"; export { SelectChildProductEventArgs } from "./components/commerce/product-template-components/atomic-product-children/atomic-product-children"; @@ -69,8 +68,9 @@ export { TruncateAfter } from "./components/common/expandable-text/expandable-te export { RecommendationEngine } from "@coveo/headless/recommendation"; export { InteractiveResult as RecsInteractiveResult, LogLevel as RecsLogLevel, Result as RecsResult, ResultTemplate as RecsResultTemplate, ResultTemplateCondition as RecsResultTemplateCondition } from "./components/recommendations"; export { RecsInitializationOptions } from "./components/recommendations/atomic-recs-interface/atomic-recs-interface"; -export { AtomicRecsStore } from "./components/recommendations/atomic-recs-interface/store"; +export { RecsStore } from "./components/recommendations/atomic-recs-interface/store"; export { Bindings as Bindings1 } from "./components/search/atomic-search-interface/atomic-search-interface"; +export { SearchStore } from "./components/search/atomic-search-interface/store"; export { AriaLabelGenerator as AriaLabelGenerator1 } from "./components/search/search-box-suggestions/atomic-search-box-instant-results/atomic-search-box-instant-results"; export { InitializationOptions } from "./components/search/atomic-search-interface/atomic-search-interface"; export namespace Components { @@ -1413,7 +1413,7 @@ export namespace Components { /** * Global Atomic state. */ - "store"?: AtomicInsightStore; + "store"?: InsightStore; } interface AtomicInsightResultAction { /** @@ -2056,9 +2056,7 @@ export namespace Components { * Global Atomic state. * @alpha */ - "store"?: | AtomicCommonStore - | CommerceStore - | CommerceRecommendationStore; + "store"?: CommerceStore | CommerceRecommendationStore; } /** * @alpha The `atomic-product-children` component renders a section that allows the user to select a nested product (e.g., a color variant of a given product). @@ -2702,7 +2700,7 @@ export namespace Components { /** * Global Atomic state. */ - "store"?: AtomicRecsStore; + "store"?: RecsStore; } /** * A [result template](https://docs.coveo.com/en/atomic/latest/usage/displaying-results#defining-a-result-template) determines the format of the query results, depending on the conditions that are defined for each template. @@ -2824,7 +2822,7 @@ export namespace Components { /** * Global Atomic state. */ - "store"?: AtomicCommonStore; + "store"?: SearchStore; } /** * The `atomic-result-badge` element renders a badge to highlight special features of a result. @@ -7573,7 +7571,7 @@ declare namespace LocalJSX { /** * Global Atomic state. */ - "store"?: AtomicInsightStore; + "store"?: InsightStore; } interface AtomicInsightResultAction { /** @@ -8191,9 +8189,7 @@ declare namespace LocalJSX { * Global Atomic state. * @alpha */ - "store"?: | AtomicCommonStore - | CommerceStore - | CommerceRecommendationStore; + "store"?: CommerceStore | CommerceRecommendationStore; } /** * @alpha The `atomic-product-children` component renders a section that allows the user to select a nested product (e.g., a color variant of a given product). @@ -8797,7 +8793,7 @@ declare namespace LocalJSX { /** * Global Atomic state. */ - "store"?: AtomicRecsStore; + "store"?: RecsStore; } /** * A [result template](https://docs.coveo.com/en/atomic/latest/usage/displaying-results#defining-a-result-template) determines the format of the query results, depending on the conditions that are defined for each template. @@ -8916,7 +8912,7 @@ declare namespace LocalJSX { /** * Global Atomic state. */ - "store"?: AtomicCommonStore; + "store"?: SearchStore; } /** * The `atomic-result-badge` element renders a badge to highlight special features of a result. diff --git a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts index 1d0ab42717b..4881572b6ba 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts @@ -9,23 +9,19 @@ import {CommonStore, ResultListInfo} from '../../common/interface/store'; import {makeDesktopQuery} from '../../search/atomic-layout/search-layout'; interface Data { - //IMPORTANT does mobileBreakpoint work in commerce-layout ?? it does not get updated... mobileBreakpoint: string; activeProductChild: ChildProduct | undefined; - // why is this undefined I don't like resultList?: ResultListInfo; iconAssetsPath: string; loadingFlags: string[]; } export type CommerceStore = CommonStore & { - hasLoadingFlag(loadingFlag: string): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; isAppLoaded(): boolean; isMobile(): boolean; getUniqueIDFromEngine(engine: CommerceEngine): string; - registerResultList(data: ResultListInfo): void; }; export function createCommerceStore( @@ -41,10 +37,6 @@ export function createCommerceStore( return { ...store, - hasLoadingFlag(loadingFlag: string) { - return store.state.loadingFlags.indexOf(loadingFlag) !== -1; - }, - unsetLoadingFlag(loadingFlag: string) { const flags = store.state.loadingFlags; store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); @@ -76,10 +68,5 @@ export function createCommerceStore( ); } }, - - // This is not necessary, we could just do store.state.resultList = data; - registerResultList(data: ResultListInfo) { - store.state.resultList = data; - }, }; } diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts index 1cde834e143..e3468bbbec4 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts @@ -11,7 +11,6 @@ export type CommerceRecommendationStore = CommonStore & { isAppLoaded(): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; - registerResultList(data: ResultListInfo): void; // getUniqueIDFromEngine(engine: CommerceEngine): string; }; @@ -38,11 +37,6 @@ export function createCommerceRecommendationStore(): CommerceRecommendationStore store.state.loadingFlags = flags.concat(loadingFlag); }, - // This is not necessary, we could just do store.state.resultList = data; - registerResultList(data: ResultListInfo) { - store.state.resultList = data; - }, - // getUniqueIDFromEngine(engine: CommerceEngine): string { // return null; // }, diff --git a/packages/atomic/src/components/common/item-list/item-list-common.tsx b/packages/atomic/src/components/common/item-list/item-list-common.tsx index 7e55a743810..9fa0267ffca 100644 --- a/packages/atomic/src/components/common/item-list/item-list-common.tsx +++ b/packages/atomic/src/components/common/item-list/item-list-common.tsx @@ -47,7 +47,7 @@ export class ItemListCommon { constructor(private props: ItemListCommonProps) { this.props.store.setLoadingFlag(this.props.loadingFlag); - this.props.store.registerResultList(this); + this.props.store.state.resultList = this; this.updateBreakpointsOnce = once(() => updateBreakpoints(this.props.host)); } diff --git a/packages/atomic/src/components/common/refine-modal/modal.tsx b/packages/atomic/src/components/common/refine-modal/modal.tsx index 3d8e80a0667..517aef0ce95 100644 --- a/packages/atomic/src/components/common/refine-modal/modal.tsx +++ b/packages/atomic/src/components/common/refine-modal/modal.tsx @@ -1,10 +1,10 @@ import {FunctionalComponent, h} from '@stencil/core'; import {i18n} from 'i18next'; import CloseIcon from '../../../images/close.svg'; +import {isRefineModalFacet} from '../../search/atomic-search-interface/store'; import {Button} from '../button'; import {BaseFacetElement} from '../facets/facet-common'; import {popoverClass} from '../facets/popover/popover-type'; -import {isRefineModalFacet} from '../interface/store'; interface RefineModalProps { host: HTMLElement; diff --git a/packages/atomic/src/components/insight/atomic-insight-interface/store.ts b/packages/atomic/src/components/insight/atomic-insight-interface/store.ts index 784833fd853..4fb19ffcaf8 100644 --- a/packages/atomic/src/components/insight/atomic-insight-interface/store.ts +++ b/packages/atomic/src/components/insight/atomic-insight-interface/store.ts @@ -34,7 +34,6 @@ export type InsightStore = CommonStore & { isAppLoaded(): boolean; getFacetElements(): HTMLElement[]; waitUntilAppLoaded(callback: () => void): void; - registerResultList(data: ResultListInfo): void; getUniqueIDFromEngine(engine: InsightEngine): string; }; @@ -109,10 +108,6 @@ export function createInsightStore(): InsightStore { } }, - registerResultList(data: ResultListInfo) { - store.state.resultList = data; - }, - getUniqueIDFromEngine(engine: InsightEngine): string { return engine.state.search.searchResponseId; }, diff --git a/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts b/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts index 40662e80b6a..f1daa75ef34 100644 --- a/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts +++ b/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts @@ -3,24 +3,16 @@ import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; import {CommonStore, ResultListInfo} from '../../common/interface/store'; interface Data { - //IMPORTANT does mobileBreakpoint work in commerce-layout ?? it does not get updated... mobileBreakpoint: string; loadingFlags: string[]; - // activeProductChild: ChildProduct | undefined; - // why is this undefined I don't like - // resultList?: ResultListInfo; iconAssetsPath: string; resultList?: ResultListInfo; - - // loadingFlags: string[]; } export type RecsStore = CommonStore & { - // hasLoadingFlag(loadingFlag: string): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; isAppLoaded(): boolean; - registerResultList(data: ResultListInfo): void; }; export function createRecsStore(): RecsStore { @@ -46,10 +38,5 @@ export function createRecsStore(): RecsStore { isAppLoaded() { return !store.state.loadingFlags.length; }, - - // This is not necessary, we could just do store.state.resultList = data; - registerResultList(data: ResultListInfo) { - store.state.resultList = data; - }, }; } diff --git a/packages/atomic/src/components/search/atomic-search-interface/store.ts b/packages/atomic/src/components/search/atomic-search-interface/store.ts index 4ce348fbe6e..f89399afb4d 100644 --- a/packages/atomic/src/components/search/atomic-search-interface/store.ts +++ b/packages/atomic/src/components/search/atomic-search-interface/store.ts @@ -16,7 +16,6 @@ import { import {CommonStore, ResultListInfo} from '../../common/interface/store'; import {makeDesktopQuery} from '../atomic-layout/search-layout'; -// what does this have to do with store... export interface SortDropdownOption { tabs: {included: string[] | string; excluded: string[] | string}; expression: string; diff --git a/packages/atomic/src/utils/accessibility-utils.tsx b/packages/atomic/src/utils/accessibility-utils.tsx index 543cdbeaeab..a4df742a25d 100644 --- a/packages/atomic/src/utils/accessibility-utils.tsx +++ b/packages/atomic/src/utils/accessibility-utils.tsx @@ -91,7 +91,7 @@ export class FocusTargetController { public disableForCurrentSearch() { if ( - //@ts-expect-error normal for now, should go away + //@ts-expect-error to fix this.bindings.store.getUniqueIDFromEngine(this.bindings.engine) !== this.lastSearchId ) { @@ -110,7 +110,7 @@ export class FocusTargetController { } if ( this.doFocusAfterSearch && - //@ts-expect-error normal for now, should go away + //@ts-expect-error to fix this.bindings.store.getUniqueIDFromEngine(this.bindings.engine) !== this.lastSearchId ) { From 8e4b709f64857c6a1737d8d04ba3326866e59282 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Mon, 23 Dec 2024 13:43:09 -0500 Subject: [PATCH 8/9] clean up into common functions --- .../atomic-commerce-interface/store.ts | 36 +++--- .../store.ts | 18 +-- .../components/common/interface/bindings.ts | 1 - .../src/components/common/interface/store.ts | 76 ++++++++++++ .../components/common/refine-modal/modal.tsx | 2 +- .../insight/atomic-insight-interface/store.ts | 79 +++++------- .../atomic-recs-interface/store.ts | 29 ++--- .../atomic-facet-manager.tsx | 1 - .../atomic-refine-modal.tsx | 6 +- .../search/atomic-search-interface/store.ts | 116 ++++++++---------- 10 files changed, 199 insertions(+), 165 deletions(-) diff --git a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts index 4881572b6ba..57bc2d94d2c 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts @@ -5,21 +5,26 @@ import { } from '@coveo/headless/commerce'; import {createStore} from '@stencil/store'; import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; -import {CommonStore, ResultListInfo} from '../../common/interface/store'; +import { + CommonStore, + ResultListInfo, + setLoadingFlag, + unsetLoadingFlag, +} from '../../common/interface/store'; import {makeDesktopQuery} from '../../search/atomic-layout/search-layout'; interface Data { + loadingFlags: string[]; + iconAssetsPath: string; + resultList: ResultListInfo | undefined; mobileBreakpoint: string; activeProductChild: ChildProduct | undefined; - resultList?: ResultListInfo; - iconAssetsPath: string; - loadingFlags: string[]; } export type CommerceStore = CommonStore & { + isAppLoaded(): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; - isAppLoaded(): boolean; isMobile(): boolean; getUniqueIDFromEngine(engine: CommerceEngine): string; }; @@ -29,26 +34,25 @@ export function createCommerceStore( ): CommerceStore { const store = createStore({ loadingFlags: [], + iconAssetsPath: '', + resultList: undefined, mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, activeProductChild: undefined, - iconAssetsPath: '', }) as CommonStore; return { ...store, - unsetLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + isAppLoaded() { + return !store.state.loadingFlags.length; }, - setLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.concat(loadingFlag); + unsetLoadingFlag(loadingFlag: string) { + unsetLoadingFlag(store, loadingFlag); }, - isAppLoaded() { - return !store.state.loadingFlags.length; + setLoadingFlag(loadingFlag: string) { + setLoadingFlag(store, loadingFlag); }, isMobile() { @@ -62,10 +66,6 @@ export function createCommerceStore( return Selectors.Search.responseIdSelector(engine); case 'product-listing': return Selectors.ProductListing.responseIdSelector(engine); - default: - throw new Error( - `getUniqueIDFromEngine not implemented for this interface type, ${type}` - ); } }, }; diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts index e3468bbbec4..4438f3a6b05 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts @@ -1,10 +1,15 @@ import {createStore} from '@stencil/store'; -import {CommonStore, ResultListInfo} from '../../common/interface/store'; +import { + CommonStore, + ResultListInfo, + setLoadingFlag, + unsetLoadingFlag, +} from '../../common/interface/store'; interface Data { - iconAssetsPath: string; loadingFlags: string[]; - resultList?: ResultListInfo; + iconAssetsPath: string; + resultList: ResultListInfo | undefined; } export type CommerceRecommendationStore = CommonStore & { @@ -18,6 +23,7 @@ export function createCommerceRecommendationStore(): CommerceRecommendationStore const store = createStore({ loadingFlags: [], iconAssetsPath: '', + resultList: undefined, }) as CommonStore; return { @@ -28,13 +34,11 @@ export function createCommerceRecommendationStore(): CommerceRecommendationStore }, unsetLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + unsetLoadingFlag(store, loadingFlag); }, setLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.concat(loadingFlag); + setLoadingFlag(store, loadingFlag); }, // getUniqueIDFromEngine(engine: CommerceEngine): string { diff --git a/packages/atomic/src/components/common/interface/bindings.ts b/packages/atomic/src/components/common/interface/bindings.ts index 4f5f6ca21bf..374aa13383c 100644 --- a/packages/atomic/src/components/common/interface/bindings.ts +++ b/packages/atomic/src/components/common/interface/bindings.ts @@ -53,7 +53,6 @@ export interface NonceBindings { export type AnyBindings = CommonBindings< AnyEngineType, - // Instead of AtomicCommonStoreData, it should follow AnyengineType and to CommerceStore| SearchStore | ... AnyStore, HTMLStencilElement >; diff --git a/packages/atomic/src/components/common/interface/store.ts b/packages/atomic/src/components/common/interface/store.ts index 565c764d8ad..239768afcdb 100644 --- a/packages/atomic/src/components/common/interface/store.ts +++ b/packages/atomic/src/components/common/interface/store.ts @@ -1,3 +1,12 @@ +import {DateFacetValue, NumericFacetValue} from '@coveo/headless'; +import {isInDocument} from '../../../utils/utils'; +import { + FacetInfo, + FacetStore, + FacetType, + FacetValueFormat, +} from '../facets/facet-common-store'; + export interface CommonStore { state: StoreData; onChange: ( @@ -10,3 +19,70 @@ export interface ResultListInfo { focusOnNextNewResult(): void; focusOnFirstResultAfterNextSearch(): Promise; } + +export function unsetLoadingFlag( + store: CommonStore<{loadingFlags: string[]}>, + loadingFlag: string +) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); +} + +export function setLoadingFlag( + store: CommonStore<{loadingFlags: string[]}>, + loadingFlag: string +) { + const flags = store.state.loadingFlags; + store.state.loadingFlags = flags.concat(loadingFlag); +} + +interface Facets { + facets: FacetStore; + numericFacets: FacetStore>; + dateFacets: FacetStore>; + categoryFacets: FacetStore; + facetElements: HTMLElement[]; +} + +export const isRefineModalFacet = 'is-refine-modal'; + +export function registerFacet( + store: CommonStore, + facetType: T, + data: Facets[T][U] & {facetId: U; element: HTMLElement} +) { + const clearExistingFacetElement = (facetType: FacetType, facetId: string) => { + if (store.state[facetType][facetId]) { + store.state.facetElements = store.state.facetElements.filter( + (facetElement) => facetElement.getAttribute('facet-id') !== facetId + ); + } + }; + + if (data.element.getAttribute(isRefineModalFacet) !== null) { + return; + } + + clearExistingFacetElement(facetType, data.facetId); + store.state.facetElements.push(data.element); + store.state[facetType][data.facetId] = data; +} + +export function getFacetElements(store: CommonStore) { + return store.state.facetElements.filter((element) => isInDocument(element)); +} + +export function waitUntilAppLoaded( + store: CommonStore<{loadingFlags: string[]}>, + callback: () => void +) { + if (!store.state.loadingFlags.length) { + callback(); + } else { + store.onChange('loadingFlags', (flags) => { + if (!flags.length) { + callback(); + } + }); + } +} diff --git a/packages/atomic/src/components/common/refine-modal/modal.tsx b/packages/atomic/src/components/common/refine-modal/modal.tsx index 517aef0ce95..3d8e80a0667 100644 --- a/packages/atomic/src/components/common/refine-modal/modal.tsx +++ b/packages/atomic/src/components/common/refine-modal/modal.tsx @@ -1,10 +1,10 @@ import {FunctionalComponent, h} from '@stencil/core'; import {i18n} from 'i18next'; import CloseIcon from '../../../images/close.svg'; -import {isRefineModalFacet} from '../../search/atomic-search-interface/store'; import {Button} from '../button'; import {BaseFacetElement} from '../facets/facet-common'; import {popoverClass} from '../facets/popover/popover-type'; +import {isRefineModalFacet} from '../interface/store'; interface RefineModalProps { host: HTMLElement; diff --git a/packages/atomic/src/components/insight/atomic-insight-interface/store.ts b/packages/atomic/src/components/insight/atomic-insight-interface/store.ts index 4fb19ffcaf8..3e8e7b63ac8 100644 --- a/packages/atomic/src/components/insight/atomic-insight-interface/store.ts +++ b/packages/atomic/src/components/insight/atomic-insight-interface/store.ts @@ -1,49 +1,52 @@ import {InsightEngine} from '@coveo/headless/insight'; import {createStore} from '@stencil/store'; -import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; -import {isInDocument} from '../../../utils/utils'; import { FacetInfo, FacetStore, FacetType, FacetValueFormat, } from '../../common/facets/facet-common-store'; -import {CommonStore, ResultListInfo} from '../../common/interface/store'; +import { + CommonStore, + getFacetElements, + registerFacet, + ResultListInfo, + setLoadingFlag, + unsetLoadingFlag, + waitUntilAppLoaded, +} from '../../common/interface/store'; import {DateFacetValue, NumericFacetValue} from '../../common/types'; interface Data { - mobileBreakpoint: string; loadingFlags: string[]; + iconAssetsPath: string; + resultList: ResultListInfo | undefined; facets: FacetStore; numericFacets: FacetStore>; dateFacets: FacetStore>; categoryFacets: FacetStore; - iconAssetsPath: string; facetElements: HTMLElement[]; - resultList?: ResultListInfo; fieldsToInclude: string[]; } export type InsightStore = CommonStore & { + isAppLoaded(): boolean; + unsetLoadingFlag(loadingFlag: string): void; + setLoadingFlag(flag: string): void; registerFacet( facetType: T, data: Data[T][U] & {facetId: U; element: HTMLElement} ): void; - setLoadingFlag(flag: string): void; - unsetLoadingFlag(loadingFlag: string): void; - isAppLoaded(): boolean; getFacetElements(): HTMLElement[]; waitUntilAppLoaded(callback: () => void): void; getUniqueIDFromEngine(engine: InsightEngine): string; }; -export const isRefineModalFacet = 'is-refine-modal'; - export function createInsightStore(): InsightStore { const store = createStore({ - mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, loadingFlags: [], iconAssetsPath: '', + resultList: undefined, facets: {}, numericFacets: {}, dateFacets: {}, @@ -52,60 +55,34 @@ export function createInsightStore(): InsightStore { fieldsToInclude: [], }) as CommonStore; - const clearExistingFacetElement = (facetType: FacetType, facetId: string) => { - if (store.state[facetType][facetId]) { - store.state.facetElements = store.state.facetElements.filter( - (facetElement) => facetElement.getAttribute('facet-id') !== facetId - ); - } - }; - return { ...store, - registerFacet( - facetType: T, - data: Data[T][U] & {facetId: U; element: HTMLElement} - ) { - if (data.element.getAttribute(isRefineModalFacet) !== null) { - return; - } - - clearExistingFacetElement(facetType, data.facetId); - store.state.facetElements.push(data.element); - store.state[facetType][data.facetId] = data; + isAppLoaded() { + return !store.state.loadingFlags.length; }, - setLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.concat(loadingFlag); + unsetLoadingFlag(loadingFlag: string) { + unsetLoadingFlag(store, loadingFlag); }, - unsetLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + setLoadingFlag(loadingFlag: string) { + setLoadingFlag(store, loadingFlag); }, - isAppLoaded() { - return !store.state.loadingFlags.length; + registerFacet( + facetType: T, + data: Data[T][U] & {facetId: U; element: HTMLElement} + ) { + registerFacet(store, facetType, data); }, getFacetElements() { - return store.state.facetElements.filter((element) => - isInDocument(element) - ); + return getFacetElements(store); }, waitUntilAppLoaded(callback: () => void) { - if (!store.state.loadingFlags.length) { - callback(); - } else { - store.onChange('loadingFlags', (flags) => { - if (!flags.length) { - callback(); - } - }); - } + waitUntilAppLoaded(store, callback); }, getUniqueIDFromEngine(engine: InsightEngine): string { diff --git a/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts b/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts index f1daa75ef34..267b14d5977 100644 --- a/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts +++ b/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts @@ -1,42 +1,43 @@ import {createStore} from '@stencil/store'; -import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; -import {CommonStore, ResultListInfo} from '../../common/interface/store'; +import { + CommonStore, + ResultListInfo, + setLoadingFlag, + unsetLoadingFlag, +} from '../../common/interface/store'; interface Data { - mobileBreakpoint: string; loadingFlags: string[]; iconAssetsPath: string; - resultList?: ResultListInfo; + resultList: ResultListInfo | undefined; } export type RecsStore = CommonStore & { + isAppLoaded(): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; - isAppLoaded(): boolean; }; export function createRecsStore(): RecsStore { const store = createStore({ loadingFlags: [], - mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, iconAssetsPath: '', + resultList: undefined, }) as CommonStore; return { ...store, - unsetLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + isAppLoaded() { + return !store.state.loadingFlags.length; }, - setLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.concat(loadingFlag); + unsetLoadingFlag(loadingFlag: string) { + unsetLoadingFlag(store, loadingFlag); }, - isAppLoaded() { - return !store.state.loadingFlags.length; + setLoadingFlag(loadingFlag: string) { + setLoadingFlag(store, loadingFlag); }, }; } diff --git a/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx b/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx index 882e8460a2e..347205361da 100644 --- a/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx +++ b/packages/atomic/src/components/search/atomic-facet-manager/atomic-facet-manager.tsx @@ -64,7 +64,6 @@ export class AtomicFacetManager implements InitializableComponent { const {visibleFacets, invisibleFacets} = sortFacetVisibility( sortedFacets, - //Is this needed for insight ??? this.bindings.store.getAllFacets() ); diff --git a/packages/atomic/src/components/search/atomic-refine-modal/atomic-refine-modal.tsx b/packages/atomic/src/components/search/atomic-refine-modal/atomic-refine-modal.tsx index 9674ba3c72e..3f04edf7eb8 100644 --- a/packages/atomic/src/components/search/atomic-refine-modal/atomic-refine-modal.tsx +++ b/packages/atomic/src/components/search/atomic-refine-modal/atomic-refine-modal.tsx @@ -34,6 +34,7 @@ import { collapseFacetsAfter, } from '../../common/facets/facet-common'; import {popoverClass} from '../../common/facets/popover/popover-type'; +import {isRefineModalFacet} from '../../common/interface/store'; import {RefineModalBody} from '../../common/refine-modal/body'; import { RefineModalFiltersClearButton, @@ -42,10 +43,7 @@ import { import {RefineModal} from '../../common/refine-modal/modal'; import {RefineModalSortSection} from '../../common/refine-modal/sort'; import {Bindings} from '../atomic-search-interface/atomic-search-interface'; -import { - isRefineModalFacet, - SortDropdownOption, -} from '../atomic-search-interface/store'; +import {SortDropdownOption} from '../atomic-search-interface/store'; /** * The `atomic-refine-modal` is automatically created as a child of the `atomic-search-interface` when the `atomic-refine-toggle` is initialized. diff --git a/packages/atomic/src/components/search/atomic-search-interface/store.ts b/packages/atomic/src/components/search/atomic-search-interface/store.ts index f89399afb4d..cfe0b1ac24a 100644 --- a/packages/atomic/src/components/search/atomic-search-interface/store.ts +++ b/packages/atomic/src/components/search/atomic-search-interface/store.ts @@ -6,14 +6,21 @@ import { } from '@coveo/headless'; import {createStore} from '@stencil/store'; import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; -import {isInDocument} from '../../../utils/utils'; import { FacetInfo, FacetStore, FacetType, FacetValueFormat, } from '../../common/facets/facet-common-store'; -import {CommonStore, ResultListInfo} from '../../common/interface/store'; +import { + CommonStore, + getFacetElements, + registerFacet, + ResultListInfo, + setLoadingFlag, + unsetLoadingFlag, + waitUntilAppLoaded, +} from '../../common/interface/store'; import {makeDesktopQuery} from '../atomic-layout/search-layout'; export interface SortDropdownOption { @@ -31,42 +38,41 @@ interface FacetInfoMap { } interface Data { + loadingFlags: string[]; + iconAssetsPath: string; + resultList: ResultListInfo | undefined; mobileBreakpoint: string; facets: FacetStore; numericFacets: FacetStore>; dateFacets: FacetStore>; categoryFacets: FacetStore; - resultList?: ResultListInfo; - loadingFlags: string[]; facetElements: HTMLElement[]; - iconAssetsPath: string; fieldsToInclude: string[]; sortOptions: SortDropdownOption[]; } export type SearchStore = CommonStore & { - hasLoadingFlag(loadingFlag: string): boolean; + isAppLoaded(): boolean; + unsetLoadingFlag(loadingFlag: string): void; + setLoadingFlag(flag: string): void; + isMobile(): boolean; registerFacet( facetType: T, data: Data[T][U] & {facetId: U; element: HTMLElement} ): void; - isAppLoaded(): boolean; - getAllFacets(): FacetInfoMap; getFacetElements(): HTMLElement[]; - setLoadingFlag(flag: string): void; - unsetLoadingFlag(loadingFlag: string): void; - isMobile(): boolean; waitUntilAppLoaded(callback: () => void): void; getUniqueIDFromEngine(engine: SearchEngine): string; + hasLoadingFlag(loadingFlag: string): boolean; + getAllFacets(): FacetInfoMap; addFieldsToInclude(fields: string[]): void; }; -export const isRefineModalFacet = 'is-refine-modal'; - export function createSearchStore(): SearchStore { const store = createStore({ - iconAssetsPath: '', loadingFlags: [], + iconAssetsPath: '', + resultList: undefined, mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, facets: {}, numericFacets: {}, @@ -77,57 +83,19 @@ export function createSearchStore(): SearchStore { sortOptions: [], }) as CommonStore; - const clearExistingFacetElement = (facetType: FacetType, facetId: string) => { - if (store.state[facetType][facetId]) { - store.state.facetElements = store.state.facetElements.filter( - (facetElement) => facetElement.getAttribute('facet-id') !== facetId - ); - } - }; - return { ...store, - registerFacet( - facetType: T, - data: Data[T][U] & {facetId: U; element: HTMLElement} - ) { - if (data.element.getAttribute(isRefineModalFacet) !== null) { - return; - } - - clearExistingFacetElement(facetType, data.facetId); - store.state.facetElements.push(data.element); - store.state[facetType][data.facetId] = data; - }, - isAppLoaded() { return !store.state.loadingFlags.length; }, - getAllFacets() { - return { - ...store.state.facets, - ...store.state.dateFacets, - ...store.state.categoryFacets, - ...store.state.numericFacets, - }; - }, - - getFacetElements() { - return store.state.facetElements.filter((element) => - isInDocument(element) - ); + unsetLoadingFlag(loadingFlag: string) { + unsetLoadingFlag(store, loadingFlag); }, setLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.concat(loadingFlag); - }, - - unsetLoadingFlag(loadingFlag: string) { - const flags = store.state.loadingFlags; - store.state.loadingFlags = flags.filter((value) => value !== loadingFlag); + setLoadingFlag(store, loadingFlag); }, isMobile() { @@ -135,16 +103,19 @@ export function createSearchStore(): SearchStore { .matches; }, + registerFacet( + facetType: T, + data: Data[T][U] & {facetId: U; element: HTMLElement} + ) { + registerFacet(store, facetType, data); + }, + + getFacetElements() { + return getFacetElements(store); + }, + waitUntilAppLoaded(callback: () => void) { - if (!store.state.loadingFlags.length) { - callback(); - } else { - store.onChange('loadingFlags', (flags) => { - if (!flags.length) { - callback(); - } - }); - } + waitUntilAppLoaded(store, callback); }, //This does not makes sense in the store... should just be regular functions @@ -152,13 +123,22 @@ export function createSearchStore(): SearchStore { return engine.state.search.response.searchUid; }, + hasLoadingFlag(loadingFlag: string) { + return store.state.loadingFlags.indexOf(loadingFlag) !== -1; + }, + + getAllFacets() { + return { + ...store.state.facets, + ...store.state.dateFacets, + ...store.state.categoryFacets, + ...store.state.numericFacets, + }; + }, + addFieldsToInclude(fields) { const currentFields = store.state.fieldsToInclude; store.state.fieldsToInclude = [...currentFields, ...fields]; }, - - hasLoadingFlag(loadingFlag: string) { - return store.state.loadingFlags.indexOf(loadingFlag) !== -1; - }, }; } From 1e7ed4d187aa9a9c8a42ba5b12a1c0444057cc2a Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Fri, 27 Dec 2024 10:18:34 -0500 Subject: [PATCH 9/9] extends from base store --- .../atomic-commerce-interface/store.ts | 10 +++++----- .../store.ts | 15 +++++--------- .../src/components/common/interface/store.ts | 20 +++++++++++++++++++ .../insight/atomic-insight-interface/store.ts | 10 +++++----- .../atomic-recs-interface/store.ts | 10 +++++----- .../search/atomic-search-interface/store.ts | 8 ++++---- .../atomic/src/utils/accessibility-utils.tsx | 3 --- 7 files changed, 44 insertions(+), 32 deletions(-) diff --git a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts index 57bc2d94d2c..5eb8255b6a5 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-interface/store.ts @@ -3,10 +3,10 @@ import { Selectors, ChildProduct, } from '@coveo/headless/commerce'; -import {createStore} from '@stencil/store'; import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; import { - CommonStore, + BaseStore, + createBaseStore, ResultListInfo, setLoadingFlag, unsetLoadingFlag, @@ -21,7 +21,7 @@ interface Data { activeProductChild: ChildProduct | undefined; } -export type CommerceStore = CommonStore & { +export type CommerceStore = BaseStore & { isAppLoaded(): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; @@ -32,13 +32,13 @@ export type CommerceStore = CommonStore & { export function createCommerceStore( type: 'search' | 'product-listing' ): CommerceStore { - const store = createStore({ + const store = createBaseStore({ loadingFlags: [], iconAssetsPath: '', resultList: undefined, mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT, activeProductChild: undefined, - }) as CommonStore; + }); return { ...store, diff --git a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts index 4438f3a6b05..7cb126903ad 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-recommendation-interface/store.ts @@ -1,6 +1,6 @@ -import {createStore} from '@stencil/store'; import { - CommonStore, + BaseStore, + createBaseStore, ResultListInfo, setLoadingFlag, unsetLoadingFlag, @@ -12,19 +12,18 @@ interface Data { resultList: ResultListInfo | undefined; } -export type CommerceRecommendationStore = CommonStore & { +export type CommerceRecommendationStore = BaseStore & { isAppLoaded(): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; - // getUniqueIDFromEngine(engine: CommerceEngine): string; }; export function createCommerceRecommendationStore(): CommerceRecommendationStore { - const store = createStore({ + const store = createBaseStore({ loadingFlags: [], iconAssetsPath: '', resultList: undefined, - }) as CommonStore; + }); return { ...store, @@ -40,9 +39,5 @@ export function createCommerceRecommendationStore(): CommerceRecommendationStore setLoadingFlag(loadingFlag: string) { setLoadingFlag(store, loadingFlag); }, - - // getUniqueIDFromEngine(engine: CommerceEngine): string { - // return null; - // }, }; } diff --git a/packages/atomic/src/components/common/interface/store.ts b/packages/atomic/src/components/common/interface/store.ts index 239768afcdb..433143d6549 100644 --- a/packages/atomic/src/components/common/interface/store.ts +++ b/packages/atomic/src/components/common/interface/store.ts @@ -1,4 +1,5 @@ import {DateFacetValue, NumericFacetValue} from '@coveo/headless'; +import {createStore} from '@stencil/store'; import {isInDocument} from '../../../utils/utils'; import { FacetInfo, @@ -6,6 +7,7 @@ import { FacetType, FacetValueFormat, } from '../facets/facet-common-store'; +import {AnyEngineType} from './bindings'; export interface CommonStore { state: StoreData; @@ -15,6 +17,24 @@ export interface CommonStore { ) => () => void; } +export type BaseStore = CommonStore & { + getUniqueIDFromEngine(engine: unknown): string | undefined; +}; + +export function createBaseStore(initialState: T): BaseStore { + const store = createStore(initialState) as CommonStore; + + return { + ...store, + + getUniqueIDFromEngine(_engine: AnyEngineType) { + throw new Error( + 'getUniqueIDFromEngine not implemented at the base store level.' + ); + }, + }; +} + export interface ResultListInfo { focusOnNextNewResult(): void; focusOnFirstResultAfterNextSearch(): Promise; diff --git a/packages/atomic/src/components/insight/atomic-insight-interface/store.ts b/packages/atomic/src/components/insight/atomic-insight-interface/store.ts index 3e8e7b63ac8..936a511f2d3 100644 --- a/packages/atomic/src/components/insight/atomic-insight-interface/store.ts +++ b/packages/atomic/src/components/insight/atomic-insight-interface/store.ts @@ -1,5 +1,4 @@ import {InsightEngine} from '@coveo/headless/insight'; -import {createStore} from '@stencil/store'; import { FacetInfo, FacetStore, @@ -7,7 +6,8 @@ import { FacetValueFormat, } from '../../common/facets/facet-common-store'; import { - CommonStore, + BaseStore, + createBaseStore, getFacetElements, registerFacet, ResultListInfo, @@ -29,7 +29,7 @@ interface Data { fieldsToInclude: string[]; } -export type InsightStore = CommonStore & { +export type InsightStore = BaseStore & { isAppLoaded(): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; @@ -43,7 +43,7 @@ export type InsightStore = CommonStore & { }; export function createInsightStore(): InsightStore { - const store = createStore({ + const store = createBaseStore({ loadingFlags: [], iconAssetsPath: '', resultList: undefined, @@ -53,7 +53,7 @@ export function createInsightStore(): InsightStore { categoryFacets: {}, facetElements: [], fieldsToInclude: [], - }) as CommonStore; + }); return { ...store, diff --git a/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts b/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts index 267b14d5977..1e419fb4ce3 100644 --- a/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts +++ b/packages/atomic/src/components/recommendations/atomic-recs-interface/store.ts @@ -1,6 +1,6 @@ -import {createStore} from '@stencil/store'; import { - CommonStore, + BaseStore, + createBaseStore, ResultListInfo, setLoadingFlag, unsetLoadingFlag, @@ -12,18 +12,18 @@ interface Data { resultList: ResultListInfo | undefined; } -export type RecsStore = CommonStore & { +export type RecsStore = BaseStore & { isAppLoaded(): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; }; export function createRecsStore(): RecsStore { - const store = createStore({ + const store = createBaseStore({ loadingFlags: [], iconAssetsPath: '', resultList: undefined, - }) as CommonStore; + }); return { ...store, diff --git a/packages/atomic/src/components/search/atomic-search-interface/store.ts b/packages/atomic/src/components/search/atomic-search-interface/store.ts index cfe0b1ac24a..e42f712a68c 100644 --- a/packages/atomic/src/components/search/atomic-search-interface/store.ts +++ b/packages/atomic/src/components/search/atomic-search-interface/store.ts @@ -4,7 +4,6 @@ import { SortCriterion, SearchEngine, } from '@coveo/headless'; -import {createStore} from '@stencil/store'; import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint'; import { FacetInfo, @@ -13,7 +12,9 @@ import { FacetValueFormat, } from '../../common/facets/facet-common-store'; import { + BaseStore, CommonStore, + createBaseStore, getFacetElements, registerFacet, ResultListInfo, @@ -51,7 +52,7 @@ interface Data { sortOptions: SortDropdownOption[]; } -export type SearchStore = CommonStore & { +export type SearchStore = BaseStore & { isAppLoaded(): boolean; unsetLoadingFlag(loadingFlag: string): void; setLoadingFlag(flag: string): void; @@ -69,7 +70,7 @@ export type SearchStore = CommonStore & { }; export function createSearchStore(): SearchStore { - const store = createStore({ + const store = createBaseStore({ loadingFlags: [], iconAssetsPath: '', resultList: undefined, @@ -118,7 +119,6 @@ export function createSearchStore(): SearchStore { waitUntilAppLoaded(store, callback); }, - //This does not makes sense in the store... should just be regular functions getUniqueIDFromEngine(engine: SearchEngine): string { return engine.state.search.response.searchUid; }, diff --git a/packages/atomic/src/utils/accessibility-utils.tsx b/packages/atomic/src/utils/accessibility-utils.tsx index a4df742a25d..45b7807d8fa 100644 --- a/packages/atomic/src/utils/accessibility-utils.tsx +++ b/packages/atomic/src/utils/accessibility-utils.tsx @@ -76,7 +76,6 @@ export class FocusTargetController { } public focusAfterSearch() { - //@ts-expect-error normal for now, should go away this.lastSearchId = this.bindings.store.getUniqueIDFromEngine( this.bindings.engine ); @@ -91,7 +90,6 @@ export class FocusTargetController { public disableForCurrentSearch() { if ( - //@ts-expect-error to fix this.bindings.store.getUniqueIDFromEngine(this.bindings.engine) !== this.lastSearchId ) { @@ -110,7 +108,6 @@ export class FocusTargetController { } if ( this.doFocusAfterSearch && - //@ts-expect-error to fix this.bindings.store.getUniqueIDFromEngine(this.bindings.engine) !== this.lastSearchId ) {