Skip to content

Commit

Permalink
refactor(atomic): split the atomic store into composable parts (#4806)
Browse files Browse the repository at this point in the history
https://coveord.atlassian.net/browse/KIT-3814

The atomic store was messy. The common store was way too big. There was
no need for all that stuff in every store.
  • Loading branch information
alexprudhomme authored Jan 15, 2025
1 parent e3801cc commit de2a920
Show file tree
Hide file tree
Showing 29 changed files with 444 additions and 408 deletions.
32 changes: 18 additions & 14 deletions packages/atomic/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,20 @@ 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";
import { TruncateAfter } from "./components/common/expandable-text/expandable-text";
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";
Expand All @@ -55,18 +57,20 @@ 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";
export { TruncateAfter } from "./components/common/expandable-text/expandable-text";
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 {
Expand Down Expand Up @@ -1417,7 +1421,7 @@ export namespace Components {
/**
* Global Atomic state.
*/
"store"?: AtomicInsightStore;
"store"?: InsightStore;
}
interface AtomicInsightResultAction {
/**
Expand Down Expand Up @@ -2060,7 +2064,7 @@ export namespace Components {
* Global Atomic state.
* @alpha
*/
"store"?: AtomicCommonStore<AtomicCommonStoreData>;
"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).
Expand Down Expand Up @@ -2704,7 +2708,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.
Expand Down Expand Up @@ -2826,7 +2830,7 @@ export namespace Components {
/**
* Global Atomic state.
*/
"store"?: AtomicCommonStore<AtomicCommonStoreData>;
"store"?: SearchStore;
}
/**
* The `atomic-result-badge` element renders a badge to highlight special features of a result.
Expand Down Expand Up @@ -7595,7 +7599,7 @@ declare namespace LocalJSX {
/**
* Global Atomic state.
*/
"store"?: AtomicInsightStore;
"store"?: InsightStore;
}
interface AtomicInsightResultAction {
/**
Expand Down Expand Up @@ -8213,7 +8217,7 @@ declare namespace LocalJSX {
* Global Atomic state.
* @alpha
*/
"store"?: AtomicCommonStore<AtomicCommonStoreData>;
"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).
Expand Down Expand Up @@ -8817,7 +8821,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.
Expand Down Expand Up @@ -8936,7 +8940,7 @@ declare namespace LocalJSX {
/**
* Global Atomic state.
*/
"store"?: AtomicCommonStore<AtomicCommonStoreData>;
"store"?: SearchStore;
}
/**
* The `atomic-result-badge` element renders a badge to highlight special features of a result.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<CommerceEngine>;

@Element() public host!: HTMLAtomicCommerceInterfaceElement;
Expand Down Expand Up @@ -178,7 +178,7 @@ export class AtomicCommerceInterface
this,
'CoveoAtomic'
);
this.store = createAtomicCommerceStore(this.type);
this.store = createCommerceStore(this.type);
}

public connectedCallback() {
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,78 +1,63 @@
import {
CommerceEngine,
Selectors,
NumericFacetValue,
DateFacetValue,
SortCriterion,
ChildProduct,
} from '@coveo/headless/commerce';
import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint';
import {
FacetInfo,
FacetStore,
FacetValueFormat,
} from '../../common/facets/facet-common-store';
import {
createAtomicCommonStore,
AtomicCommonStoreData,
AtomicCommonStore,
BaseStore,
createBaseStore,
ResultListInfo,
setLoadingFlag,
unsetLoadingFlag,
} 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<FacetInfo>;
numericFacets: FacetStore<FacetInfo & FacetValueFormat<NumericFacetValue>>;
dateFacets: FacetStore<FacetInfo & FacetValueFormat<DateFacetValue>>;
categoryFacets: FacetStore<FacetInfo>;
sortOptions: SortDropdownOption[];
interface Data {
loadingFlags: string[];
iconAssetsPath: string;
resultList: ResultListInfo | undefined;
mobileBreakpoint: string;
currentQuickviewPosition: number;
activeProductChild: ChildProduct | undefined;
}

export interface AtomicCommerceStore
extends AtomicCommonStore<AtomicStoreData> {
export type CommerceStore = BaseStore<Data> & {
isAppLoaded(): boolean;
unsetLoadingFlag(loadingFlag: string): void;
setLoadingFlag(flag: string): void;
isMobile(): boolean;
}

export interface FacetInfoMap {
[facetId: string]:
| FacetInfo
| (FacetInfo & FacetValueFormat<NumericFacetValue>)
| (FacetInfo & FacetValueFormat<DateFacetValue>);
}
getUniqueIDFromEngine(engine: CommerceEngine): string;
};

export function createAtomicCommerceStore(
export function createCommerceStore(
type: 'search' | 'product-listing'
): AtomicCommerceStore {
const commonStore = createAtomicCommonStore<AtomicStoreData>({
): CommerceStore {
const store = createBaseStore({
loadingFlags: [],
facets: {},
numericFacets: {},
dateFacets: {},
categoryFacets: {},
facetElements: [],
sortOptions: [],
iconAssetsPath: '',
resultList: undefined,
mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT,
fieldsToInclude: [],
currentQuickviewPosition: -1,
activeProductChild: undefined,
});

return {
...commonStore,
...store,

isAppLoaded() {
return !store.state.loadingFlags.length;
},

unsetLoadingFlag(loadingFlag: string) {
unsetLoadingFlag(store, loadingFlag);
},

setLoadingFlag(loadingFlag: string) {
setLoadingFlag(store, loadingFlag);
},

isMobile() {
return !window.matchMedia(
makeDesktopQuery(commonStore.state.mobileBreakpoint)
).matches;
return !window.matchMedia(makeDesktopQuery(store.state.mobileBreakpoint))
.matches;
},

getUniqueIDFromEngine(engine: CommerceEngine): string {
Expand All @@ -81,10 +66,6 @@ export function createAtomicCommerceStore(
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}`
);
}
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -48,7 +48,7 @@ export type CommerceBindings = CommonBindings<
export class AtomicCommerceRecommendationInterface
implements BaseAtomicInterface<CommerceEngine>
{
private store = createAtomicCommerceRecommendationStore();
private store = createCommerceRecommendationStore();
private commonInterfaceHelper: CommonAtomicInterfaceHelper<CommerceEngine>;

@Element() public host!: HTMLAtomicCommerceInterfaceElement;
Expand Down Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,43 @@
import {ChildProduct} from '@coveo/headless/commerce';
import {DEFAULT_MOBILE_BREAKPOINT} from '../../../utils/replace-breakpoint';
import {
AtomicCommonStore,
AtomicCommonStoreData,
createAtomicCommonStore,
BaseStore,
createBaseStore,
ResultListInfo,
setLoadingFlag,
unsetLoadingFlag,
} from '../../common/interface/store';
import {makeDesktopQuery} from '../atomic-commerce-layout/commerce-layout';

export interface AtomicStoreData extends AtomicCommonStoreData {
mobileBreakpoint: string;
currentQuickviewPosition: number;
activeProductChild: ChildProduct | undefined;
interface Data {
loadingFlags: string[];
iconAssetsPath: string;
resultList: ResultListInfo | undefined;
}

export interface AtomicCommerceRecommendationStore
extends AtomicCommonStore<AtomicStoreData> {
isMobile(): boolean;
}
export type CommerceRecommendationStore = BaseStore<Data> & {
isAppLoaded(): boolean;
unsetLoadingFlag(loadingFlag: string): void;
setLoadingFlag(flag: string): void;
};

export function createAtomicCommerceRecommendationStore(): AtomicCommerceRecommendationStore {
const commonStore = createAtomicCommonStore<AtomicStoreData>({
export function createCommerceRecommendationStore(): CommerceRecommendationStore {
const store = createBaseStore<Data>({
loadingFlags: [],
facets: {},
numericFacets: {},
dateFacets: {},
categoryFacets: {},
facetElements: [],
iconAssetsPath: '',
mobileBreakpoint: DEFAULT_MOBILE_BREAKPOINT,
fieldsToInclude: [],
currentQuickviewPosition: -1,
activeProductChild: undefined,
resultList: undefined,
});

return {
...commonStore,
...store,

isAppLoaded() {
return !store.state.loadingFlags.length;
},

unsetLoadingFlag(loadingFlag: string) {
unsetLoadingFlag(store, loadingFlag);
},

isMobile() {
return !window.matchMedia(
makeDesktopQuery(commonStore.state.mobileBreakpoint)
).matches;
setLoadingFlag(loadingFlag: string) {
setLoadingFlag(store, loadingFlag);
},
};
}
Loading

0 comments on commit de2a920

Please sign in to comment.