From a8de28ce4ee15631c11171f4a8f9d66b96b67199 Mon Sep 17 00:00:00 2001 From: Felix Perron-Brault Date: Tue, 21 Oct 2025 10:34:17 -0400 Subject: [PATCH 1/4] migrate item-list https://coveord.atlassian.net/browse/KIT-5052 --- .../atomic-commerce-product-list.ts | 158 +++++++++--------- .../common/item-list/item-list.spec.ts | 76 +++++++++ .../components/common/item-list/item-list.ts | 22 +++ ...ard.tsx => stencil-item-display-guard.tsx} | 0 ...-guard.tsx => stencil-item-list-guard.tsx} | 0 .../atomic-insight-folded-result-list.tsx | 4 +- .../atomic-insight-result-list.tsx | 4 +- .../atomic-recs-list/atomic-ipx-recs-list.tsx | 2 +- .../atomic-recs-list/atomic-recs-list.tsx | 2 +- .../atomic-folded-result-list.tsx | 4 +- .../atomic-result-list/atomic-result-list.tsx | 4 +- 11 files changed, 186 insertions(+), 90 deletions(-) create mode 100644 packages/atomic/src/components/common/item-list/item-list.spec.ts create mode 100644 packages/atomic/src/components/common/item-list/item-list.ts rename packages/atomic/src/components/common/item-list/{item-display-guard.tsx => stencil-item-display-guard.tsx} (100%) rename packages/atomic/src/components/common/item-list/{item-list-guard.tsx => stencil-item-list-guard.tsx} (100%) diff --git a/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts b/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts index 525e4090984..c626b3ee2c5 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts @@ -16,41 +16,42 @@ import {keyed} from 'lit/directives/keyed.js'; import {map} from 'lit/directives/map.js'; import {ref} from 'lit/directives/ref.js'; import {when} from 'lit/directives/when.js'; -import {bindStateToController} from '@/src/decorators/bind-state.js'; -import {bindingGuard} from '@/src/decorators/binding-guard.js'; -import {bindings} from '@/src/decorators/bindings.js'; -import {errorGuard} from '@/src/decorators/error-guard.js'; -import type {InitializableComponent} from '@/src/decorators/types.js'; -import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; -import {ChildrenUpdateCompleteMixin} from '@/src/mixins/children-update-complete-mixin.js'; -import {FocusTargetController} from '@/src/utils/accessibility-utils.js'; -import {randomID} from '@/src/utils/utils.js'; -import {renderItemPlaceholders} from '../../common/atomic-result-placeholder/item-placeholders.js'; -import {createAppLoadedListener} from '../../common/interface/store.js'; -import {renderDisplayWrapper} from '../../common/item-list/display-wrapper.js'; -import {renderGridLayout} from '../../common/item-list/grid-layout.js'; +import type {CommerceBindings} from '@/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.js'; +import type {SelectChildProductEventArgs} from '@/src/components/commerce/atomic-product-children/select-child-product-event.js'; +import {ProductTemplateProvider} from '@/src/components/commerce/product-list/product-template-provider.js'; +import {renderItemPlaceholders} from '@/src/components/common/atomic-result-placeholder/item-placeholders.js'; +import {createAppLoadedListener} from '@/src/components/common/interface/store.js'; +import {renderDisplayWrapper} from '@/src/components/common/item-list/display-wrapper.js'; +import {renderGridLayout} from '@/src/components/common/item-list/grid-layout.js'; +import {renderItemList} from '@/src/components/common/item-list/item-list.js'; import { ItemListCommon, type ItemRenderingFunction, -} from '../../common/item-list/item-list-common.js'; -import gridDisplayStyles from '../../common/item-list/styles/grid-display.tw.css'; -import listDisplayStyles from '../../common/item-list/styles/list-display.tw.css'; -import placeholderStyles from '../../common/item-list/styles/placeholders.tw.css'; -import tableDisplayStyles from '../../common/item-list/styles/table-display.tw.css'; +} from '@/src/components/common/item-list/item-list-common.js'; +import gridDisplayStyles from '@/src/components/common/item-list/styles/grid-display.tw.css'; +import listDisplayStyles from '@/src/components/common/item-list/styles/list-display.tw.css'; +import placeholderStyles from '@/src/components/common/item-list/styles/placeholders.tw.css'; +import tableDisplayStyles from '@/src/components/common/item-list/styles/table-display.tw.css'; import { renderTableData, renderTableLayout, renderTableRow, -} from '../../common/item-list/table-layout.js'; +} from '@/src/components/common/item-list/table-layout.js'; import { getItemListDisplayClasses, type ItemDisplayDensity, type ItemDisplayImageSize, type ItemDisplayLayout, -} from '../../common/layout/display-options.js'; -import type {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface.js'; -import type {SelectChildProductEventArgs} from '../atomic-product-children/select-child-product-event.js'; -import {ProductTemplateProvider} from '../product-list/product-template-provider.js'; +} from '@/src/components/common/layout/display-options.js'; +import {bindStateToController} from '@/src/decorators/bind-state.js'; +import {bindingGuard} from '@/src/decorators/binding-guard.js'; +import {bindings} from '@/src/decorators/bindings.js'; +import {errorGuard} from '@/src/decorators/error-guard.js'; +import type {InitializableComponent} from '@/src/decorators/types.js'; +import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; +import {ChildrenUpdateCompleteMixin} from '@/src/mixins/children-update-complete-mixin.js'; +import {FocusTargetController} from '@/src/utils/accessibility-utils.js'; +import {randomID} from '@/src/utils/utils.js'; /** * The `atomic-commerce-product-list` component is responsible for displaying products. @@ -250,55 +251,60 @@ export class AtomicCommerceProductList @bindingGuard() @errorGuard() render() { - return html`${when( - this.shouldRender, - () => - html`${when( - this.templateHasError, - () => html``, - () => { - const listClasses = this.computeListDisplayClasses(); - const productClasses = `${listClasses} ${!this.isEveryProductReady && 'hidden'}`; - - // Products must be rendered immediately (though hidden) to start their initialization and loading processes. - // If we wait to render products until placeholders are removed, the components won't begin loading until then, - // causing a longer delay. The `isEveryProductsReady` flag hides products while preserving placeholders, - // then removes placeholders once products are fully loaded to prevent content flash. - return html` - ${when(this.isAppLoaded, () => - renderDisplayWrapper({ - props: {listClasses: productClasses, display: this.display}, - })( - html`${when( - this.display === 'grid', - () => this.renderGrid(), - () => - html`${when( - this.display === 'list', - () => this.renderList(), - () => this.renderTable() - )}` - )}` - ) - )} - ${when(!this.isEveryProductReady, () => - renderDisplayWrapper({ - props: {listClasses, display: this.display}, - })( - renderItemPlaceholders({ - props: { - density: this.density, - display: this.display, - imageSize: this.imageSize, - numberOfPlaceholders: this.numberOfPlaceholders, - }, - }) - ) - )} - `; - } - )}`, - () => nothing + return html`${renderItemList({ + props: { + hasError: this.summaryState.hasError, + hasItems: this.summaryState.hasProducts, + hasTemplate: this.resultTemplateRegistered, + firstRequestExecuted: this.summaryState.firstRequestExecuted, + templateHasError: this.templateHasError, + }, + })( + html`${when( + this.templateHasError, + () => html``, + () => { + const listClasses = this.computeListDisplayClasses(); + const productClasses = `${listClasses} ${!this.isEveryProductReady && 'hidden'}`; + + // Products must be rendered immediately (though hidden) to start their initialization and loading processes. + // If we wait to render products until placeholders are removed, the components won't begin loading until then, + // causing a longer delay. The `isEveryProductsReady` flag hides products while preserving placeholders, + // then removes placeholders once products are fully loaded to prevent content flash. + return html` + ${when(this.isAppLoaded, () => + renderDisplayWrapper({ + props: {listClasses: productClasses, display: this.display}, + })( + html`${when( + this.display === 'grid', + () => this.renderGrid(), + () => + html`${when( + this.display === 'list', + () => this.renderList(), + () => this.renderTable() + )}` + )}` + ) + )} + ${when(!this.isEveryProductReady, () => + renderDisplayWrapper({ + props: {listClasses, display: this.display}, + })( + renderItemPlaceholders({ + props: { + density: this.density, + display: this.display, + imageSize: this.imageSize, + numberOfPlaceholders: this.numberOfPlaceholders, + }, + }) + ) + )} + `; + } + )}` )}`; } @@ -543,14 +549,6 @@ export class AtomicCommerceProductList } return this.nextNewResultTarget; } - - private get shouldRender() { - return ( - !this.summaryState.hasError && - this.resultTemplateRegistered && - (!this.summaryState.firstRequestExecuted || this.summaryState.hasProducts) - ); - } } declare global { diff --git a/packages/atomic/src/components/common/item-list/item-list.spec.ts b/packages/atomic/src/components/common/item-list/item-list.spec.ts new file mode 100644 index 00000000000..6c4f30724cd --- /dev/null +++ b/packages/atomic/src/components/common/item-list/item-list.spec.ts @@ -0,0 +1,76 @@ +import {html, nothing, render, type TemplateResult} from 'lit'; +import {describe, expect, it} from 'vitest'; +import {type ItemListProps, renderItemList} from './item-list'; + +describe('renderItemList', () => { + const renderItemListWithChildren = ( + props: Partial = {}, + children: TemplateResult | typeof nothing = nothing + ) => { + const container = document.createElement('div'); + document.body.appendChild(container); + + render( + renderItemList({ + props: { + hasError: false, + hasItems: true, + hasTemplate: true, + firstRequestExecuted: true, + templateHasError: false, + ...props, + }, + })(children), + container + ); + + return container; + }; + + const testChild = html`
Test Child
`; + + it.each([ + { + description: 'all conditions are met', + props: {}, + }, + { + description: '#firstRequestExecuted is false and #hasItems is true', + props: {firstRequestExecuted: false, hasItems: true}, + }, + { + description: '#firstRequestExecuted is false and #hasItems is false', + props: {firstRequestExecuted: false, hasItems: false}, + }, + ])('should render children when $description', ({props}) => { + const container = renderItemListWithChildren(props, testChild); + const child = container.querySelector('.child'); + + expect(child).toBeInTheDocument(); + expect(child?.textContent).toBe('Test Child'); + + container.remove(); + }); + + it.each([ + { + description: '#hasError is true', + props: {hasError: true}, + }, + { + description: '#hasTemplate is false', + props: {hasTemplate: false}, + }, + { + description: '#firstRequestExecuted is true and #hasItems is false', + props: {firstRequestExecuted: true, hasItems: false}, + }, + ])('should not render children when $description', ({props}) => { + const container = renderItemListWithChildren(props, testChild); + const child = container.querySelector('.child'); + + expect(child).not.toBeInTheDocument(); + + container.remove(); + }); +}); diff --git a/packages/atomic/src/components/common/item-list/item-list.ts b/packages/atomic/src/components/common/item-list/item-list.ts new file mode 100644 index 00000000000..1279ecdc3b4 --- /dev/null +++ b/packages/atomic/src/components/common/item-list/item-list.ts @@ -0,0 +1,22 @@ +import {nothing} from 'lit'; +import type {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils'; + +export interface ItemListProps { + hasError: boolean; + hasItems: boolean; + hasTemplate: boolean; + firstRequestExecuted: boolean; + templateHasError: boolean; +} + +export const renderItemList: FunctionalComponentWithChildren = + ({props}) => + (children) => { + const {hasError, hasItems, firstRequestExecuted, hasTemplate} = props; + + if (hasError || (firstRequestExecuted && !hasItems) || !hasTemplate) { + return nothing; + } + + return children; + }; diff --git a/packages/atomic/src/components/common/item-list/item-display-guard.tsx b/packages/atomic/src/components/common/item-list/stencil-item-display-guard.tsx similarity index 100% rename from packages/atomic/src/components/common/item-list/item-display-guard.tsx rename to packages/atomic/src/components/common/item-list/stencil-item-display-guard.tsx diff --git a/packages/atomic/src/components/common/item-list/item-list-guard.tsx b/packages/atomic/src/components/common/item-list/stencil-item-list-guard.tsx similarity index 100% rename from packages/atomic/src/components/common/item-list/item-list-guard.tsx rename to packages/atomic/src/components/common/item-list/stencil-item-list-guard.tsx diff --git a/packages/atomic/src/components/insight/result-lists/atomic-insight-folded-result-list/atomic-insight-folded-result-list.tsx b/packages/atomic/src/components/insight/result-lists/atomic-insight-folded-result-list/atomic-insight-folded-result-list.tsx index c63007df971..7514bb97526 100644 --- a/packages/atomic/src/components/insight/result-lists/atomic-insight-folded-result-list/atomic-insight-folded-result-list.tsx +++ b/packages/atomic/src/components/insight/result-lists/atomic-insight-folded-result-list/atomic-insight-folded-result-list.tsx @@ -27,9 +27,9 @@ import {randomID} from '../../../../utils/utils'; import {ResultsPlaceholdersGuard} from '../../../common/atomic-result-placeholder/stencil-placeholders'; import {extractUnfoldedItem} from '../../../common/item-list/unfolded-item'; import {createAppLoadedListener} from '../../../common/interface/store'; -import {ItemDisplayGuard} from '../../../common/item-list/item-display-guard'; +import {ItemDisplayGuard} from '../../../common/item-list/stencil-item-display-guard'; import {FoldedItemListStateContextEvent} from '../../../common/item-list/item-list-decorators'; -import {ItemListGuard} from '../../../common/item-list/item-list-guard'; +import {ItemListGuard} from '../../../common/item-list/stencil-item-list-guard'; import {ResultTemplateProvider} from '../../../common/item-list/result-template-provider'; import {DisplayWrapper} from '../../../common/item-list/stencil-display-wrapper'; import { diff --git a/packages/atomic/src/components/insight/result-lists/atomic-insight-result-list/atomic-insight-result-list.tsx b/packages/atomic/src/components/insight/result-lists/atomic-insight-result-list/atomic-insight-result-list.tsx index 3ee0dd0c167..42306782c86 100644 --- a/packages/atomic/src/components/insight/result-lists/atomic-insight-result-list/atomic-insight-result-list.tsx +++ b/packages/atomic/src/components/insight/result-lists/atomic-insight-result-list/atomic-insight-result-list.tsx @@ -18,8 +18,8 @@ import {FocusTargetController} from '../../../../utils/stencil-accessibility-uti import {randomID} from '../../../../utils/utils'; import {ResultsPlaceholdersGuard} from '../../../common/atomic-result-placeholder/stencil-placeholders'; import {createAppLoadedListener} from '../../../common/interface/store'; -import {ItemDisplayGuard} from '../../../common/item-list/item-display-guard'; -import {ItemListGuard} from '../../../common/item-list/item-list-guard'; +import {ItemDisplayGuard} from '../../../common/item-list/stencil-item-display-guard'; +import {ItemListGuard} from '../../../common/item-list/stencil-item-list-guard'; import {ResultTemplateProvider} from '../../../common/item-list/result-template-provider'; import {DisplayWrapper} from '../../../common/item-list/stencil-display-wrapper'; import { diff --git a/packages/atomic/src/components/ipx/atomic-ipx-recs-list/atomic-recs-list/atomic-ipx-recs-list.tsx b/packages/atomic/src/components/ipx/atomic-ipx-recs-list/atomic-recs-list/atomic-ipx-recs-list.tsx index 5db44ba8c89..ee0af8ce859 100644 --- a/packages/atomic/src/components/ipx/atomic-ipx-recs-list/atomic-recs-list/atomic-ipx-recs-list.tsx +++ b/packages/atomic/src/components/ipx/atomic-ipx-recs-list/atomic-recs-list/atomic-ipx-recs-list.tsx @@ -30,7 +30,7 @@ import {FocusTargetController} from '../../../../utils/stencil-accessibility-uti import {randomID} from '../../../../utils/utils'; import {ResultsPlaceholdersGuard} from '../../../common/atomic-result-placeholder/stencil-placeholders'; import {createAppLoadedListener} from '../../../common/interface/store'; -import {ItemDisplayGuard} from '../../../common/item-list/item-display-guard'; +import {ItemDisplayGuard} from '../../../common/item-list/stencil-item-display-guard'; import {ResultTemplateProvider} from '../../../common/item-list/result-template-provider'; import {DisplayGrid} from '../../../common/item-list/stencil-display-grid'; import {DisplayWrapper} from '../../../common/item-list/stencil-display-wrapper'; diff --git a/packages/atomic/src/components/recommendations/atomic-recs-list/atomic-recs-list.tsx b/packages/atomic/src/components/recommendations/atomic-recs-list/atomic-recs-list.tsx index 3d6ef7add62..e6a39cf8787 100644 --- a/packages/atomic/src/components/recommendations/atomic-recs-list/atomic-recs-list.tsx +++ b/packages/atomic/src/components/recommendations/atomic-recs-list/atomic-recs-list.tsx @@ -26,7 +26,7 @@ import {FocusTargetController} from '../../../utils/stencil-accessibility-utils' import {randomID} from '../../../utils/utils'; import {ResultsPlaceholdersGuard} from '../../common/atomic-result-placeholder/stencil-placeholders'; import {createAppLoadedListener} from '../../common/interface/store'; -import {ItemDisplayGuard} from '../../common/item-list/item-display-guard'; +import {ItemDisplayGuard} from '../../common/item-list/stencil-item-display-guard'; import {ResultTemplateProvider} from '../../common/item-list/result-template-provider'; import {DisplayGrid} from '../../common/item-list/stencil-display-grid'; import {DisplayWrapper} from '../../common/item-list/stencil-display-wrapper'; diff --git a/packages/atomic/src/components/search/result-lists/atomic-folded-result-list/atomic-folded-result-list.tsx b/packages/atomic/src/components/search/result-lists/atomic-folded-result-list/atomic-folded-result-list.tsx index b6bdd6f2435..edff31ce679 100644 --- a/packages/atomic/src/components/search/result-lists/atomic-folded-result-list/atomic-folded-result-list.tsx +++ b/packages/atomic/src/components/search/result-lists/atomic-folded-result-list/atomic-folded-result-list.tsx @@ -35,8 +35,8 @@ import {randomID} from '../../../../utils/utils'; import {ResultsPlaceholdersGuard} from '../../../common/atomic-result-placeholder/stencil-placeholders'; import {extractUnfoldedItem} from '../../../common/item-list/unfolded-item'; import {createAppLoadedListener} from '../../../common/interface/store'; -import {ItemDisplayGuard} from '../../../common/item-list/item-display-guard'; -import {ItemListGuard} from '../../../common/item-list/item-list-guard'; +import {ItemDisplayGuard} from '../../../common/item-list/stencil-item-display-guard'; +import {ItemListGuard} from '../../../common/item-list/stencil-item-list-guard'; import {ResultTemplateProvider} from '../../../common/item-list/result-template-provider'; import {DisplayWrapper} from '../../../common/item-list/stencil-display-wrapper'; import { diff --git a/packages/atomic/src/components/search/result-lists/atomic-result-list/atomic-result-list.tsx b/packages/atomic/src/components/search/result-lists/atomic-result-list/atomic-result-list.tsx index d7b598f86ab..dabbe3ac66f 100644 --- a/packages/atomic/src/components/search/result-lists/atomic-result-list/atomic-result-list.tsx +++ b/packages/atomic/src/components/search/result-lists/atomic-result-list/atomic-result-list.tsx @@ -22,8 +22,8 @@ import {FocusTargetController} from '../../../../utils/stencil-accessibility-uti import {randomID} from '../../../../utils/utils'; import {ResultsPlaceholdersGuard} from '../../../common/atomic-result-placeholder/stencil-placeholders'; import {createAppLoadedListener} from '../../../common/interface/store'; -import {ItemDisplayGuard} from '../../../common/item-list/item-display-guard'; -import {ItemListGuard} from '../../../common/item-list/item-list-guard'; +import {ItemDisplayGuard} from '../../../common/item-list/stencil-item-display-guard'; +import {ItemListGuard} from '../../../common/item-list/stencil-item-list-guard'; import {ResultTemplateProvider} from '../../../common/item-list/result-template-provider'; import {DisplayGrid} from '../../../common/item-list/stencil-display-grid'; import { From 4beffa91cd442f5c96400a39491730d2e7f16b83 Mon Sep 17 00:00:00 2001 From: Felix Perron-Brault Date: Wed, 22 Oct 2025 10:28:19 -0400 Subject: [PATCH 2/4] cleanup https://coveord.atlassian.net/browse/KIT-5052 --- .../atomic-commerce-product-list.ts | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts b/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts index c626b3ee2c5..43afd6d1e15 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts @@ -11,23 +11,23 @@ import { type Summary, } from '@coveo/headless/commerce'; import {type CSSResultGroup, css, html, LitElement, nothing} from 'lit'; -import {customElement, property, state} from 'lit/decorators.js'; -import {keyed} from 'lit/directives/keyed.js'; -import {map} from 'lit/directives/map.js'; -import {ref} from 'lit/directives/ref.js'; -import {when} from 'lit/directives/when.js'; -import type {CommerceBindings} from '@/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface.js'; -import type {SelectChildProductEventArgs} from '@/src/components/commerce/atomic-product-children/select-child-product-event.js'; -import {ProductTemplateProvider} from '@/src/components/commerce/product-list/product-template-provider.js'; -import {renderItemPlaceholders} from '@/src/components/common/atomic-result-placeholder/item-placeholders.js'; -import {createAppLoadedListener} from '@/src/components/common/interface/store.js'; -import {renderDisplayWrapper} from '@/src/components/common/item-list/display-wrapper.js'; -import {renderGridLayout} from '@/src/components/common/item-list/grid-layout.js'; -import {renderItemList} from '@/src/components/common/item-list/item-list.js'; +import {customElement, property, state} from 'lit/decorators'; +import {keyed} from 'lit/directives/keyed'; +import {map} from 'lit/directives/map'; +import {ref} from 'lit/directives/ref'; +import {when} from 'lit/directives/when'; +import type {CommerceBindings} from '@/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface'; +import type {SelectChildProductEventArgs} from '@/src/components/commerce/atomic-product-children/select-child-product-event'; +import {ProductTemplateProvider} from '@/src/components/commerce/product-list/product-template-provider'; +import {renderItemPlaceholders} from '@/src/components/common/atomic-result-placeholder/item-placeholders'; +import {createAppLoadedListener} from '@/src/components/common/interface/store'; +import {renderDisplayWrapper} from '@/src/components/common/item-list/display-wrapper'; +import {renderGridLayout} from '@/src/components/common/item-list/grid-layout'; +import {renderItemList} from '@/src/components/common/item-list/item-list'; import { ItemListCommon, type ItemRenderingFunction, -} from '@/src/components/common/item-list/item-list-common.js'; +} from '@/src/components/common/item-list/item-list-common'; import gridDisplayStyles from '@/src/components/common/item-list/styles/grid-display.tw.css'; import listDisplayStyles from '@/src/components/common/item-list/styles/list-display.tw.css'; import placeholderStyles from '@/src/components/common/item-list/styles/placeholders.tw.css'; @@ -36,22 +36,22 @@ import { renderTableData, renderTableLayout, renderTableRow, -} from '@/src/components/common/item-list/table-layout.js'; +} from '@/src/components/common/item-list/table-layout'; import { getItemListDisplayClasses, type ItemDisplayDensity, type ItemDisplayImageSize, type ItemDisplayLayout, -} from '@/src/components/common/layout/display-options.js'; -import {bindStateToController} from '@/src/decorators/bind-state.js'; -import {bindingGuard} from '@/src/decorators/binding-guard.js'; -import {bindings} from '@/src/decorators/bindings.js'; -import {errorGuard} from '@/src/decorators/error-guard.js'; -import type {InitializableComponent} from '@/src/decorators/types.js'; -import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; -import {ChildrenUpdateCompleteMixin} from '@/src/mixins/children-update-complete-mixin.js'; -import {FocusTargetController} from '@/src/utils/accessibility-utils.js'; -import {randomID} from '@/src/utils/utils.js'; +} from '@/src/components/common/layout/display-options'; +import {bindStateToController} from '@/src/decorators/bind-state'; +import {bindingGuard} from '@/src/decorators/binding-guard'; +import {bindings} from '@/src/decorators/bindings'; +import {errorGuard} from '@/src/decorators/error-guard'; +import type {InitializableComponent} from '@/src/decorators/types'; +import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles'; +import {ChildrenUpdateCompleteMixin} from '@/src/mixins/children-update-complete-mixin'; +import {FocusTargetController} from '@/src/utils/accessibility-utils'; +import {randomID} from '@/src/utils/utils'; /** * The `atomic-commerce-product-list` component is responsible for displaying products. From ee3250f511d64a05ad02449b79e45554f1f05cf4 Mon Sep 17 00:00:00 2001 From: Felix Perron-Brault Date: Wed, 22 Oct 2025 15:34:45 -0400 Subject: [PATCH 3/4] fix build https://coveord.atlassian.net/browse/KIT-5052 --- .../atomic-commerce-product-list.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts b/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts index 43afd6d1e15..21dbb49e6dc 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts +++ b/packages/atomic/src/components/commerce/atomic-commerce-product-list/atomic-commerce-product-list.ts @@ -11,11 +11,11 @@ import { type Summary, } from '@coveo/headless/commerce'; import {type CSSResultGroup, css, html, LitElement, nothing} from 'lit'; -import {customElement, property, state} from 'lit/decorators'; -import {keyed} from 'lit/directives/keyed'; -import {map} from 'lit/directives/map'; -import {ref} from 'lit/directives/ref'; -import {when} from 'lit/directives/when'; +import {customElement, property, state} from 'lit/decorators.js'; +import {keyed} from 'lit/directives/keyed.js'; +import {map} from 'lit/directives/map.js'; +import {ref} from 'lit/directives/ref.js'; +import {when} from 'lit/directives/when.js'; import type {CommerceBindings} from '@/src/components/commerce/atomic-commerce-interface/atomic-commerce-interface'; import type {SelectChildProductEventArgs} from '@/src/components/commerce/atomic-product-children/select-child-product-event'; import {ProductTemplateProvider} from '@/src/components/commerce/product-list/product-template-provider'; From be3d6b900a99c893f9f71ef2b39ca349009006b6 Mon Sep 17 00:00:00 2001 From: Felix Perron-Brault Date: Thu, 23 Oct 2025 10:00:58 -0400 Subject: [PATCH 4/4] fix tests https://coveord.atlassian.net/browse/KIT-5052 --- .../common/item-list/table-layout.spec.ts | 23 +++++++++++++++++++ .../common/item-list/table-layout.ts | 8 +++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/atomic/src/components/common/item-list/table-layout.spec.ts b/packages/atomic/src/components/common/item-list/table-layout.spec.ts index 08df2449499..7322b1b59a8 100644 --- a/packages/atomic/src/components/common/item-list/table-layout.spec.ts +++ b/packages/atomic/src/components/common/item-list/table-layout.spec.ts @@ -488,5 +488,28 @@ describe('renderTableData', () => { expect(renderItem).toHaveBeenNthCalledWith(2, tableElement2); }); }); + + describe('when #templateContentForFirstItem is null', () => { + it('should not render any td elements', async () => { + const tableData = await tableDataFixture({ + templateContentForFirstItem: null as unknown as DocumentFragment, + }); + + const tdElements = tableData.querySelectorAll('td'); + + expect(tdElements.length).toBe(0); + }); + + it('should not call #renderItem', async () => { + const renderItem = vi.fn(); + + await tableDataFixture({ + templateContentForFirstItem: null as unknown as DocumentFragment, + renderItem, + }); + + expect(renderItem).not.toHaveBeenCalled(); + }); + }); }); }); diff --git a/packages/atomic/src/components/common/item-list/table-layout.ts b/packages/atomic/src/components/common/item-list/table-layout.ts index 455b51784c1..b52caa7bdff 100644 --- a/packages/atomic/src/components/common/item-list/table-layout.ts +++ b/packages/atomic/src/components/common/item-list/table-layout.ts @@ -145,7 +145,11 @@ const getFieldTableColumnsFromRenderingFunction = ( const getFieldTableColumnsFromHTMLTemplate = ( props: Pick -): Element[] => - Array.from( +): Element[] => { + if (!props.templateContentForFirstItem) { + return []; + } + return Array.from( props.templateContentForFirstItem.querySelectorAll(tableElementTagName) ); +};