diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts index 5330e0cf113..df0711a55ce 100644 --- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts +++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/atomic-angular.module.ts @@ -5,6 +5,10 @@ import {APP_INITIALIZER, ModuleWithProviders, NgModule, Provider} from '@angular import { +AtomicCommerceSearchBox, +AtomicCommerceSearchBoxInstantProducts, +AtomicCommerceSearchBoxQuerySuggestions, +AtomicCommerceSearchBoxRecentQueries, AtomicComponentError, AtomicIcon, AtomicAriaLive, @@ -33,10 +37,6 @@ AtomicCommerceRecommendationInterface, AtomicCommerceRecommendationList, AtomicCommerceRefineModal, AtomicCommerceRefineToggle, -AtomicCommerceSearchBox, -AtomicCommerceSearchBoxInstantProducts, -AtomicCommerceSearchBoxQuerySuggestions, -AtomicCommerceSearchBoxRecentQueries, AtomicCommerceSortDropdown, AtomicCommerceText, AtomicCommerceTimeframeFacet, @@ -147,6 +147,10 @@ AtomicTimeframeFacet const DECLARATIONS = [ +AtomicCommerceSearchBox, +AtomicCommerceSearchBoxInstantProducts, +AtomicCommerceSearchBoxQuerySuggestions, +AtomicCommerceSearchBoxRecentQueries, AtomicComponentError, AtomicIcon, AtomicAriaLive, @@ -175,10 +179,6 @@ AtomicCommerceRecommendationInterface, AtomicCommerceRecommendationList, AtomicCommerceRefineModal, AtomicCommerceRefineToggle, -AtomicCommerceSearchBox, -AtomicCommerceSearchBoxInstantProducts, -AtomicCommerceSearchBoxQuerySuggestions, -AtomicCommerceSearchBoxRecentQueries, AtomicCommerceSortDropdown, AtomicCommerceText, AtomicCommerceTimeframeFacet, diff --git a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts index e800f0af137..4ca9cff210a 100644 --- a/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts +++ b/packages/atomic-angular/projects/atomic-angular/src/lib/stencil-generated/components.ts @@ -580,115 +580,6 @@ export class AtomicCommerceRefineToggle { export declare interface AtomicCommerceRefineToggle extends Components.AtomicCommerceRefineToggle {} -@ProxyCmp({ - inputs: ['clearFilters', 'disableSearch', 'minimumQueryLength', 'numberOfQueries', 'redirectionUrl', 'suggestionDelay', 'suggestionTimeout'] -, defineCustomElementFn: defineCustomElementAtomicCommerceSearchBox}) -@Component({standalone:false, - selector: 'atomic-commerce-search-box', - changeDetection: ChangeDetectionStrategy.OnPush, - template: '', - // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['clearFilters', 'disableSearch', 'minimumQueryLength', 'numberOfQueries', 'redirectionUrl', 'suggestionDelay', 'suggestionTimeout'], -}) -export class AtomicCommerceSearchBox { - protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { - c.detach(); - this.el = r.nativeElement; - proxyOutputs(this, this.el, ['redirect']); - } -} - - -import type { RedirectionPayload as IAtomicCommerceSearchBoxRedirectionPayload } from '@coveo/atomic'; - -export declare interface AtomicCommerceSearchBox extends Components.AtomicCommerceSearchBox { - /** - * Event that is emitted when a standalone search box redirection is triggered. By default, the search box will directly change the URL and redirect accordingly, so if you want to handle the redirection differently, use this event. - -Example: -```html - -... - -``` - */ - redirect: EventEmitter>; -} - - -@ProxyCmp({ - inputs: ['ariaLabelGenerator', 'density', 'imageSize'], - methods: ['setRenderFunction'] -, defineCustomElementFn: defineCustomElementAtomicCommerceSearchBoxInstantProducts}) -@Component({standalone:false, - selector: 'atomic-commerce-search-box-instant-products', - changeDetection: ChangeDetectionStrategy.OnPush, - template: '', - // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['ariaLabelGenerator', 'density', 'imageSize'], -}) -export class AtomicCommerceSearchBoxInstantProducts { - protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { - c.detach(); - this.el = r.nativeElement; - } -} - - -export declare interface AtomicCommerceSearchBoxInstantProducts extends Components.AtomicCommerceSearchBoxInstantProducts {} - - -@ProxyCmp({ - inputs: ['icon', 'maxWithQuery', 'maxWithoutQuery'] -, defineCustomElementFn: defineCustomElementAtomicCommerceSearchBoxQuerySuggestions}) -@Component({standalone:false, - selector: 'atomic-commerce-search-box-query-suggestions', - changeDetection: ChangeDetectionStrategy.OnPush, - template: '', - // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['icon', 'maxWithQuery', 'maxWithoutQuery'], -}) -export class AtomicCommerceSearchBoxQuerySuggestions { - protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { - c.detach(); - this.el = r.nativeElement; - } -} - - -export declare interface AtomicCommerceSearchBoxQuerySuggestions extends Components.AtomicCommerceSearchBoxQuerySuggestions {} - - -@ProxyCmp({ - inputs: ['icon', 'maxWithQuery', 'maxWithoutQuery'] -, defineCustomElementFn: defineCustomElementAtomicCommerceSearchBoxRecentQueries}) -@Component({standalone:false, - selector: 'atomic-commerce-search-box-recent-queries', - changeDetection: ChangeDetectionStrategy.OnPush, - template: '', - // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['icon', 'maxWithQuery', 'maxWithoutQuery'], -}) -export class AtomicCommerceSearchBoxRecentQueries { - protected el: HTMLElement; - constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { - c.detach(); - this.el = r.nativeElement; - } -} - - -export declare interface AtomicCommerceSearchBoxRecentQueries extends Components.AtomicCommerceSearchBoxRecentQueries {} - - @ProxyCmp({defineCustomElementFn: defineCustomElementAtomicCommerceSortDropdown}) @Component({standalone:false, selector: 'atomic-commerce-sort-dropdown', @@ -3028,6 +2919,110 @@ export declare interface AtomicTimeframeFacet extends Components.AtomicTimeframe //#region Lit Declarations +@ProxyCmp({ + inputs: ['numberOfQueries', 'redirectionUrl', 'suggestionTimeout', 'suggestionDelay', 'disableSearch', 'minimumQueryLength', 'clearFilters'], + methods: ['initialize', 'watchRedirectionUrl', 'initBindings'], + defineCustomElementFn: () => {customElements.get('atomic-commerce-search-box') || customElements.define('atomic-commerce-search-box', LitAtomicCommerceSearchBox);} +}) +@Component({ + selector: 'atomic-commerce-search-box', + standalone: false, + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: ['numberOfQueries', 'redirectionUrl', 'suggestionTimeout', 'suggestionDelay', 'disableSearch', 'minimumQueryLength', 'clearFilters'] +}) +export class AtomicCommerceSearchBox { + protected readonly el: HTMLElement; + constructor(c: ChangeDetectorRef, el: ElementRef, protected z: NgZone) { + c.detach(); + this.el = el.nativeElement; + proxyOutputs(this, this.el, ['redirect']); + } +} + +export declare interface AtomicCommerceSearchBox extends LitAtomicCommerceSearchBox { + 'redirect': EventEmitter>; +} + +@ProxyCmp({ + inputs: ['density', 'imageSize', 'ariaLabelGenerator'], + methods: ['setRenderFunction', 'initialize'], + defineCustomElementFn: () => {customElements.get('atomic-commerce-search-box-instant-products') || customElements.define('atomic-commerce-search-box-instant-products', LitAtomicCommerceSearchBoxInstantProducts);} +}) +@Component({ + selector: 'atomic-commerce-search-box-instant-products', + standalone: false, + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: ['density', 'imageSize', 'ariaLabelGenerator'] +}) +export class AtomicCommerceSearchBoxInstantProducts { + protected readonly el: HTMLElement; + constructor(c: ChangeDetectorRef, el: ElementRef, protected z: NgZone) { + c.detach(); + this.el = el.nativeElement; + + } +} + +export declare interface AtomicCommerceSearchBoxInstantProducts extends LitAtomicCommerceSearchBoxInstantProducts { + +} + +@ProxyCmp({ + inputs: ['icon', 'maxWithQuery', 'maxWithoutQuery'], + methods: ['initialize'], + defineCustomElementFn: () => {customElements.get('atomic-commerce-search-box-query-suggestions') || customElements.define('atomic-commerce-search-box-query-suggestions', LitAtomicCommerceSearchBoxQuerySuggestions);} +}) +@Component({ + selector: 'atomic-commerce-search-box-query-suggestions', + standalone: false, + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: ['icon', 'maxWithQuery', 'maxWithoutQuery'] +}) +export class AtomicCommerceSearchBoxQuerySuggestions { + protected readonly el: HTMLElement; + constructor(c: ChangeDetectorRef, el: ElementRef, protected z: NgZone) { + c.detach(); + this.el = el.nativeElement; + + } +} + +export declare interface AtomicCommerceSearchBoxQuerySuggestions extends LitAtomicCommerceSearchBoxQuerySuggestions { + +} + +@ProxyCmp({ + inputs: ['icon', 'maxWithQuery', 'maxWithoutQuery'], + methods: ['initialize'], + defineCustomElementFn: () => {customElements.get('atomic-commerce-search-box-recent-queries') || customElements.define('atomic-commerce-search-box-recent-queries', LitAtomicCommerceSearchBoxRecentQueries);} +}) +@Component({ + selector: 'atomic-commerce-search-box-recent-queries', + standalone: false, + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: ['icon', 'maxWithQuery', 'maxWithoutQuery'] +}) +export class AtomicCommerceSearchBoxRecentQueries { + protected readonly el: HTMLElement; + constructor(c: ChangeDetectorRef, el: ElementRef, protected z: NgZone) { + c.detach(); + this.el = el.nativeElement; + + } +} + +export declare interface AtomicCommerceSearchBoxRecentQueries extends LitAtomicCommerceSearchBoxRecentQueries { + +} + @ProxyCmp({ inputs: ['element', 'error'], methods: [], @@ -3080,7 +3075,7 @@ export declare interface AtomicIcon extends LitAtomicIcon { } -import {AtomicComponentError as LitAtomicComponentError, AtomicIcon as LitAtomicIcon} from '@coveo/atomic/components'; +import {AtomicCommerceSearchBox as LitAtomicCommerceSearchBox, AtomicCommerceSearchBoxInstantProducts as LitAtomicCommerceSearchBoxInstantProducts, AtomicCommerceSearchBoxQuerySuggestions as LitAtomicCommerceSearchBoxQuerySuggestions, AtomicCommerceSearchBoxRecentQueries as LitAtomicCommerceSearchBoxRecentQueries, AtomicComponentError as LitAtomicComponentError, AtomicIcon as LitAtomicIcon} from '@coveo/atomic/components'; -import {defineCustomElementAtomicAriaLive, defineCustomElementAtomicAutomaticFacet, defineCustomElementAtomicAutomaticFacetGenerator, defineCustomElementAtomicBreadbox, defineCustomElementAtomicCategoryFacet, defineCustomElementAtomicColorFacet, defineCustomElementAtomicCommerceBreadbox, defineCustomElementAtomicCommerceCategoryFacet, defineCustomElementAtomicCommerceDidYouMean, defineCustomElementAtomicCommerceFacet, defineCustomElementAtomicCommerceFacetNumberInput, defineCustomElementAtomicCommerceFacets, defineCustomElementAtomicCommerceInterface, defineCustomElementAtomicCommerceLayout, defineCustomElementAtomicCommerceLoadMoreProducts, defineCustomElementAtomicCommerceNoProducts, defineCustomElementAtomicCommerceNumericFacet, defineCustomElementAtomicCommercePager, defineCustomElementAtomicCommerceProductList, defineCustomElementAtomicCommerceProductsPerPage, defineCustomElementAtomicCommerceQueryError, defineCustomElementAtomicCommerceQuerySummary, defineCustomElementAtomicCommerceRecommendationInterface, defineCustomElementAtomicCommerceRecommendationList, defineCustomElementAtomicCommerceRefineModal, defineCustomElementAtomicCommerceRefineToggle, defineCustomElementAtomicCommerceSearchBox, defineCustomElementAtomicCommerceSearchBoxInstantProducts, defineCustomElementAtomicCommerceSearchBoxQuerySuggestions, defineCustomElementAtomicCommerceSearchBoxRecentQueries, defineCustomElementAtomicCommerceSortDropdown, defineCustomElementAtomicCommerceText, defineCustomElementAtomicCommerceTimeframeFacet, defineCustomElementAtomicDidYouMean, defineCustomElementAtomicExternal, defineCustomElementAtomicFacet, defineCustomElementAtomicFacetManager, defineCustomElementAtomicFieldCondition, defineCustomElementAtomicFoldedResultList, defineCustomElementAtomicFormatCurrency, defineCustomElementAtomicFormatNumber, defineCustomElementAtomicFormatUnit, defineCustomElementAtomicGeneratedAnswer, defineCustomElementAtomicHtml, defineCustomElementAtomicLayoutSection, defineCustomElementAtomicLoadMoreResults, defineCustomElementAtomicNoResults, defineCustomElementAtomicNotifications, defineCustomElementAtomicNumericFacet, defineCustomElementAtomicNumericRange, defineCustomElementAtomicPager, defineCustomElementAtomicPopover, defineCustomElementAtomicProduct, defineCustomElementAtomicProductChildren, defineCustomElementAtomicProductDescription, defineCustomElementAtomicProductExcerpt, defineCustomElementAtomicProductFieldCondition, defineCustomElementAtomicProductImage, defineCustomElementAtomicProductLink, defineCustomElementAtomicProductMultiValueText, defineCustomElementAtomicProductNumericFieldValue, defineCustomElementAtomicProductPrice, defineCustomElementAtomicProductRating, defineCustomElementAtomicProductSectionActions, defineCustomElementAtomicProductSectionBadges, defineCustomElementAtomicProductSectionBottomMetadata, defineCustomElementAtomicProductSectionChildren, defineCustomElementAtomicProductSectionDescription, defineCustomElementAtomicProductSectionEmphasized, defineCustomElementAtomicProductSectionMetadata, defineCustomElementAtomicProductSectionName, defineCustomElementAtomicProductSectionVisual, defineCustomElementAtomicProductTemplate, defineCustomElementAtomicProductText, defineCustomElementAtomicQueryError, defineCustomElementAtomicQuerySummary, defineCustomElementAtomicQuickview, defineCustomElementAtomicQuickviewModal, defineCustomElementAtomicRatingFacet, defineCustomElementAtomicRatingRangeFacet, defineCustomElementAtomicRecsError, defineCustomElementAtomicRecsInterface, defineCustomElementAtomicRecsList, defineCustomElementAtomicRecsResult, defineCustomElementAtomicRecsResultTemplate, defineCustomElementAtomicRefineModal, defineCustomElementAtomicRefineToggle, defineCustomElementAtomicRelevanceInspector, defineCustomElementAtomicResult, defineCustomElementAtomicResultBadge, defineCustomElementAtomicResultChildren, defineCustomElementAtomicResultChildrenTemplate, defineCustomElementAtomicResultDate, defineCustomElementAtomicResultFieldsList, defineCustomElementAtomicResultHtml, defineCustomElementAtomicResultIcon, defineCustomElementAtomicResultImage, defineCustomElementAtomicResultLink, defineCustomElementAtomicResultList, defineCustomElementAtomicResultLocalizedText, defineCustomElementAtomicResultMultiValueText, defineCustomElementAtomicResultNumber, defineCustomElementAtomicResultPrintableUri, defineCustomElementAtomicResultRating, defineCustomElementAtomicResultSectionActions, defineCustomElementAtomicResultSectionBadges, defineCustomElementAtomicResultSectionBottomMetadata, defineCustomElementAtomicResultSectionChildren, defineCustomElementAtomicResultSectionEmphasized, defineCustomElementAtomicResultSectionExcerpt, defineCustomElementAtomicResultSectionTitle, defineCustomElementAtomicResultSectionTitleMetadata, defineCustomElementAtomicResultSectionVisual, defineCustomElementAtomicResultTemplate, defineCustomElementAtomicResultText, defineCustomElementAtomicResultTimespan, defineCustomElementAtomicResultsPerPage, defineCustomElementAtomicSearchBox, defineCustomElementAtomicSearchBoxInstantResults, defineCustomElementAtomicSearchBoxQuerySuggestions, defineCustomElementAtomicSearchBoxRecentQueries, defineCustomElementAtomicSearchInterface, defineCustomElementAtomicSearchLayout, defineCustomElementAtomicSegmentedFacet, defineCustomElementAtomicSegmentedFacetScrollable, defineCustomElementAtomicSmartSnippet, defineCustomElementAtomicSmartSnippetFeedbackModal, defineCustomElementAtomicSmartSnippetSuggestions, defineCustomElementAtomicSortDropdown, defineCustomElementAtomicSortExpression, defineCustomElementAtomicTab, defineCustomElementAtomicTabManager, defineCustomElementAtomicTableElement, defineCustomElementAtomicText, defineCustomElementAtomicTimeframe, defineCustomElementAtomicTimeframeFacet} from '@coveo/atomic/components'; +import {defineCustomElementAtomicAriaLive, defineCustomElementAtomicAutomaticFacet, defineCustomElementAtomicAutomaticFacetGenerator, defineCustomElementAtomicBreadbox, defineCustomElementAtomicCategoryFacet, defineCustomElementAtomicColorFacet, defineCustomElementAtomicCommerceBreadbox, defineCustomElementAtomicCommerceCategoryFacet, defineCustomElementAtomicCommerceDidYouMean, defineCustomElementAtomicCommerceFacet, defineCustomElementAtomicCommerceFacetNumberInput, defineCustomElementAtomicCommerceFacets, defineCustomElementAtomicCommerceInterface, defineCustomElementAtomicCommerceLayout, defineCustomElementAtomicCommerceLoadMoreProducts, defineCustomElementAtomicCommerceNoProducts, defineCustomElementAtomicCommerceNumericFacet, defineCustomElementAtomicCommercePager, defineCustomElementAtomicCommerceProductList, defineCustomElementAtomicCommerceProductsPerPage, defineCustomElementAtomicCommerceQueryError, defineCustomElementAtomicCommerceQuerySummary, defineCustomElementAtomicCommerceRecommendationInterface, defineCustomElementAtomicCommerceRecommendationList, defineCustomElementAtomicCommerceRefineModal, defineCustomElementAtomicCommerceRefineToggle, defineCustomElementAtomicCommerceSortDropdown, defineCustomElementAtomicCommerceText, defineCustomElementAtomicCommerceTimeframeFacet, defineCustomElementAtomicDidYouMean, defineCustomElementAtomicExternal, defineCustomElementAtomicFacet, defineCustomElementAtomicFacetManager, defineCustomElementAtomicFieldCondition, defineCustomElementAtomicFoldedResultList, defineCustomElementAtomicFormatCurrency, defineCustomElementAtomicFormatNumber, defineCustomElementAtomicFormatUnit, defineCustomElementAtomicGeneratedAnswer, defineCustomElementAtomicHtml, defineCustomElementAtomicLayoutSection, defineCustomElementAtomicLoadMoreResults, defineCustomElementAtomicNoResults, defineCustomElementAtomicNotifications, defineCustomElementAtomicNumericFacet, defineCustomElementAtomicNumericRange, defineCustomElementAtomicPager, defineCustomElementAtomicPopover, defineCustomElementAtomicProduct, defineCustomElementAtomicProductChildren, defineCustomElementAtomicProductDescription, defineCustomElementAtomicProductExcerpt, defineCustomElementAtomicProductFieldCondition, defineCustomElementAtomicProductImage, defineCustomElementAtomicProductLink, defineCustomElementAtomicProductMultiValueText, defineCustomElementAtomicProductNumericFieldValue, defineCustomElementAtomicProductPrice, defineCustomElementAtomicProductRating, defineCustomElementAtomicProductSectionActions, defineCustomElementAtomicProductSectionBadges, defineCustomElementAtomicProductSectionBottomMetadata, defineCustomElementAtomicProductSectionChildren, defineCustomElementAtomicProductSectionDescription, defineCustomElementAtomicProductSectionEmphasized, defineCustomElementAtomicProductSectionMetadata, defineCustomElementAtomicProductSectionName, defineCustomElementAtomicProductSectionVisual, defineCustomElementAtomicProductTemplate, defineCustomElementAtomicProductText, defineCustomElementAtomicQueryError, defineCustomElementAtomicQuerySummary, defineCustomElementAtomicQuickview, defineCustomElementAtomicQuickviewModal, defineCustomElementAtomicRatingFacet, defineCustomElementAtomicRatingRangeFacet, defineCustomElementAtomicRecsError, defineCustomElementAtomicRecsInterface, defineCustomElementAtomicRecsList, defineCustomElementAtomicRecsResult, defineCustomElementAtomicRecsResultTemplate, defineCustomElementAtomicRefineModal, defineCustomElementAtomicRefineToggle, defineCustomElementAtomicRelevanceInspector, defineCustomElementAtomicResult, defineCustomElementAtomicResultBadge, defineCustomElementAtomicResultChildren, defineCustomElementAtomicResultChildrenTemplate, defineCustomElementAtomicResultDate, defineCustomElementAtomicResultFieldsList, defineCustomElementAtomicResultHtml, defineCustomElementAtomicResultIcon, defineCustomElementAtomicResultImage, defineCustomElementAtomicResultLink, defineCustomElementAtomicResultList, defineCustomElementAtomicResultLocalizedText, defineCustomElementAtomicResultMultiValueText, defineCustomElementAtomicResultNumber, defineCustomElementAtomicResultPrintableUri, defineCustomElementAtomicResultRating, defineCustomElementAtomicResultSectionActions, defineCustomElementAtomicResultSectionBadges, defineCustomElementAtomicResultSectionBottomMetadata, defineCustomElementAtomicResultSectionChildren, defineCustomElementAtomicResultSectionEmphasized, defineCustomElementAtomicResultSectionExcerpt, defineCustomElementAtomicResultSectionTitle, defineCustomElementAtomicResultSectionTitleMetadata, defineCustomElementAtomicResultSectionVisual, defineCustomElementAtomicResultTemplate, defineCustomElementAtomicResultText, defineCustomElementAtomicResultTimespan, defineCustomElementAtomicResultsPerPage, defineCustomElementAtomicSearchBox, defineCustomElementAtomicSearchBoxInstantResults, defineCustomElementAtomicSearchBoxQuerySuggestions, defineCustomElementAtomicSearchBoxRecentQueries, defineCustomElementAtomicSearchInterface, defineCustomElementAtomicSearchLayout, defineCustomElementAtomicSegmentedFacet, defineCustomElementAtomicSegmentedFacetScrollable, defineCustomElementAtomicSmartSnippet, defineCustomElementAtomicSmartSnippetFeedbackModal, defineCustomElementAtomicSmartSnippetSuggestions, defineCustomElementAtomicSortDropdown, defineCustomElementAtomicSortExpression, defineCustomElementAtomicTab, defineCustomElementAtomicTabManager, defineCustomElementAtomicTableElement, defineCustomElementAtomicText, defineCustomElementAtomicTimeframe, defineCustomElementAtomicTimeframeFacet} from '@coveo/atomic/components'; //#endregion Lit Declarations \ No newline at end of file diff --git a/packages/atomic-react/src/components/commerce/components.ts b/packages/atomic-react/src/components/commerce/components.ts index a9e46ff81a2..1421a469e8a 100644 --- a/packages/atomic-react/src/components/commerce/components.ts +++ b/packages/atomic-react/src/components/commerce/components.ts @@ -1,10 +1,38 @@ import { + AtomicCommerceSearchBox as LitAtomicCommerceSearchBox, + AtomicCommerceSearchBoxInstantProducts as LitAtomicCommerceSearchBoxInstantProducts, + AtomicCommerceSearchBoxQuerySuggestions as LitAtomicCommerceSearchBoxQuerySuggestions, + AtomicCommerceSearchBoxRecentQueries as LitAtomicCommerceSearchBoxRecentQueries, AtomicComponentError as LitAtomicComponentError, AtomicIcon as LitAtomicIcon, } from '@coveo/atomic/components'; import {createComponent} from '@lit/react'; import React from 'react'; +export const AtomicCommerceSearchBox = createComponent({ + tagName: 'atomic-commerce-search-box', + react: React, + elementClass: LitAtomicCommerceSearchBox, +}); + +export const AtomicCommerceSearchBoxInstantProducts = createComponent({ + tagName: 'atomic-commerce-search-box-instant-products', + react: React, + elementClass: LitAtomicCommerceSearchBoxInstantProducts, +}); + +export const AtomicCommerceSearchBoxQuerySuggestions = createComponent({ + tagName: 'atomic-commerce-search-box-query-suggestions', + react: React, + elementClass: LitAtomicCommerceSearchBoxQuerySuggestions, +}); + +export const AtomicCommerceSearchBoxRecentQueries = createComponent({ + tagName: 'atomic-commerce-search-box-recent-queries', + react: React, + elementClass: LitAtomicCommerceSearchBoxRecentQueries, +}); + export const AtomicComponentError = createComponent({ tagName: 'atomic-component-error', react: React, diff --git a/packages/atomic-react/src/components/stencil-generated/commerce/index.ts b/packages/atomic-react/src/components/stencil-generated/commerce/index.ts index 3cee1f3c273..5868296ddae 100644 --- a/packages/atomic-react/src/components/stencil-generated/commerce/index.ts +++ b/packages/atomic-react/src/components/stencil-generated/commerce/index.ts @@ -28,10 +28,6 @@ export const AtomicCommerceRecommendationInterface = /*@__PURE__*/createReactCom export const AtomicCommerceRecommendationList = /*@__PURE__*/createReactComponent('atomic-commerce-recommendation-list'); export const AtomicCommerceRefineModal = /*@__PURE__*/createReactComponent('atomic-commerce-refine-modal'); export const AtomicCommerceRefineToggle = /*@__PURE__*/createReactComponent('atomic-commerce-refine-toggle'); -export const AtomicCommerceSearchBox = /*@__PURE__*/createReactComponent('atomic-commerce-search-box'); -export const AtomicCommerceSearchBoxInstantProducts = /*@__PURE__*/createReactComponent('atomic-commerce-search-box-instant-products'); -export const AtomicCommerceSearchBoxQuerySuggestions = /*@__PURE__*/createReactComponent('atomic-commerce-search-box-query-suggestions'); -export const AtomicCommerceSearchBoxRecentQueries = /*@__PURE__*/createReactComponent('atomic-commerce-search-box-recent-queries'); export const AtomicCommerceSortDropdown = /*@__PURE__*/createReactComponent('atomic-commerce-sort-dropdown'); export const AtomicCommerceText = /*@__PURE__*/createReactComponent('atomic-commerce-text'); export const AtomicCommerceTimeframeFacet = /*@__PURE__*/createReactComponent('atomic-commerce-timeframe-facet'); diff --git a/packages/atomic/scripts/generate-lit-exports.mjs b/packages/atomic/scripts/generate-lit-exports.mjs index 49c0a1bb98b..db818ff6fe0 100644 --- a/packages/atomic/scripts/generate-lit-exports.mjs +++ b/packages/atomic/scripts/generate-lit-exports.mjs @@ -1,6 +1,17 @@ import chalk from 'chalk'; import fs from 'fs'; import path from 'path'; +import prettier from 'prettier'; + +async function formatWithPrettier(content, filePath) { + try { + const options = await prettier.resolveConfig(filePath); + return prettier.format(content, {...options, filepath: filePath}); + } catch (error) { + console.warn(`Failed to format ${filePath} with Prettier`, error); + return content; + } +} const baseComponentsDir = path.resolve( path.dirname(new URL(import.meta.url).pathname), diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index 03c8f19b437..cd585701212 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -13,8 +13,6 @@ import { i18n } from "i18next"; import { StandaloneSearchBoxData } from "./utils/local-storage-utils"; import { ItemDisplayBasicLayout, ItemDisplayDensity, ItemDisplayImageSize, ItemDisplayLayout } from "./components/common/layout/display-options"; import { ItemRenderingFunction } from "./components/common/item-list/stencil-item-list-common"; -import { RedirectionPayload } from "./components/common/search-box/redirection-payload"; -import { AriaLabelGenerator } from "./components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products"; import { AtomicInterface } from "./utils/initialization-utils"; import { AnyBindings } from "./components/common/interface/bindings"; import { NumberInputType } from "./components/common/facets/facet-number-input/number-input-type"; @@ -33,7 +31,8 @@ import { RecsInitializationOptions } from "./components/recommendations/atomic-r 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 { RedirectionPayload } from "./components/common/search-box/redirection-payload"; +import { AriaLabelGenerator } 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, NumericFilter, NumericFilterState, RangeFacetRangeAlgorithm, RangeFacetSortCriterion, RelativeDateUnit, Result, ResultTemplate, ResultTemplateCondition, SearchEngine, SearchStatus } from "@coveo/headless"; export { CategoryFacet, CommerceEngine, DateFacet, InteractiveProduct, LogLevel, NumericFacet, Product, ProductListing, ProductListingSummaryState, ProductTemplate, ProductTemplateCondition, RegularFacet, Search, SearchSummaryState, Summary } from "@coveo/headless/commerce"; @@ -43,8 +42,6 @@ export { i18n } from "i18next"; export { StandaloneSearchBoxData } from "./utils/local-storage-utils"; export { ItemDisplayBasicLayout, ItemDisplayDensity, ItemDisplayImageSize, ItemDisplayLayout } from "./components/common/layout/display-options"; export { ItemRenderingFunction } from "./components/common/item-list/stencil-item-list-common"; -export { RedirectionPayload } from "./components/common/search-box/redirection-payload"; -export { AriaLabelGenerator } from "./components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products"; export { AtomicInterface } from "./utils/initialization-utils"; export { AnyBindings } from "./components/common/interface/bindings"; export { NumberInputType } from "./components/common/facets/facet-number-input/number-input-type"; @@ -63,7 +60,8 @@ export { RecsInitializationOptions } from "./components/recommendations/atomic-r 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 { RedirectionPayload } from "./components/common/search-box/redirection-payload"; +export { AriaLabelGenerator } 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 { /** @@ -651,101 +649,6 @@ export namespace Components { */ interface AtomicCommerceRefineToggle { } - /** - * The `atomic-commerce-search-box` component creates a search box with built-in support for suggestions. - * @alpha - */ - interface AtomicCommerceSearchBox { - /** - * Whether to clear all active query filters when the end user submits a new query from the search box. Setting this option to "false" is not recommended & can lead to an increasing number of queries returning no results. - */ - "clearFilters": boolean; - /** - * Whether to prevent the user from triggering searches and query suggestions from the component. Perfect for use cases where you need to disable the search conditionally. For the specific case when you need to disable the search based on the length of the query, refer to {@link minimumQueryLength}. - */ - "disableSearch": boolean; - /** - * The minimum query length required to enable search. For example, to disable the search for empty queries, set this to `1`. - */ - "minimumQueryLength": number; - /** - * The amount of queries displayed when the user interacts with the search box. By default, a mix of query suggestions and recent queries will be shown. You can configure those settings using the following components as children: - atomic-commerce-search-box-query-suggestions - atomic-commerce-search-box-recent-queries - */ - "numberOfQueries": number; - /** - * Defining this option makes the search box standalone (see [Use a Standalone Search Box](https://docs.coveo.com/en/atomic/latest/usage/ssb/)). This option defines the default URL the user should be redirected to, when a query is submitted. If a query pipeline redirect is triggered, it will redirect to that URL instead (see [query pipeline triggers](https://docs.coveo.com/en/1458)). - */ - "redirectionUrl"?: string; - /** - * The delay for suggestion queries on input, in milliseconds. The suggestion request will be delayed until the end user stops typing for at least the specified amount of time. This delay is used to avoid sending too many requests to the Coveo Platform when the user is typing, as well as reducing potential input lag on low end devices. A higher delay will reduce input lag, at the cost of suggestions freshness. - */ - "suggestionDelay": number; - /** - * The timeout for suggestion queries, in milliseconds. If a suggestion query times out, the suggestions from that particular query won't be shown. - */ - "suggestionTimeout": number; - } - /** - * The `atomic-commerce-search-box-instant-products` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of instant results behavior. - * This component does not support accessibility out-of-the-box. To do so, see [Instant Results Accessibility](https://docs.coveo.com/en/atomic/latest/usage/accessibility/#instant-results-accessibility). - * This component is not supported on mobile. - * @alpha - */ - interface AtomicCommerceSearchBoxInstantProducts { - /** - * The callback to generate an [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label) for a given product so that accessibility tools can fully describe what's visually rendered by a product. By default, or if an empty string is returned, `product.ec_name` is used. - */ - "ariaLabelGenerator"?: AriaLabelGenerator; - /** - * The spacing of various elements in the product list, including the gap between products, the gap between parts of a product, and the font sizes of different parts in a product. - */ - "density": ItemDisplayDensity; - /** - * The expected size of the image displayed in the products. - */ - "imageSize": ItemDisplayImageSize; - /** - * Sets a rendering function to bypass the standard HTML template mechanism for rendering results. You can use this function while working with web frameworks that don't use plain HTML syntax, e.g., React, Angular or Vue. Do not use this method if you integrate Atomic in a plain HTML deployment. - * @param resultRenderingFunction - */ - "setRenderFunction": (resultRenderingFunction: ItemRenderingFunction) => Promise; - } - /** - * The `atomic-commerce-search-box-query-suggestions` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of query suggestion behavior. - * @alpha - */ - interface AtomicCommerceSearchBoxQuerySuggestions { - /** - * The SVG icon to display. - Use a value that starts with `http://`, `https://`, `./`, or `../`, to fetch and display an icon from a given location. - Use a value that starts with `assets://`, to display an icon from the Atomic package. - Use a stringified SVG to display it directly. - */ - "icon"?: string; - /** - * The maximum number of suggestions that will be displayed if the user has typed something into the input field. - */ - "maxWithQuery"?: number; - /** - * The maximum number of suggestions that will be displayed initially when the input field is empty. - */ - "maxWithoutQuery"?: number; - } - /** - * The `atomic-commerce-search-box-recent-queries` component can be added as a child of an `atomic-commerce-search-box` component, allowing for the configuration of recent query suggestions. - * @alpha - */ - interface AtomicCommerceSearchBoxRecentQueries { - /** - * The SVG icon to display. - Use a value that starts with `http://`, `https://`, `./`, or `../`, to fetch and display an icon from a given location. - Use a value that starts with `assets://`, to display an icon from the Atomic package. - Use a stringified SVG to display it directly. - */ - "icon"?: string; - /** - * The maximum number of suggestions to display when the user types in the input field. - */ - "maxWithQuery": number; - /** - * The maximum number of suggestions to display initially, when the input field is empty. - */ - "maxWithoutQuery"?: number; - } /** * The `atomic-commerce-sort-dropdown` component renders a dropdown that the end user can interact with to select the criteria to use when sorting products. * @alpha @@ -3317,7 +3220,7 @@ export namespace Components { /** * The callback to generate an [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label) for a given result so that accessibility tools can fully describe what's visually rendered by a result. By default, or if an empty string is returned, `result.title` is used. */ - "ariaLabelGenerator"?: AriaLabelGenerator1; + "ariaLabelGenerator"?: AriaLabelGenerator; /** * The spacing of various elements in the result list, including the gap between results, the gap between parts of a result, and the font sizes of different parts in a result. */ @@ -3816,10 +3719,6 @@ export interface AtomicCommerceProductsPerPageCustomEvent extends CustomEvent detail: T; target: HTMLAtomicCommerceProductsPerPageElement; } -export interface AtomicCommerceSearchBoxCustomEvent extends CustomEvent { - detail: T; - target: HTMLAtomicCommerceSearchBoxElement; -} export interface AtomicFacetDateInputCustomEvent extends CustomEvent { detail: T; target: HTMLAtomicFacetDateInputElement; @@ -4211,59 +4110,6 @@ declare global { prototype: HTMLAtomicCommerceRefineToggleElement; new (): HTMLAtomicCommerceRefineToggleElement; }; - interface HTMLAtomicCommerceSearchBoxElementEventMap { - "redirect": RedirectionPayload; - } - /** - * The `atomic-commerce-search-box` component creates a search box with built-in support for suggestions. - * @alpha - */ - interface HTMLAtomicCommerceSearchBoxElement extends Components.AtomicCommerceSearchBox, HTMLStencilElement { - addEventListener(type: K, listener: (this: HTMLAtomicCommerceSearchBoxElement, ev: AtomicCommerceSearchBoxCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; - removeEventListener(type: K, listener: (this: HTMLAtomicCommerceSearchBoxElement, ev: AtomicCommerceSearchBoxCustomEvent) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; - removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; - } - var HTMLAtomicCommerceSearchBoxElement: { - prototype: HTMLAtomicCommerceSearchBoxElement; - new (): HTMLAtomicCommerceSearchBoxElement; - }; - /** - * The `atomic-commerce-search-box-instant-products` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of instant results behavior. - * This component does not support accessibility out-of-the-box. To do so, see [Instant Results Accessibility](https://docs.coveo.com/en/atomic/latest/usage/accessibility/#instant-results-accessibility). - * This component is not supported on mobile. - * @alpha - */ - interface HTMLAtomicCommerceSearchBoxInstantProductsElement extends Components.AtomicCommerceSearchBoxInstantProducts, HTMLStencilElement { - } - var HTMLAtomicCommerceSearchBoxInstantProductsElement: { - prototype: HTMLAtomicCommerceSearchBoxInstantProductsElement; - new (): HTMLAtomicCommerceSearchBoxInstantProductsElement; - }; - /** - * The `atomic-commerce-search-box-query-suggestions` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of query suggestion behavior. - * @alpha - */ - interface HTMLAtomicCommerceSearchBoxQuerySuggestionsElement extends Components.AtomicCommerceSearchBoxQuerySuggestions, HTMLStencilElement { - } - var HTMLAtomicCommerceSearchBoxQuerySuggestionsElement: { - prototype: HTMLAtomicCommerceSearchBoxQuerySuggestionsElement; - new (): HTMLAtomicCommerceSearchBoxQuerySuggestionsElement; - }; - /** - * The `atomic-commerce-search-box-recent-queries` component can be added as a child of an `atomic-commerce-search-box` component, allowing for the configuration of recent query suggestions. - * @alpha - */ - interface HTMLAtomicCommerceSearchBoxRecentQueriesElement extends Components.AtomicCommerceSearchBoxRecentQueries, HTMLStencilElement { - } - var HTMLAtomicCommerceSearchBoxRecentQueriesElement: { - prototype: HTMLAtomicCommerceSearchBoxRecentQueriesElement; - new (): HTMLAtomicCommerceSearchBoxRecentQueriesElement; - }; /** * The `atomic-commerce-sort-dropdown` component renders a dropdown that the end user can interact with to select the criteria to use when sorting products. * @alpha @@ -6086,10 +5932,6 @@ declare global { "atomic-commerce-recommendation-list": HTMLAtomicCommerceRecommendationListElement; "atomic-commerce-refine-modal": HTMLAtomicCommerceRefineModalElement; "atomic-commerce-refine-toggle": HTMLAtomicCommerceRefineToggleElement; - "atomic-commerce-search-box": HTMLAtomicCommerceSearchBoxElement; - "atomic-commerce-search-box-instant-products": HTMLAtomicCommerceSearchBoxInstantProductsElement; - "atomic-commerce-search-box-query-suggestions": HTMLAtomicCommerceSearchBoxQuerySuggestionsElement; - "atomic-commerce-search-box-recent-queries": HTMLAtomicCommerceSearchBoxRecentQueriesElement; "atomic-commerce-sort-dropdown": HTMLAtomicCommerceSortDropdownElement; "atomic-commerce-text": HTMLAtomicCommerceTextElement; "atomic-commerce-timeframe-facet": HTMLAtomicCommerceTimeframeFacetElement; @@ -6811,100 +6653,6 @@ declare namespace LocalJSX { */ interface AtomicCommerceRefineToggle { } - /** - * The `atomic-commerce-search-box` component creates a search box with built-in support for suggestions. - * @alpha - */ - interface AtomicCommerceSearchBox { - /** - * Whether to clear all active query filters when the end user submits a new query from the search box. Setting this option to "false" is not recommended & can lead to an increasing number of queries returning no results. - */ - "clearFilters"?: boolean; - /** - * Whether to prevent the user from triggering searches and query suggestions from the component. Perfect for use cases where you need to disable the search conditionally. For the specific case when you need to disable the search based on the length of the query, refer to {@link minimumQueryLength}. - */ - "disableSearch"?: boolean; - /** - * The minimum query length required to enable search. For example, to disable the search for empty queries, set this to `1`. - */ - "minimumQueryLength"?: number; - /** - * The amount of queries displayed when the user interacts with the search box. By default, a mix of query suggestions and recent queries will be shown. You can configure those settings using the following components as children: - atomic-commerce-search-box-query-suggestions - atomic-commerce-search-box-recent-queries - */ - "numberOfQueries"?: number; - /** - * Event that is emitted when a standalone search box redirection is triggered. By default, the search box will directly change the URL and redirect accordingly, so if you want to handle the redirection differently, use this event. Example: ```html ... ``` - */ - "onRedirect"?: (event: AtomicCommerceSearchBoxCustomEvent) => void; - /** - * Defining this option makes the search box standalone (see [Use a Standalone Search Box](https://docs.coveo.com/en/atomic/latest/usage/ssb/)). This option defines the default URL the user should be redirected to, when a query is submitted. If a query pipeline redirect is triggered, it will redirect to that URL instead (see [query pipeline triggers](https://docs.coveo.com/en/1458)). - */ - "redirectionUrl"?: string; - /** - * The delay for suggestion queries on input, in milliseconds. The suggestion request will be delayed until the end user stops typing for at least the specified amount of time. This delay is used to avoid sending too many requests to the Coveo Platform when the user is typing, as well as reducing potential input lag on low end devices. A higher delay will reduce input lag, at the cost of suggestions freshness. - */ - "suggestionDelay"?: number; - /** - * The timeout for suggestion queries, in milliseconds. If a suggestion query times out, the suggestions from that particular query won't be shown. - */ - "suggestionTimeout"?: number; - } - /** - * The `atomic-commerce-search-box-instant-products` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of instant results behavior. - * This component does not support accessibility out-of-the-box. To do so, see [Instant Results Accessibility](https://docs.coveo.com/en/atomic/latest/usage/accessibility/#instant-results-accessibility). - * This component is not supported on mobile. - * @alpha - */ - interface AtomicCommerceSearchBoxInstantProducts { - /** - * The callback to generate an [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label) for a given product so that accessibility tools can fully describe what's visually rendered by a product. By default, or if an empty string is returned, `product.ec_name` is used. - */ - "ariaLabelGenerator"?: AriaLabelGenerator; - /** - * The spacing of various elements in the product list, including the gap between products, the gap between parts of a product, and the font sizes of different parts in a product. - */ - "density"?: ItemDisplayDensity; - /** - * The expected size of the image displayed in the products. - */ - "imageSize"?: ItemDisplayImageSize; - } - /** - * The `atomic-commerce-search-box-query-suggestions` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of query suggestion behavior. - * @alpha - */ - interface AtomicCommerceSearchBoxQuerySuggestions { - /** - * The SVG icon to display. - Use a value that starts with `http://`, `https://`, `./`, or `../`, to fetch and display an icon from a given location. - Use a value that starts with `assets://`, to display an icon from the Atomic package. - Use a stringified SVG to display it directly. - */ - "icon"?: string; - /** - * The maximum number of suggestions that will be displayed if the user has typed something into the input field. - */ - "maxWithQuery"?: number; - /** - * The maximum number of suggestions that will be displayed initially when the input field is empty. - */ - "maxWithoutQuery"?: number; - } - /** - * The `atomic-commerce-search-box-recent-queries` component can be added as a child of an `atomic-commerce-search-box` component, allowing for the configuration of recent query suggestions. - * @alpha - */ - interface AtomicCommerceSearchBoxRecentQueries { - /** - * The SVG icon to display. - Use a value that starts with `http://`, `https://`, `./`, or `../`, to fetch and display an icon from a given location. - Use a value that starts with `assets://`, to display an icon from the Atomic package. - Use a stringified SVG to display it directly. - */ - "icon"?: string; - /** - * The maximum number of suggestions to display when the user types in the input field. - */ - "maxWithQuery"?: number; - /** - * The maximum number of suggestions to display initially, when the input field is empty. - */ - "maxWithoutQuery"?: number; - } /** * The `atomic-commerce-sort-dropdown` component renders a dropdown that the end user can interact with to select the criteria to use when sorting products. * @alpha @@ -9384,7 +9132,7 @@ declare namespace LocalJSX { /** * The callback to generate an [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label) for a given result so that accessibility tools can fully describe what's visually rendered by a result. By default, or if an empty string is returned, `result.title` is used. */ - "ariaLabelGenerator"?: AriaLabelGenerator1; + "ariaLabelGenerator"?: AriaLabelGenerator; /** * The spacing of various elements in the result list, including the gap between results, the gap between parts of a result, and the font sizes of different parts in a result. */ @@ -9891,10 +9639,6 @@ declare namespace LocalJSX { "atomic-commerce-recommendation-list": AtomicCommerceRecommendationList; "atomic-commerce-refine-modal": AtomicCommerceRefineModal; "atomic-commerce-refine-toggle": AtomicCommerceRefineToggle; - "atomic-commerce-search-box": AtomicCommerceSearchBox; - "atomic-commerce-search-box-instant-products": AtomicCommerceSearchBoxInstantProducts; - "atomic-commerce-search-box-query-suggestions": AtomicCommerceSearchBoxQuerySuggestions; - "atomic-commerce-search-box-recent-queries": AtomicCommerceSearchBoxRecentQueries; "atomic-commerce-sort-dropdown": AtomicCommerceSortDropdown; "atomic-commerce-text": AtomicCommerceText; "atomic-commerce-timeframe-facet": AtomicCommerceTimeframeFacet; @@ -10206,28 +9950,6 @@ declare module "@stencil/core" { * @alpha */ "atomic-commerce-refine-toggle": LocalJSX.AtomicCommerceRefineToggle & JSXBase.HTMLAttributes; - /** - * The `atomic-commerce-search-box` component creates a search box with built-in support for suggestions. - * @alpha - */ - "atomic-commerce-search-box": LocalJSX.AtomicCommerceSearchBox & JSXBase.HTMLAttributes; - /** - * The `atomic-commerce-search-box-instant-products` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of instant results behavior. - * This component does not support accessibility out-of-the-box. To do so, see [Instant Results Accessibility](https://docs.coveo.com/en/atomic/latest/usage/accessibility/#instant-results-accessibility). - * This component is not supported on mobile. - * @alpha - */ - "atomic-commerce-search-box-instant-products": LocalJSX.AtomicCommerceSearchBoxInstantProducts & JSXBase.HTMLAttributes; - /** - * The `atomic-commerce-search-box-query-suggestions` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of query suggestion behavior. - * @alpha - */ - "atomic-commerce-search-box-query-suggestions": LocalJSX.AtomicCommerceSearchBoxQuerySuggestions & JSXBase.HTMLAttributes; - /** - * The `atomic-commerce-search-box-recent-queries` component can be added as a child of an `atomic-commerce-search-box` component, allowing for the configuration of recent query suggestions. - * @alpha - */ - "atomic-commerce-search-box-recent-queries": LocalJSX.AtomicCommerceSearchBoxRecentQueries & JSXBase.HTMLAttributes; /** * The `atomic-commerce-sort-dropdown` component renders a dropdown that the end user can interact with to select the criteria to use when sorting products. * @alpha diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.mdx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.mdx new file mode 100644 index 00000000000..4ad5e6d0cb4 --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.mdx @@ -0,0 +1,16 @@ +import {Canvas, ArgTypes, Meta, Title, Description} from '@storybook/blocks'; +import * as AtomicCommerceSearchBoxInstantProductsStories from './atomic-commerce-search-box-instant-products.new.stories'; + +{/* Storybook provides a number of blocs to construct documentation pages */} +{/* https://storybook.js.org/docs/writing-docs/doc-blocks#available-blocks */} + + + + +<Description /> + +<Canvas of={AtomicCommerceSearchBoxInstantProductsStories.Default} /> + +## Properties + +<ArgTypes /> diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.new.stories.tsx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.new.stories.tsx similarity index 100% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.new.stories.tsx rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.new.stories.tsx diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.spec.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.spec.ts similarity index 100% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.spec.ts rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.spec.ts diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.tsx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.ts similarity index 70% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.tsx rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.ts index bd010d51f48..5548da725ac 100644 --- a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.ts @@ -1,34 +1,38 @@ -import { - SearchBox, - buildInstantProducts, - Product, - InstantProducts, -} from '@coveo/headless/commerce'; -import {Component, Element, State, h, Prop, Method} from '@stencil/core'; -import {InitializableComponent} from '../../../../utils/initialization-utils'; -import {encodeForDomAttribute} from '../../../../utils/string-utils'; -import {ItemRenderingFunction} from '../../../common/item-list/stencil-item-list-common'; +import {ItemRenderingFunction} from '@/src/components/common/item-list/item-list-common'; import { ItemDisplayDensity, ItemDisplayImageSize, ItemDisplayLayout, -} from '../../../common/layout/display-options'; +} from '@/src/components/common/layout/display-options'; import { getPartialInstantItemElement, getPartialInstantItemShowAllElement, - InstantItemShowAllButton, -} from '../../../common/suggestions/stencil-instant-item'; + instantItemShowAllButton, +} from '@/src/components/common/suggestions/instant-item'; import { dispatchSearchBoxSuggestionsEvent, SearchBoxSuggestionElement, SearchBoxSuggestions, SearchBoxSuggestionsBindings, -} from '../../../common/suggestions/stencil-suggestions-common'; -import {CommerceBindings as Bindings} from '../../atomic-commerce-interface/atomic-commerce-interface'; -import {ProductTemplateProvider} from '../../product-list/product-template-provider'; +} from '@/src/components/common/suggestions/suggestions-common'; +import {errorGuard} from '@/src/decorators/error-guard'; +import {InitializableComponent} from '@/src/decorators/types'; +import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; +import {encodeForDomAttribute} from '@/src/utils/string-utils'; +import { + buildInstantProducts, + InstantProducts, + Product, + SearchBox, +} from '@coveo/headless/commerce'; +import {html, LitElement} from 'lit'; +import {customElement, property, state} from 'lit/decorators.js'; +import {keyed} from 'lit/directives/keyed.js'; +import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface'; +import {ProductTemplateProvider} from '../product-list/product-template-provider'; export type AriaLabelGenerator = ( - bindings: Bindings, + bindings: CommerceBindings, product: Product ) => string | undefined; @@ -42,24 +46,21 @@ export type AriaLabelGenerator = ( * @slot default - The default slot where the instant products are rendered. * @alpha */ -@Component({ - tag: 'atomic-commerce-search-box-instant-products', - shadow: true, -}) +@customElement('atomic-commerce-search-box-instant-products') +@withTailwindStyles export class AtomicCommerceSearchBoxInstantProducts - implements InitializableComponent<Bindings> + extends LitElement + implements InitializableComponent<CommerceBindings> { - public bindings!: SearchBoxSuggestionsBindings<SearchBox, Bindings>; + public bindings!: SearchBoxSuggestionsBindings<SearchBox, CommerceBindings>; private itemRenderingFunction: ItemRenderingFunction; private products: Product[] = []; private itemTemplateProvider!: ProductTemplateProvider; private instantProducts!: InstantProducts; private display: ItemDisplayLayout = 'list'; - @Element() public host!: HTMLElement; - - @State() public error!: Error; - @State() private templateHasError = false; + @state() public error!: Error; + @state() private templateHasError = false; /** * Sets a rendering function to bypass the standard HTML template mechanism for rendering results. @@ -69,32 +70,39 @@ export class AtomicCommerceSearchBoxInstantProducts * * @param resultRenderingFunction */ - @Method() public async setRenderFunction( + public async setRenderFunction( resultRenderingFunction: ItemRenderingFunction ) { this.itemRenderingFunction = resultRenderingFunction; } + /** * The spacing of various elements in the product list, including the gap between products, the gap between parts of a product, and the font sizes of different parts in a product. */ - @Prop({reflect: true}) public density: ItemDisplayDensity = 'normal'; + @property({reflect: true}) public density: ItemDisplayDensity = 'normal'; + /** * The expected size of the image displayed in the products. */ - @Prop({reflect: true}) public imageSize: ItemDisplayImageSize = 'icon'; + @property({attribute: 'image-size', reflect: true}) + public imageSize: ItemDisplayImageSize = 'icon'; + /** * The callback to generate an [`aria-label`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label) for a given product so that accessibility tools can fully describe what's visually rendered by a product. * * By default, or if an empty string is returned, `product.ec_name` is used. */ - @Prop() public ariaLabelGenerator?: AriaLabelGenerator; + @property() public ariaLabelGenerator?: AriaLabelGenerator; - public componentWillLoad() { + willUpdate() { try { - dispatchSearchBoxSuggestionsEvent<SearchBox, Bindings>((bindings) => { - this.bindings = bindings; - return this.initialize(); - }, this.host); + dispatchSearchBoxSuggestionsEvent<SearchBox, CommerceBindings>( + (bindings) => { + this.bindings = bindings; + return this.initialize(); + }, + this + ); } catch (error) { this.error = error as Error; } @@ -142,22 +150,25 @@ export class AtomicCommerceSearchBoxInstantProducts this.ariaLabelGenerator?.(this.bindings, product) || product.ec_name!, product.permanentid ); + const key = `instant-product-${encodeForDomAttribute( + product.permanentid + )}`; return { ...partialItem, - content: ( - <atomic-product - key={`instant-product-${encodeForDomAttribute(product.permanentid)}`} + content: html`${keyed( + key, + html`<atomic-product part="outline" - product={product} - interactiveProduct={interactiveProduct} - display={this.display} - density={this.density} - imageSize={this.imageSize} - content={this.itemTemplateProvider.getTemplateContent(product)} - stopPropagation={false} - renderingFunction={this.itemRenderingFunction} - ></atomic-product> - ), + .product=${product} + .interactiveProduct=${interactiveProduct} + .display=${this.display} + .density=${this.density} + .imageSize=${this.imageSize} + .content=${this.itemTemplateProvider.getTemplateContent(product)} + .stopPropagation=${false} + .renderingFunction=${this.itemRenderingFunction} + ></atomic-product>` + )}`, onSelect: (e: MouseEvent) => { const link = this.getLink(e.target as HTMLElement); @@ -175,7 +186,9 @@ export class AtomicCommerceSearchBoxInstantProducts ); elements.push({ ...partialItem, - content: <InstantItemShowAllButton i18n={this.bindings.i18n} />, + content: instantItemShowAllButton({ + props: {i18n: this.bindings.i18n}, + }), onSelect: () => { this.bindings.clearSuggestions(); this.bindings.searchBoxController.updateText( @@ -204,7 +217,7 @@ export class AtomicCommerceSearchBoxInstantProducts this.itemTemplateProvider = new ProductTemplateProvider({ includeDefaultTemplate: true, templateElements: Array.from( - this.host.querySelectorAll('atomic-product-template') + this.querySelectorAll('atomic-product-template') ), getResultTemplateRegistered: () => true, setResultTemplateRegistered: () => {}, @@ -215,7 +228,7 @@ export class AtomicCommerceSearchBoxInstantProducts }); return { - position: Array.from(this.host.parentNode!.children).indexOf(this.host), + position: Array.from(this.parentNode!.children).indexOf(this), panel: 'right', onSuggestedQueryChange: (q) => { this.instantProducts.updateQuery(q); @@ -249,14 +262,14 @@ export class AtomicCommerceSearchBoxInstantProducts }); } - public render() { - if (this.error) { - return ( - <atomic-component-error - element={this.host} - error={this.error} - ></atomic-component-error> - ); - } + @errorGuard() + render() { + return html``; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'atomic-commerce-search-box-instant-products': AtomicCommerceSearchBoxInstantProducts; } } diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/e2e/atomic-commerce-search-box-instant-products.e2e.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/e2e/atomic-commerce-search-box-instant-products.e2e.ts similarity index 100% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/e2e/atomic-commerce-search-box-instant-products.e2e.ts rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/e2e/atomic-commerce-search-box-instant-products.e2e.ts diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/e2e/fixture.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/e2e/fixture.ts similarity index 100% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/e2e/fixture.ts rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/e2e/fixture.ts diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/e2e/page-object.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/e2e/page-object.ts similarity index 100% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-instant-products/e2e/page-object.ts rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-instant-products/e2e/page-object.ts diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.mdx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.mdx new file mode 100644 index 00000000000..bc8ee11f72d --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.mdx @@ -0,0 +1,16 @@ +import {Canvas, ArgTypes, Meta, Title, Description} from '@storybook/blocks'; +import * as AtomicCommerceSearchBoxQuerySuggestionsStories from './atomic-commerce-search-box-query-suggestions.new.stories'; + +{/* Storybook provides a number of blocs to construct documentation pages */} +{/* https://storybook.js.org/docs/writing-docs/doc-blocks#available-blocks */} + +<Meta of={AtomicCommerceSearchBoxQuerySuggestionsStories} /> + +<Title /> +<Description /> + +<Canvas of={AtomicCommerceSearchBoxQuerySuggestionsStories.Default} /> + +## Properties + +<ArgTypes /> diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.new.stories.tsx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.new.stories.tsx new file mode 100644 index 00000000000..110ed80007a --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.new.stories.tsx @@ -0,0 +1,26 @@ +import {parameters} from '@/storybook-utils/common/common-meta-parameters'; +import {renderComponent} from '@/storybook-utils/common/render-component'; +import {wrapInSearchInterface} from '@/storybook-utils/search/search-interface-wrapper'; +// import {wrapInCommerceInterface} from '@/storybook-utils/commerce/commerce-interface-wrapper'; +import type {Meta, StoryObj as Story} from '@storybook/web-components'; +import './atomic-commerce-search-box-query-suggestions'; + +// Wrap it in whatever interface/component you need +const {decorator, play} = wrapInSearchInterface(); +// const {decorator, play} = wrapInCommerceInterface(); + +const meta: Meta = { + component: 'atomic-commerce-search-box-query-suggestions', + title: 'AtomicCommerceSearchBoxQuerySuggestions', + id: 'atomic-commerce-search-box-query-suggestions', + render: renderComponent, + decorators: [decorator], + parameters, + play, +}; + +export default meta; + +export const Default: Story = { + name: 'atomic-commerce-search-box-query-suggestions', +}; diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.spec.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.spec.ts similarity index 100% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.spec.ts rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.spec.ts diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.tsx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.ts similarity index 54% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.tsx rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.ts index 11824b60b27..b11b7d3c5dd 100644 --- a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.ts @@ -1,37 +1,42 @@ -import { - SearchBox, - Suggestion, - CommerceEngine, - loadQuerySuggestActions, -} from '@coveo/headless/commerce'; -import {Component, Element, Prop, State, h} from '@stencil/core'; -import SearchIcon from '../../../../images/search.svg'; import { getPartialSearchBoxSuggestionElement, - QuerySuggestionContainer, - QuerySuggestionIcon, - QuerySuggestionText, -} from '../../../common/suggestions/stencil-query-suggestions'; + querySuggestionContainer, + querySuggestionIcon, + querySuggestionText, +} from '@/src/components/common/suggestions/query-suggestions'; import { dispatchSearchBoxSuggestionsEvent, SearchBoxSuggestionElement, SearchBoxSuggestions, SearchBoxSuggestionsBindings, -} from '../../../common/suggestions/stencil-suggestions-common'; +} from '@/src/components/common/suggestions/suggestions-common'; +import {errorGuard} from '@/src/decorators/error-guard'; +import {InitializableComponent} from '@/src/decorators/types'; +import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; +import { + CommerceEngine, + loadQuerySuggestActions, + SearchBox, + Suggestion, +} from '@coveo/headless/commerce'; +import {html, LitElement} from 'lit'; +import {customElement, property, state} from 'lit/decorators.js'; +import SearchIcon from '../../../images/search.svg'; +import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface'; /** * The `atomic-commerce-search-box-query-suggestions` component can be added as a child of an `atomic-search-box` component, allowing for the configuration of query suggestion behavior. * @alpha */ -@Component({ - tag: 'atomic-commerce-search-box-query-suggestions', - shadow: true, -}) -export class AtomicCommerceSearchBoxQuerySuggestions { - private bindings!: SearchBoxSuggestionsBindings<SearchBox>; - @Element() private host!: HTMLElement; +@customElement('atomic-commerce-search-box-query-suggestions') +@withTailwindStyles +export class AtomicCommerceSearchBoxQuerySuggestions + extends LitElement + implements InitializableComponent<CommerceBindings> +{ + public bindings!: SearchBoxSuggestionsBindings<SearchBox, CommerceBindings>; - @State() public error!: Error; + @state() public error!: Error; /** * The SVG icon to display. @@ -40,29 +45,35 @@ export class AtomicCommerceSearchBoxQuerySuggestions { * - Use a value that starts with `assets://`, to display an icon from the Atomic package. * - Use a stringified SVG to display it directly. */ - @Prop() public icon?: string; + @property() icon?: string; /** * The maximum number of suggestions that will be displayed if the user has typed something into the input field. */ - @Prop({reflect: true}) public maxWithQuery?: number; + @property({type: Number, attribute: 'max-with-query', reflect: true}) + public maxWithQuery = 3; + /** * The maximum number of suggestions that will be displayed initially when the input field is empty. */ - @Prop({reflect: true}) public maxWithoutQuery?: number; + @property({type: Number, attribute: 'max-without-query', reflect: true}) + public maxWithoutQuery?: number; - componentWillLoad() { + willUpdate() { try { - dispatchSearchBoxSuggestionsEvent<SearchBox>((bindings) => { - this.bindings = bindings; - return this.initialize(); - }, this.host); + dispatchSearchBoxSuggestionsEvent<SearchBox, CommerceBindings>( + (bindings) => { + this.bindings = bindings; + return this.initialize(); + }, + this + ); } catch (error) { this.error = error as Error; } } - private initialize(): SearchBoxSuggestions { + public initialize(): SearchBoxSuggestions { const engine = this.bindings.engine as CommerceEngine<{querySet: string}>; const {registerQuerySuggest, fetchQuerySuggestions} = loadQuerySuggestActions(engine); @@ -75,7 +86,7 @@ export class AtomicCommerceSearchBoxQuerySuggestions { ); return { - position: Array.from(this.host.parentNode!.children).indexOf(this.host), + position: Array.from(this.parentNode!.children).indexOf(this), onInput: () => engine.dispatch( fetchQuerySuggestions({ @@ -103,30 +114,29 @@ export class AtomicCommerceSearchBoxQuerySuggestions { return { ...partialItem, - content: ( - <QuerySuggestionContainer> - <QuerySuggestionIcon - icon={this.icon || SearchIcon} - hasSuggestion={this.bindings.getSuggestions().length > 1} - /> - - <QuerySuggestionText suggestion={suggestion} hasQuery={hasQuery} /> - </QuerySuggestionContainer> - ), + content: querySuggestionContainer({props: {}})(html` + ${querySuggestionIcon({ + props: { + icon: this.icon || SearchIcon, + hasSuggestion: this.bindings.getSuggestions().length > 1, + }, + })} + ${querySuggestionText({props: {suggestion, hasQuery}})} + `), onSelect: () => { this.bindings.searchBoxController.selectSuggestion(suggestion.rawValue); }, }; } - public render() { - if (this.error) { - return ( - <atomic-component-error - element={this.host} - error={this.error} - ></atomic-component-error> - ); - } + @errorGuard() + render() { + return html``; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'atomic-commerce-search-box-query-suggestions': AtomicCommerceSearchBoxQuerySuggestions; } } diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/atomic-commerce-search-box-query-suggestions.e2e.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/atomic-commerce-search-box-query-suggestions.e2e.ts new file mode 100644 index 00000000000..20be6b612b6 --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/atomic-commerce-search-box-query-suggestions.e2e.ts @@ -0,0 +1,12 @@ +import {test, expect} from './fixture'; + +test.describe('AtomicCommerceSearchBoxQuerySuggestions', () => { + test.beforeEach(async ({commerceSearchBoxQuerySuggestions}) => { + await commerceSearchBoxQuerySuggestions.load(); + }); + + test('should be accessible', async ({makeAxeBuilder}) => { + const accessibilityResults = await makeAxeBuilder().analyze(); + expect(accessibilityResults.violations.length).toEqual(0); + }); +}); diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/fixture.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/fixture.ts new file mode 100644 index 00000000000..0637aab1f87 --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/fixture.ts @@ -0,0 +1,16 @@ +import {AxeFixture, makeAxeBuilder} from '@/playwright-utils/base-fixture'; +import {test as base} from '@playwright/test'; +import {AtomicCommerceSearchBoxQuerySuggestionsPageObject} from './page-object'; + +type Fixtures = { + commerceSearchBoxQuerySuggestions: AtomicCommerceSearchBoxQuerySuggestionsPageObject; +}; + +export const test = base.extend<Fixtures & AxeFixture>({ + makeAxeBuilder, + commerceSearchBoxQuerySuggestions: async ({page}, use) => { + await use(new AtomicCommerceSearchBoxQuerySuggestionsPageObject(page)); + }, +}); + +export {expect} from '@playwright/test'; diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/page-object.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/page-object.ts new file mode 100644 index 00000000000..1f33ca85fb9 --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-query-suggestions/e2e/page-object.ts @@ -0,0 +1,8 @@ +import {BasePageObject} from '@/playwright-utils/lit-base-page-object'; +import type {Page} from '@playwright/test'; + +export class AtomicCommerceSearchBoxQuerySuggestionsPageObject extends BasePageObject { + constructor(page: Page) { + super(page, 'atomic-commerce-search-box-query-suggestions'); + } +} diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.mdx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.mdx new file mode 100644 index 00000000000..a3d11dd238f --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.mdx @@ -0,0 +1,16 @@ +import {Canvas, ArgTypes, Meta, Title, Description} from '@storybook/blocks'; +import * as AtomicCommerceSearchBoxRecentQueriesStories from './atomic-commerce-search-box-recent-queries.new.stories'; + +{/* Storybook provides a number of blocs to construct documentation pages */} +{/* https://storybook.js.org/docs/writing-docs/doc-blocks#available-blocks */} + +<Meta of={AtomicCommerceSearchBoxRecentQueriesStories} /> + +<Title /> +<Description /> + +<Canvas of={AtomicCommerceSearchBoxRecentQueriesStories.Default} /> + +## Properties + +<ArgTypes /> diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.new.stories.tsx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.new.stories.tsx new file mode 100644 index 00000000000..228781da5cf --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.new.stories.tsx @@ -0,0 +1,26 @@ +import {parameters} from '@/storybook-utils/common/common-meta-parameters'; +import {renderComponent} from '@/storybook-utils/common/render-component'; +import {wrapInSearchInterface} from '@/storybook-utils/search/search-interface-wrapper'; +// import {wrapInCommerceInterface} from '@/storybook-utils/commerce/commerce-interface-wrapper'; +import type {Meta, StoryObj as Story} from '@storybook/web-components'; +import './atomic-commerce-search-box-recent-queries'; + +// Wrap it in whatever interface/component you need +const {decorator, play} = wrapInSearchInterface(); +// const {decorator, play} = wrapInCommerceInterface(); + +const meta: Meta = { + component: 'atomic-commerce-search-box-recent-queries', + title: 'AtomicCommerceSearchBoxRecentQueries', + id: 'atomic-commerce-search-box-recent-queries', + render: renderComponent, + decorators: [decorator], + parameters, + play, +}; + +export default meta; + +export const Default: Story = { + name: 'atomic-commerce-search-box-recent-queries', +}; diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.spec.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.spec.ts similarity index 100% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.spec.ts rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.spec.ts diff --git a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.tsx b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.ts similarity index 65% rename from packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.tsx rename to packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.ts index 4cb51e7b0d8..06f241438b9 100644 --- a/packages/atomic/src/components/commerce/search-box-suggestions/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.ts @@ -1,27 +1,31 @@ -import { - SearchBox, - RecentQueriesList, - buildRecentQueriesList, -} from '@coveo/headless/commerce'; -import {Component, Element, Prop, State, h} from '@stencil/core'; -import Clock from '../../../../images/clock.svg'; -import {SafeStorage, StorageItems} from '../../../../utils/local-storage-utils'; -import {once} from '../../../../utils/stencil-utils'; import { getPartialRecentQueryClearElement, getPartialRecentQueryElement, - RecentQueriesContainer, - RecentQueryClear, - RecentQueryIcon, - RecentQueryText, -} from '../../../common/suggestions/stencil-recent-queries'; + recentQueriesContainer, + recentQueryClear, + recentQueryIcon, + recentQueryText, +} from '@/src/components/common/suggestions/recent-queries'; import { dispatchSearchBoxSuggestionsEvent, SearchBoxSuggestionElement, SearchBoxSuggestions, SearchBoxSuggestionsBindings, -} from '../../../common/suggestions/stencil-suggestions-common'; -import {CommerceBindings as Bindings} from '../../atomic-commerce-interface/atomic-commerce-interface'; +} from '@/src/components/common/suggestions/suggestions-common'; +import {errorGuard} from '@/src/decorators/error-guard'; +import {InitializableComponent} from '@/src/decorators/types'; +import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; +import {SafeStorage, StorageItems} from '@/src/utils/local-storage-utils'; +import {once} from '@/src/utils/utils'; +import { + buildRecentQueriesList, + RecentQueriesList, + SearchBox, +} from '@coveo/headless/commerce'; +import {html, LitElement} from 'lit'; +import {customElement, property, state} from 'lit/decorators.js'; +import Clock from '../../../images/clock.svg'; +import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface'; /** * The `atomic-commerce-search-box-recent-queries` component can be added as a child of an `atomic-commerce-search-box` component, allowing for the configuration of recent query suggestions. @@ -31,18 +35,17 @@ import {CommerceBindings as Bindings} from '../../atomic-commerce-interface/atom * * @alpha */ -@Component({ - tag: 'atomic-commerce-search-box-recent-queries', - shadow: true, -}) -export class AtomicCommerceSearchBoxRecentQueries { - private bindings!: SearchBoxSuggestionsBindings<SearchBox, Bindings>; +@customElement('atomic-commerce-search-box-recent-queries') +@withTailwindStyles +export class AtomicCommerceSearchBoxRecentQueries + extends LitElement + implements InitializableComponent<CommerceBindings> +{ + public bindings!: SearchBoxSuggestionsBindings<SearchBox, CommerceBindings>; private recentQueriesList!: RecentQueriesList; private storage!: SafeStorage; - @Element() private host!: HTMLElement; - - @State() public error!: Error; + @state() public error!: Error; /** * The SVG icon to display. @@ -51,23 +54,29 @@ export class AtomicCommerceSearchBoxRecentQueries { * - Use a value that starts with `assets://`, to display an icon from the Atomic package. * - Use a stringified SVG to display it directly. */ - @Prop() public icon?: string; + @property() icon?: string; /** * The maximum number of suggestions to display when the user types in the input field. */ - @Prop({reflect: true}) public maxWithQuery = 3; + @property({type: Number, attribute: 'max-with-query', reflect: true}) + public maxWithQuery = 3; + /** * The maximum number of suggestions to display initially, when the input field is empty. */ - @Prop({reflect: true}) public maxWithoutQuery?: number; + @property({type: Number, attribute: 'max-without-query', reflect: true}) + public maxWithoutQuery?: number; - componentWillLoad() { + willUpdate() { try { - dispatchSearchBoxSuggestionsEvent<SearchBox, Bindings>((bindings) => { - this.bindings = bindings; - return this.initialize(); - }, this.host); + dispatchSearchBoxSuggestionsEvent<SearchBox, CommerceBindings>( + (bindings) => { + this.bindings = bindings; + return this.initialize(); + }, + this + ); } catch (error) { this.error = error as Error; } @@ -77,7 +86,7 @@ export class AtomicCommerceSearchBoxRecentQueries { return this.icon || Clock; } - private initialize(): SearchBoxSuggestions { + public initialize(): SearchBoxSuggestions { this.storage = new SafeStorage(); this.recentQueriesList = buildRecentQueriesList(this.bindings.engine, { initialState: {queries: this.retrieveLocalStorage()}, @@ -87,7 +96,7 @@ export class AtomicCommerceSearchBoxRecentQueries { this.recentQueriesList.subscribe(() => this.updateLocalStorage()); return { - position: Array.from(this.host.parentNode!.children).indexOf(this.host), + position: Array.from(this.parentNode!.children).indexOf(this), renderItems: () => this.renderItems(), }; } @@ -146,10 +155,10 @@ export class AtomicCommerceSearchBoxRecentQueries { return { ...partialItem, - content: <RecentQueryClear i18n={this.bindings.i18n} />, + content: recentQueryClear({props: {i18n: this.bindings.i18n}}), onSelect: () => { this.recentQueriesList.clear(); - this.bindings.triggerSuggestions(); + this.storage.removeItem(StorageItems.RECENT_QUERIES); }, }; } @@ -159,13 +168,12 @@ export class AtomicCommerceSearchBoxRecentQueries { const partialItem = getPartialRecentQueryElement(value, this.bindings.i18n); return { ...partialItem, - content: ( - <RecentQueriesContainer> - <RecentQueryIcon icon={this.renderIcon()} /> - <RecentQueryText query={query} value={value} /> - </RecentQueriesContainer> - ), - + content: recentQueriesContainer({props: {}})(html` + ${recentQueryIcon({ + props: {icon: this.renderIcon()}, + })} + ${recentQueryText({props: {query, value}})} + `), onSelect: () => { if (this.bindings.isStandalone) { this.bindings.searchBoxController.updateText(value); @@ -180,14 +188,14 @@ export class AtomicCommerceSearchBoxRecentQueries { }; } - public render() { - if (this.error) { - return ( - <atomic-component-error - element={this.host} - error={this.error} - ></atomic-component-error> - ); - } + @errorGuard() + render() { + return html``; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'atomic-commerce-search-box-recent-queries': AtomicCommerceSearchBoxRecentQueries; } } diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/atomic-commerce-search-box-recent-queries.e2e.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/atomic-commerce-search-box-recent-queries.e2e.ts new file mode 100644 index 00000000000..58b5973fc9e --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/atomic-commerce-search-box-recent-queries.e2e.ts @@ -0,0 +1,12 @@ +import {test, expect} from './fixture'; + +test.describe('AtomicCommerceSearchBoxRecentQueries', () => { + test.beforeEach(async ({commerceSearchBoxRecentQueries}) => { + await commerceSearchBoxRecentQueries.load(); + }); + + test('should be accessible', async ({makeAxeBuilder}) => { + const accessibilityResults = await makeAxeBuilder().analyze(); + expect(accessibilityResults.violations.length).toEqual(0); + }); +}); diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/fixture.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/fixture.ts new file mode 100644 index 00000000000..fb8763f7d9d --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/fixture.ts @@ -0,0 +1,16 @@ +import {AxeFixture, makeAxeBuilder} from '@/playwright-utils/base-fixture'; +import {test as base} from '@playwright/test'; +import {AtomicCommerceSearchBoxRecentQueriesPageObject} from './page-object'; + +type Fixtures = { + commerceSearchBoxRecentQueries: AtomicCommerceSearchBoxRecentQueriesPageObject; +}; + +export const test = base.extend<Fixtures & AxeFixture>({ + makeAxeBuilder, + commerceSearchBoxRecentQueries: async ({page}, use) => { + await use(new AtomicCommerceSearchBoxRecentQueriesPageObject(page)); + }, +}); + +export {expect} from '@playwright/test'; diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/page-object.ts b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/page-object.ts new file mode 100644 index 00000000000..f591ac24b7b --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box-recent-queries/e2e/page-object.ts @@ -0,0 +1,8 @@ +import {BasePageObject} from '@/playwright-utils/lit-base-page-object'; +import type {Page} from '@playwright/test'; + +export class AtomicCommerceSearchBoxRecentQueriesPageObject extends BasePageObject { + constructor(page: Page) { + super(page, 'atomic-commerce-search-box-recent-queries'); + } +} diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.mdx b/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.mdx new file mode 100644 index 00000000000..e1838296b6b --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.mdx @@ -0,0 +1,16 @@ +import {Canvas, ArgTypes, Meta, Title, Description} from '@storybook/blocks'; +import * as AtomicCommerceSearchBoxStories from './atomic-commerce-search-box.new.stories'; + +{/* Storybook provides a number of blocs to construct documentation pages */} +{/* https://storybook.js.org/docs/writing-docs/doc-blocks#available-blocks */} + +<Meta of={AtomicCommerceSearchBoxStories} /> + +<Title /> +<Description /> + +<Canvas of={AtomicCommerceSearchBoxStories.Default} /> + +## Properties + +<ArgTypes /> diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.tsx b/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.ts similarity index 68% rename from packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.tsx rename to packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.ts index 22f613375c0..e792b08d9c8 100644 --- a/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.tsx +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.ts @@ -1,39 +1,32 @@ +import {bindStateToController} from '@/src/decorators/bind-state'; +import {bindingGuard} from '@/src/decorators/binding-guard'; +import {errorGuard} from '@/src/decorators/error-guard'; +import {InitializableComponent} from '@/src/decorators/types'; +import {watch} from '@/src/decorators/watch'; +import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; +import {InitializeBindingsMixin} from '@/src/mixins/bindings-mixin'; +import {hasKeyboard, isMacOS} from '@/src/utils/device-utils'; +import { + SafeStorage, + StandaloneSearchBoxData, + StorageItems, +} from '@/src/utils/local-storage-utils'; +import {updateBreakpoints} from '@/src/utils/replace-breakpoint'; import {isNullOrUndefined} from '@coveo/bueno'; import { - SearchBoxOptions, - StandaloneSearchBoxState, - StandaloneSearchBox, - SearchBox, - SearchBoxState, buildSearchBox, buildStandaloneSearchBox, loadQuerySetActions, + SearchBox, + SearchBoxOptions, + SearchBoxState, + StandaloneSearchBox, + StandaloneSearchBoxState, } from '@coveo/headless/commerce'; -import { - Component, - Element, - Event, - EventEmitter, - Host, - Listen, - Prop, - State, - Watch, - h, -} from '@stencil/core'; -import {hasKeyboard, isMacOS} from '../../../utils/device-utils'; -import { - BindStateToController, - InitializableComponent, - InitializeBindings, -} from '../../../utils/initialization-utils'; -import { - SafeStorage, - StandaloneSearchBoxData, - StorageItems, -} from '../../../utils/local-storage-utils'; -import {updateBreakpoints} from '../../../utils/replace-breakpoint'; -import {AriaLiveRegion} from '../../../utils/stencil-accessibility-utils'; +import {CSSResultGroup, html, LitElement, nothing, unsafeCSS} from 'lit'; +import {customElement, property, state} from 'lit/decorators.js'; +import {classMap} from 'lit/directives/class-map.js'; +import {ref, RefOrCallback} from 'lit/directives/ref.js'; import { isFocusingOut, once, @@ -41,22 +34,23 @@ import { spreadProperties, } from '../../../utils/stencil-utils'; import {RedirectionPayload} from '../../common/search-box/redirection-payload'; -import {SearchBoxWrapper} from '../../common/search-box/stencil-search-box-wrapper'; +import {searchBoxTextArea} from '../../common/search-box/search-box-text-area'; import { - ButtonSearchSuggestion, - SimpleSearchSuggestion, -} from '../../common/search-box/stencil-search-suggestion'; -import {SearchTextArea} from '../../common/search-box/stencil-search-text-area'; -import {SubmitButton} from '../../common/search-box/stencil-submit-button'; -import {SuggestionManager} from '../../common/suggestions/stencil-suggestion-manager'; + buttonSearchSuggestion, + simpleSearchSuggestion, +} from '../../common/search-box/search-suggestion'; +import {submitButton} from '../../common/search-box/submit-button'; +import {wrapper} from '../../common/search-box/wrapper'; +import {SuggestionManager} from '../../common/suggestions/suggestion-manager'; import { + elementHasQuery, SearchBoxSuggestionElement, SearchBoxSuggestionsBindings, SearchBoxSuggestionsEvent, - elementHasQuery, -} from '../../common/suggestions/stencil-suggestions-common'; -import type {CommerceBindings as Bindings} from '../atomic-commerce-interface/atomic-commerce-interface'; +} from '../../common/suggestions/suggestions-common'; +import {CommerceBindings} from '../atomic-commerce-interface/atomic-commerce-interface'; import {SelectChildProductEventArgs} from '../product-template-components/atomic-product-children/select-child-product-event'; +import styles from './atomic-commerce-search-box.tw.css'; /** * The `atomic-commerce-search-box` component creates a search box with built-in support for suggestions. @@ -100,34 +94,63 @@ import {SelectChildProductEventArgs} from '../product-template-components/atomic * @part instant-results-show-all - The clickable suggestion to show all items for the current instant results search rendered by an `atomic-commerce-search-box-instant-products` component. * @part instant-results-show-all-button - The button inside the clickable suggestion from the `atomic-commerce-search-box-instant-products` component. * + * @event redirect - Event that is emitted when a standalone search box redirection is triggered. By default, the search box will directly change the URL and redirect accordingly, so if you want to handle the redirection differently, use this event. + * * @alpha */ -@Component({ - tag: 'atomic-commerce-search-box', - styleUrl: 'atomic-commerce-search-box.pcss', - shadow: true, -}) +@customElement('atomic-commerce-search-box') +@withTailwindStyles export class AtomicCommerceSearchBox - implements InitializableComponent<Bindings> + extends InitializeBindingsMixin(LitElement) + implements InitializableComponent<CommerceBindings> { - @InitializeBindings() public bindings!: Bindings; - private searchBox!: SearchBox | StandaloneSearchBox; + static styles: CSSResultGroup = [unsafeCSS(styles)]; + + constructor() { + super(); + this.addEventListener( + 'atomic/searchBoxSuggestion/register', + (event: Event) => { + const customEvent = event as CustomEvent< + SearchBoxSuggestionsEvent<SearchBox | StandaloneSearchBox> + >; + if (!this.bindings) { + this.searchBoxSuggestionEventsQueue.push(customEvent); + } else { + this.suggestionManager.registerSuggestionsFromEvent( + customEvent, + this.suggestionBindings + ); + } + } + ); + this.addEventListener('atomic/selectChildProduct', (event: Event) => { + const customEvent = event as CustomEvent<SelectChildProductEventArgs>; + customEvent.stopPropagation(); + this.bindings.store.state.activeProductChild = customEvent.detail.child; + this.suggestionManager.forceUpdate(); + }); + } + + @state() + bindings!: CommerceBindings; + @state() error!: Error; + @state() isExpanded = false; + + private textAreaRef!: HTMLTextAreaElement; + private searchBoxSuggestionEventsQueue: CustomEvent< SearchBoxSuggestionsEvent<SearchBox | StandaloneSearchBox> >[] = []; - private id!: string; - private textAreaRef!: HTMLTextAreaElement; + private suggestionManager!: SuggestionManager< SearchBox | StandaloneSearchBox >; - @Element() private host!: HTMLElement; - - @BindStateToController('searchBox') - @State() + public searchBox!: SearchBox | StandaloneSearchBox; + @bindStateToController('searchBox') + @state() private searchBoxState!: SearchBoxState | StandaloneSearchBoxState; - @State() public error!: Error; - @State() private isExpanded = false; /** * The amount of queries displayed when the user interacts with the search box. @@ -136,7 +159,8 @@ export class AtomicCommerceSearchBox * - atomic-commerce-search-box-query-suggestions * - atomic-commerce-search-box-recent-queries */ - @Prop({reflect: true}) public numberOfQueries = 8; + @property({type: Number, attribute: 'number-of-queries', reflect: true}) + numberOfQueries = 8; /** * Defining this option makes the search box standalone (see [Use a @@ -146,13 +170,15 @@ export class AtomicCommerceSearchBox * If a query pipeline redirect is triggered, it will redirect to that URL instead * (see [query pipeline triggers](https://docs.coveo.com/en/1458)). */ - @Prop({reflect: true}) public redirectionUrl?: string; + @property({attribute: 'redirection-url', reflect: true}) + public redirectionUrl?: string; /** * The timeout for suggestion queries, in milliseconds. * If a suggestion query times out, the suggestions from that particular query won't be shown. */ - @Prop() public suggestionTimeout = 400; + @property({type: Number, attribute: 'suggestion-timeout'}) + public suggestionTimeout = 400; /** * The delay for suggestion queries on input, in milliseconds. @@ -162,51 +188,37 @@ export class AtomicCommerceSearchBox * This delay is used to avoid sending too many requests to the Coveo Platform when the user is typing, as well as reducing potential input lag on low end devices. * A higher delay will reduce input lag, at the cost of suggestions freshness. */ - @Prop() public suggestionDelay = 0; + @property({type: Number, attribute: 'suggestion-delay'}) + public suggestionDelay = 0; /** * Whether to prevent the user from triggering searches and query suggestions from the component. * Perfect for use cases where you need to disable the search conditionally. * For the specific case when you need to disable the search based on the length of the query, refer to {@link minimumQueryLength}. */ - @Prop({reflect: true}) public disableSearch = false; + @property({type: Boolean, attribute: 'disable-search', reflect: true}) + public disableSearch = false; /** * The minimum query length required to enable search. * For example, to disable the search for empty queries, set this to `1`. */ - @Prop({reflect: true}) public minimumQueryLength = 0; + @property({type: Number, attribute: 'minimum-query-length', reflect: true}) + public minimumQueryLength = 0; /** * Whether to clear all active query filters when the end user submits a new query from the search box. * Setting this option to "false" is not recommended & can lead to an increasing number of queries returning no results. */ - @Prop({reflect: true}) public clearFilters = true; - - /** - * Event that is emitted when a standalone search box redirection is triggered. By default, the search box will directly change the URL and redirect accordingly, so if you want to handle the redirection differently, use this event. - * - * Example: - * ```html - * <script> - * document.querySelector('atomic-commerce-search-box').addEventListener((e) => { - * e.preventDefault(); - * // handle redirection - * }); - * </script> - * ... - * <atomic-commerce-search-box redirection-url="/search"></atomic-commerce-search-box> - * ``` - */ - @Event({ - eventName: 'redirect', - }) - public redirect!: EventEmitter<RedirectionPayload>; + @property({type: Boolean, attribute: 'clear-filters', reflect: true}) + public clearFilters = true; - @AriaLiveRegion('search-box') + //TODO + // @AriaLiveRegion('search-box') protected searchBoxAriaMessage!: string; - @AriaLiveRegion('search-suggestions', true) + //TODO + // @AriaLiveRegion('search-suggestions', true) protected suggestionsAriaMessage!: string; public disconnectedCallback = () => {}; @@ -250,7 +262,10 @@ export class AtomicCommerceSearchBox }); } - public componentWillUpdate() { + willUpdate() { + if (!this.searchBoxState || !this.searchBox) { + return; + } if ( !('redirectTo' in this.searchBoxState) || !('afterRedirection' in this.searchBox) @@ -271,35 +286,14 @@ export class AtomicCommerceSearchBox storage.setJSON(StorageItems.STANDALONE_SEARCH_BOX_DATA, data); this.searchBox.afterRedirection(); - const event = this.redirect.emit({redirectTo, value}); + + const event = new CustomEvent<RedirectionPayload>('redirect'); + this.dispatchEvent(event); if (!event.defaultPrevented) { window.location.href = redirectTo; } } - @Listen('atomic/searchBoxSuggestion/register') - public registerSuggestions( - event: CustomEvent< - SearchBoxSuggestionsEvent<SearchBox | StandaloneSearchBox> - > - ) { - if (!this.bindings) { - this.searchBoxSuggestionEventsQueue.push(event); - } else { - this.suggestionManager.registerSuggestionsFromEvent( - event, - this.suggestionBindings - ); - } - } - - @Listen('atomic/selectChildProduct') - public onSelectChildProduct(event: CustomEvent<SelectChildProductEventArgs>) { - event.stopPropagation(); - this.bindings.store.state.activeProductChild = event.detail.child; - this.suggestionManager.forceUpdate(); - } - private registerSearchboxSuggestionEvents() { this.searchBoxSuggestionEventsQueue.forEach((evt) => { this.suggestionManager.registerSuggestionsFromEvent( @@ -310,7 +304,7 @@ export class AtomicCommerceSearchBox this.searchBoxSuggestionEventsQueue = []; } - @Watch('redirectionUrl') + @watch('redirectionUrl') watchRedirectionUrl() { this.updateRedirectionUrl(); } @@ -326,7 +320,7 @@ export class AtomicCommerceSearchBox getSearchBoxValue: () => this.searchBoxState.value, getSuggestionTimeout: () => this.suggestionTimeout, getSuggestionDelay: () => this.suggestionDelay, - getHost: () => this.host, + getHost: () => this, getLogger: () => this.bindings.engine.logger, }); this.suggestionManager.initializeSuggestions(this.suggestionBindings); @@ -401,7 +395,7 @@ export class AtomicCommerceSearchBox }; } - private updateBreakpoints = once(() => updateBreakpoints(this.host)); + private updateBreakpoints = once(() => updateBreakpoints(this)); private async onInput(value: string) { this.updateQueryWithoutQuerySuggestionTrigger(value); @@ -506,31 +500,31 @@ export class AtomicCommerceSearchBox const isButton = item.onSelect || item.query; if (!isButton) { - return ( - <SimpleSearchSuggestion - bindings={this.bindings} - id={id} - suggestion={item} - isSelected={isSelected} - side={side} - index={index} - lastIndex={lastIndex} - isDoubleList={this.suggestionManager.isDoubleList} - ></SimpleSearchSuggestion> - ); + return html`${simpleSearchSuggestion({ + props: { + i18n: this.bindings.i18n, + id, + suggestion: item, + isSelected, + side, + index, + lastIndex, + isDoubleList: this.suggestionManager.isDoubleList, + }, + })}`; } - return ( - <ButtonSearchSuggestion - bindings={this.bindings} - id={id} - suggestion={item} - isSelected={isSelected} - side={side} - index={index} - lastIndex={lastIndex} - isDoubleList={this.suggestionManager.isDoubleList} - onClick={(e: Event) => { + return html`${buttonSearchSuggestion({ + props: { + i18n: this.bindings.i18n, + id, + suggestion: item, + isSelected, + side, + index, + lastIndex, + isDoubleList: this.suggestionManager.isDoubleList, + onClick: (e: Event) => { this.suggestionManager.onSuggestionClick(item, e); if (item.key === 'recent-query-clear') { return; @@ -538,12 +532,12 @@ export class AtomicCommerceSearchBox this.isExpanded = false; this.triggerTextAreaChange(item.query ?? ''); - }} - onMouseOver={() => { + }, + onMouseOver: () => { this.suggestionManager.onSuggestionMouseOver(item, side, id); - }} - ></ButtonSearchSuggestion> - ); + }, + }, + })}`; } private renderPanel( @@ -556,22 +550,20 @@ export class AtomicCommerceSearchBox return null; } - return ( - <div - part={`suggestions suggestions-${side}`} - ref={setRef} - class="flex grow basis-1/2 flex-col" - onMouseDown={(e) => { - if (e.target === getRef()) { - e.preventDefault(); - } - }} - > - {elements.map((suggestion, index) => - this.renderSuggestion(suggestion, index, elements.length - 1, side) - )} - </div> - ); + return html`<div + part="suggestions suggestions-${side}" + ${ref(setRef as RefOrCallback<Element>)} + class="flex grow basis-1/2 flex-col" + @mousedown=${(e: MouseEvent) => { + if (e.target === getRef()) { + e.preventDefault(); + } + }} + > + ${elements.map((suggestion, index) => + this.renderSuggestion(suggestion, index, elements.length - 1, side) + )} + </div>`; } private renderSuggestions() { @@ -579,49 +571,61 @@ export class AtomicCommerceSearchBox return null; } - return ( - <div - id={`${this.id}-popup`} - part={`suggestions-wrapper ${ - this.suggestionManager.isDoubleList - ? 'suggestions-double-list' - : 'suggestions-single-list' - }`} - class={`bg-background border-neutral absolute top-full left-0 z-10 flex w-full rounded-md border ${ - this.suggestionManager.hasSuggestions && - this.isExpanded && - !this.isSearchDisabledForEndUser(this.searchBoxState.value) - ? '' - : 'hidden' - }`} - role="application" - aria-label={this.bindings.i18n.t( - this.suggestionManager.isDoubleList - ? 'search-suggestions-double-list' - : 'search-suggestions-single-list' - )} - aria-activedescendant={this.suggestionManager.activeDescendant} - > - {this.renderPanel( - 'left', - this.suggestionManager.leftSuggestionElements, - (el) => (this.suggestionManager.leftPanel = el), - () => this.suggestionManager.leftPanel - )} - {this.renderPanel( - 'right', - this.suggestionManager.rightSuggestionElements, - (el) => (this.suggestionManager.rightPanel = el), - () => this.suggestionManager.rightPanel - )} - </div> - ); - } + const part = `suggestions-wrapper ${ + this.suggestionManager.isDoubleList + ? 'suggestions-double-list' + : 'suggestions-single-list' + }`; + + const isVisible = + this.suggestionManager.hasSuggestions && + this.isExpanded && + !this.isSearchDisabledForEndUser(this.searchBoxState.value); + + const classes = { + 'bg-background': true, + 'border-neutral': true, + absolute: true, + 'top-full': true, + 'left-0': true, + 'z-10': true, + flex: true, + 'w-full': true, + 'rounded-md': true, + border: true, + hidden: !isVisible, + }; - private renderTextBox = (searchLabel: string) => { + return html`<div + id="${this.id}-popup" + part=${part} + class=${classMap(classes)} + role="application" + aria-label=${this.bindings.i18n.t( + this.suggestionManager.isDoubleList + ? 'search-suggestions-double-list' + : 'search-suggestions-single-list' + )} + aria-activedescendant=${this.suggestionManager.activeDescendant} + > + ${this.renderPanel( + 'left', + this.suggestionManager.leftSuggestionElements, + (el) => (this.suggestionManager.leftPanel = el), + () => this.suggestionManager.leftPanel + )} + ${this.renderPanel( + 'right', + this.suggestionManager.rightSuggestionElements, + (el) => (this.suggestionManager.rightPanel = el), + () => this.suggestionManager.rightPanel + )} + </div>`; + } + private renderTextBox(searchLabel: string) { const props = { loading: this.searchBoxState.isLoading, - bindings: this.bindings, + i18n: this.bindings.i18n, value: this.searchBoxState.value, title: searchLabel, ariaLabel: searchLabel, @@ -641,28 +645,30 @@ export class AtomicCommerceSearchBox }, }; - return ( - <SearchTextArea - textAreaRef={this.textAreaRef} - ref={(el) => (this.textAreaRef = el as HTMLTextAreaElement)} - {...props} - onClear={() => { + return html`${searchBoxTextArea({ + props: { + textAreaRef: this.textAreaRef, + ref: (el) => { + this.textAreaRef = el as HTMLTextAreaElement; + }, + ...props, + onClear: () => { props.onClear(); this.triggerTextAreaChange(''); - }} - /> - ); - }; + }, + }, + })}`; + } private renderAbsolutePositionSpacer() { - return ( - <textarea - aria-hidden - part="textarea-spacer" - class="invisible w-full px-4 py-3.5 text-lg" - rows={1} - ></textarea> - ); + //style 0.875 rem to replace py-3.5 that does not work.. Is there a better solution ? + return html`<textarea + aria-hidden="true" + part="textarea-spacer" + class="invisible w-full p-[5px] px-4 text-lg" + style="padding-block: 0.875rem" + rows="1" + ></textarea>`; } private isSearchDisabledForEndUser(queryValue?: string) { @@ -728,11 +734,12 @@ export class AtomicCommerceSearchBox ); } - public render() { + @bindingGuard() + @errorGuard() + render() { this.updateBreakpoints(); const searchLabel = this.getSearchInputLabel(this.minimumQueryLength); - const Submit = SubmitButton; const isDisabled = this.isSearchDisabledForEndUser( this.searchBoxState.value ); @@ -740,40 +747,48 @@ export class AtomicCommerceSearchBox this.registerSearchboxSuggestionEvents(); } - return ( - <Host> - {this.renderAbsolutePositionSpacer()} - {[ - <SearchBoxWrapper - disabled={isDisabled} - onFocusout={(event) => { - if (!isFocusingOut(event)) { - return; - } + return html` + ${this.renderAbsolutePositionSpacer()} + ${wrapper({ + props: { + disabled: isDisabled, + onFocusout: (event) => { + if (!isFocusingOut(event)) { + return; + } + this.suggestionManager.clearSuggestions(); + this.isExpanded = false; + }, + }, + })( + html`${this.renderTextBox(searchLabel)} + ${submitButton({ + props: { + i18n: this.bindings.i18n, + disabled: isDisabled, + onClick: () => { + this.searchBox.submit(); this.suggestionManager.clearSuggestions(); - this.isExpanded = false; - }} - > - {this.renderTextBox(searchLabel)} - <Submit - bindings={this.bindings} - disabled={isDisabled} - onClick={() => { - this.searchBox.submit(); - this.suggestionManager.clearSuggestions(); - }} - title={searchLabel} - /> - {this.renderSuggestions()} - </SearchBoxWrapper>, - !this.suggestionManager.suggestions.length && ( + }, + title: searchLabel, + }, + })} + ${this.renderSuggestions()}` + )} + ${this.suggestionManager.suggestions.length === 0 + ? html` <slot> <atomic-commerce-search-box-recent-queries></atomic-commerce-search-box-recent-queries> <atomic-commerce-search-box-query-suggestions></atomic-commerce-search-box-query-suggestions> </slot> - ), - ]} - </Host> - ); + ` + : nothing} + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'atomic-commerce-search-box': AtomicCommerceSearchBox; } } diff --git a/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.tw.css b/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.tw.css new file mode 100644 index 00000000000..1225171962f --- /dev/null +++ b/packages/atomic/src/components/commerce/atomic-commerce-search-box/atomic-commerce-search-box.tw.css @@ -0,0 +1 @@ +@import '../../search/atomic-search-box/atomic-search-box.pcss'; diff --git a/packages/atomic/src/components/commerce/index.ts b/packages/atomic/src/components/commerce/index.ts index 6606ec9ae26..f8e3894620b 100644 --- a/packages/atomic/src/components/commerce/index.ts +++ b/packages/atomic/src/components/commerce/index.ts @@ -1,2 +1,5 @@ // Auto-generated file -export {}; +export {AtomicCommerceSearchBox} from './atomic-commerce-search-box/atomic-commerce-search-box.js'; +export {AtomicCommerceSearchBoxInstantProducts} from './atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.js'; +export {AtomicCommerceSearchBoxQuerySuggestions} from './atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.js'; +export {AtomicCommerceSearchBoxRecentQueries} from './atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.js'; diff --git a/packages/atomic/src/components/commerce/lazy-index.ts b/packages/atomic/src/components/commerce/lazy-index.ts index eb708f85099..57463e0fd69 100644 --- a/packages/atomic/src/components/commerce/lazy-index.ts +++ b/packages/atomic/src/components/commerce/lazy-index.ts @@ -1,4 +1,19 @@ // Auto-generated file -export default {} as Record<string, () => Promise<unknown>>; +export default { + 'atomic-commerce-search-box': async () => + await import('./atomic-commerce-search-box/atomic-commerce-search-box.js'), + 'atomic-commerce-search-box-instant-products': async () => + await import( + './atomic-commerce-search-box-instant-products/atomic-commerce-search-box-instant-products.js' + ), + 'atomic-commerce-search-box-query-suggestions': async () => + await import( + './atomic-commerce-search-box-query-suggestions/atomic-commerce-search-box-query-suggestions.js' + ), + 'atomic-commerce-search-box-recent-queries': async () => + await import( + './atomic-commerce-search-box-recent-queries/atomic-commerce-search-box-recent-queries.js' + ), +} as Record<string, () => Promise<unknown>>; export type * from './index.js';