From b0e1a3fef5605b70674aa0cda4963fa9ed898d9b Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 14 May 2025 17:19:47 -0600 Subject: [PATCH 001/118] started pike theme --- packages/snap-preact-demo/templates/src/index.ts | 14 +++++++------- .../snap-preact/components/.storybook/preview.tsx | 8 +++++--- .../snap-preact/components/src/themes/index.ts | 3 ++- .../src/Templates/Stores/LibraryStore.ts | 10 +++++++--- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 92aa46913..18f1bc4b4 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -18,13 +18,13 @@ let config: SnapTemplatesConfig = { }, themes: { global: { - extends: 'base', + extends: 'pike', variables: { - breakpoints: { - mobile: 768, - tablet: 1024, - desktop: 1280, - }, + // breakpoints: { + // mobile: 768, + // tablet: 1024, + // desktop: 1280, + // }, // colors: { // primary: '#6d7175', // secondary: '#202223', @@ -58,7 +58,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'Search', + component: 'SearchHorizontal', }, ], }, diff --git a/packages/snap-preact/components/.storybook/preview.tsx b/packages/snap-preact/components/.storybook/preview.tsx index e956bd25c..5667cabbe 100644 --- a/packages/snap-preact/components/.storybook/preview.tsx +++ b/packages/snap-preact/components/.storybook/preview.tsx @@ -4,7 +4,7 @@ import { observer } from 'mobx-react-lite'; import { SnapTemplates, TemplatesStore } from '../../src'; import { ThemeProvider } from '../src/providers/theme'; -import { base, bocachica, snappy, snapnco } from '../src/themes'; +import { base, bocachica, snappy, snapnco, pike } from '../src/themes'; // custom styles for storybook import './styles.scss'; @@ -24,10 +24,11 @@ const snapTemplates = new SnapTemplates({ }); // need to add each theme synchronously -addTheme(snapTemplates, 'snappy', snappy); +addTheme(snapTemplates, 'base', base); addTheme(snapTemplates, 'bocachica', bocachica); +addTheme(snapTemplates, 'pike', pike); addTheme(snapTemplates, 'snapnco', snapnco); -addTheme(snapTemplates, 'base', base); +addTheme(snapTemplates, 'snappy', snappy); const Providers = observer( ({ templateStore, children, themeName }: { templateStore: TemplatesStore; themeName: string; children: ComponentChildren }) => { @@ -65,6 +66,7 @@ export const decorators = [ ? snapTemplates.templates.themes.library.bocachica.theme : snapTemplates.templates.themes.local.bocachicaSimple.theme, base: templateStory ? snapTemplates.templates.themes.library.base.theme : snapTemplates.templates.themes.local.baseSimple.theme, + pike: templateStory ? snapTemplates.templates.themes.library.pike.theme : snapTemplates.templates.themes.local.pikeSimple.theme, }, defaultTheme: 'base', Provider: templateStory ? CustomThemeProvider : ThemeProvider, diff --git a/packages/snap-preact/components/src/themes/index.ts b/packages/snap-preact/components/src/themes/index.ts index fe1bf6ae5..f0b8db63d 100644 --- a/packages/snap-preact/components/src/themes/index.ts +++ b/packages/snap-preact/components/src/themes/index.ts @@ -1,4 +1,5 @@ export * from './base/base'; export * from './bocachica/bocachica'; -export * from './snappy/snappy'; +export * from './pike/pike'; export * from './snapnco/snapnco'; +export * from './snappy/snappy'; diff --git a/packages/snap-preact/src/Templates/Stores/LibraryStore.ts b/packages/snap-preact/src/Templates/Stores/LibraryStore.ts index 837a360b6..cbb0bf602 100644 --- a/packages/snap-preact/src/Templates/Stores/LibraryStore.ts +++ b/packages/snap-preact/src/Templates/Stores/LibraryStore.ts @@ -29,8 +29,9 @@ export type LibraryImports = { theme: { base: (args?: any) => Promise; bocachica: (args?: any) => Promise; - snappy: (args?: any) => Promise; + pike: (args?: any) => Promise; snapnco: (args?: any) => Promise; + snappy: (args?: any) => Promise; }; plugins: { shopify: { @@ -150,12 +151,15 @@ export class LibraryStore { bocachica: async () => { return this.themes.bocachica || (this.themes.bocachica = (await import('./library/themes/bocachica')).bocachica); }, - snappy: async () => { - return this.themes.snappy || (this.themes.snappy = (await import('./library/themes/snappy')).snappy); + pike: async () => { + return this.themes.pike || (this.themes.pike = (await import('../../../components/src/themes/pike/pike')).pike); }, snapnco: async () => { return this.themes.snapnco || (this.themes.snapnco = (await import('./library/themes/snapnco')).snapnco); }, + snappy: async () => { + return this.themes.snappy || (this.themes.snappy = (await import('./library/themes/snappy')).snappy); + }, }, plugins: { shopify: { From bc3234749f4bcde347b951ca8733fe0d9699007a Mon Sep 17 00:00:00 2001 From: adria Date: Thu, 15 May 2025 18:05:11 -0600 Subject: [PATCH 002/118] fix linting --- .../snap-preact-demo/templates/src/index.ts | 4 +- .../themes/pike/components/atoms/button.ts | 20 +++ .../themes/pike/components/atoms/dropdown.ts | 20 +++ .../src/themes/pike/components/atoms/icon.ts | 20 +++ .../src/themes/pike/components/atoms/image.ts | 20 +++ .../src/themes/pike/components/atoms/index.ts | 54 +++++++ .../pike/components/atoms/loadingBar.ts | 20 +++ .../src/themes/pike/components/atoms/price.ts | 20 +++ .../pike/components/atoms/searchHeader.ts | 21 +++ .../themes/pike/components/atoms/skeleton.ts | 20 +++ .../src/themes/pike/components/index.ts | 34 ++++ .../pike/components/molecules/carousel.ts | 29 ++++ .../pike/components/molecules/checkbox.ts | 22 +++ .../pike/components/molecules/errorHandler.ts | 20 +++ .../components/molecules/facetGridOptions.ts | 21 +++ .../molecules/facetHierarchyOptions.ts | 20 +++ .../components/molecules/facetListOptions.ts | 20 +++ .../molecules/facetPaletteOptions.ts | 21 +++ .../pike/components/molecules/facetSlider.ts | 20 +++ .../pike/components/molecules/filter.ts | 20 +++ .../themes/pike/components/molecules/grid.ts | 20 +++ .../themes/pike/components/molecules/index.ts | 150 ++++++++++++++++++ .../components/molecules/layoutSelector.ts | 20 +++ .../themes/pike/components/molecules/list.ts | 20 +++ .../pike/components/molecules/loadMore.ts | 20 +++ .../pike/components/molecules/overlayBadge.ts | 20 +++ .../pike/components/molecules/pagination.ts | 20 +++ .../pike/components/molecules/perPage.ts | 20 +++ .../themes/pike/components/molecules/radio.ts | 20 +++ .../pike/components/molecules/radioList.ts | 20 +++ .../pike/components/molecules/rating.ts | 20 +++ .../pike/components/molecules/result.ts | 20 +++ .../pike/components/molecules/searchInput.ts | 20 +++ .../pike/components/molecules/select.ts | 23 +++ .../pike/components/molecules/slideout.ts | 20 +++ .../pike/components/molecules/sortBy.ts | 20 +++ .../pike/components/molecules/swatches.ts | 20 +++ .../themes/pike/components/molecules/terms.ts | 20 +++ .../components/molecules/variantSelection.ts | 20 +++ .../themes/pike/components/organisms/facet.ts | 25 +++ .../components/organisms/facetsHorizontal.ts | 36 +++++ .../components/organisms/filterSummary.ts | 23 +++ .../themes/pike/components/organisms/index.ts | 55 +++++++ .../components/organisms/mobileSidebar.ts | 27 ++++ .../pike/components/organisms/noResults.ts | 20 +++ .../pike/components/organisms/sidebar.ts | 20 +++ .../pike/components/organisms/termsList.ts | 20 +++ .../pike/components/organisms/toolbar.ts | 17 ++ .../templates/autocompleteTemplate.ts | 26 +++ .../themes/pike/components/templates/index.ts | 80 ++++++++++ .../components/templates/recommendation.ts | 26 +++ .../templates/recommendationBundle.ts | 29 ++++ .../templates/recommendationBundleEasyAdd.ts | 26 +++ .../templates/recommendationBundleList.ts | 26 +++ .../templates/recommendationBundleVertical.ts | 26 +++ .../templates/recommendationEmail.ts | 28 ++++ .../templates/recommendationGrid.ts | 26 +++ .../pike/components/templates/search.ts | 31 ++++ .../pike/components/templates/searchBoca.ts | 31 ++++ .../components/templates/searchHorizontal.ts | 26 +++ .../components/templates/searchSnapnco.ts | 26 +++ .../pike/components/templates/searchSnappy.ts | 26 +++ .../components/src/themes/pike/custom.ts | 30 ++++ .../components/src/themes/pike/pike.ts | 24 +++ .../components/src/themes/pike/responsive.ts | 8 + 65 files changed, 1696 insertions(+), 1 deletion(-) create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/button.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/image.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/index.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/price.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/index.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/index.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/list.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/perPage.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/result.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/select.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/sortBy.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/index.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/index.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleList.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/recommendationEmail.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/search.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts create mode 100644 packages/snap-preact/components/src/themes/pike/custom.ts create mode 100644 packages/snap-preact/components/src/themes/pike/pike.ts create mode 100644 packages/snap-preact/components/src/themes/pike/responsive.ts diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 18f1bc4b4..ee506142c 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -14,6 +14,7 @@ let config: SnapTemplatesConfig = { components: { result: { CustomResult: async () => (await import('./components/Result')).CustomResult, + EmailResult: async () => (await import('./components/Result')).CustomResult, }, }, themes: { @@ -41,6 +42,7 @@ let config: SnapTemplatesConfig = { email: { Email: { component: 'RecommendationEmail', + resultComponent: 'EmailResult', }, }, default: { @@ -58,7 +60,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchHorizontal', + component: 'SearchBoca', }, ], }, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts new file mode 100644 index 000000000..45644647f --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { ButtonProps } from '../../../../components/Atoms/Button'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Button component +const buttonStyleScript = ({ theme }: ButtonProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Button component props +export const button: ThemeComponent<'button', ButtonProps> = { + default: { + props: { + themeStyleScript: buttonStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts new file mode 100644 index 000000000..f26eaec5d --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { DropdownProps } from '../../../../components/Atoms/Dropdown'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Dropdown component +const dropdownStyleScript = ({ theme }: DropdownProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Dropdown component props +export const dropdown: ThemeComponent<'dropdown', DropdownProps> = { + default: { + props: { + themeStyleScript: dropdownStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts new file mode 100644 index 000000000..e9a7367c0 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { IconProps } from '../../../../components/Atoms/Icon'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Icon component +const iconStyleScript = ({ theme }: IconProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Icon component props +export const icon: ThemeComponent<'icon', IconProps> = { + default: { + props: { + themeStyleScript: iconStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts new file mode 100644 index 000000000..ae520c279 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { ImageProps } from '../../../../components/Atoms/Image'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Image component +const imageStyleScript = ({ theme }: ImageProps & { visibility: React.CSSProperties['visibility'] }) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Image component props +export const image: ThemeComponent<'image', ImageProps> = { + default: { + props: { + themeStyleScript: imageStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts new file mode 100644 index 000000000..51f2a3a64 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts @@ -0,0 +1,54 @@ +import { ThemeResponsiveComplete } from '../../../../providers'; +import { transformThemeComponent } from '../../../utils/transformThemeComponent'; + +// ATOMS Imports +import { button } from './button'; +import { dropdown } from './dropdown'; +import { icon } from './icon'; +import { image } from './image'; +import { loadingBar } from './loadingBar'; +import { price } from './price'; +import { searchHeader } from './searchHeader'; +import { skeleton } from './skeleton'; + +export const atoms: ThemeResponsiveComplete = { + default: { + ...transformThemeComponent('button', button.default), + ...transformThemeComponent('dropdown', dropdown.default), + ...transformThemeComponent('icon', icon.default), + ...transformThemeComponent('image', image.default), + ...transformThemeComponent('loadingBar', loadingBar.default), + ...transformThemeComponent('price', price.default), + ...transformThemeComponent('searchHeader', searchHeader.default), + ...transformThemeComponent('skeleton', skeleton.default), + }, + mobile: { + ...transformThemeComponent('button', button.mobile), + ...transformThemeComponent('dropdown', dropdown.mobile), + ...transformThemeComponent('icon', icon.mobile), + ...transformThemeComponent('image', image.mobile), + ...transformThemeComponent('loadingBar', loadingBar.mobile), + ...transformThemeComponent('price', price.mobile), + ...transformThemeComponent('searchHeader', searchHeader.mobile), + ...transformThemeComponent('skeleton', skeleton.mobile), + }, + tablet: { + ...transformThemeComponent('dropdown', dropdown.tablet), + ...transformThemeComponent('icon', icon.tablet), + ...transformThemeComponent('image', image.tablet), + ...transformThemeComponent('loadingBar', loadingBar.tablet), + ...transformThemeComponent('price', price.tablet), + ...transformThemeComponent('searchHeader', searchHeader.tablet), + ...transformThemeComponent('skeleton', skeleton.tablet), + }, + desktop: { + ...transformThemeComponent('button', button.desktop), + ...transformThemeComponent('dropdown', dropdown.desktop), + ...transformThemeComponent('icon', icon.desktop), + ...transformThemeComponent('image', image.desktop), + ...transformThemeComponent('loadingBar', loadingBar.desktop), + ...transformThemeComponent('price', price.desktop), + ...transformThemeComponent('searchHeader', searchHeader.desktop), + ...transformThemeComponent('skeleton', skeleton.desktop), + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts new file mode 100644 index 000000000..cf5a781c0 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { LoadingBarProps } from '../../../../components/Atoms/Loading'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the LoadingBar component +const loadingBarStyleScript = ({ theme }: LoadingBarProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// LoadingBar component props +export const loadingBar: ThemeComponent<'loadingBar', LoadingBarProps> = { + default: { + props: { + themeStyleScript: loadingBarStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts new file mode 100644 index 000000000..df3c2e516 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { PriceProps } from '../../../../components/Atoms/Price'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Price component +const priceStyleScript = ({ theme }: PriceProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Price component props +export const price: ThemeComponent<'price', PriceProps> = { + default: { + props: { + themeStyleScript: priceStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts new file mode 100644 index 000000000..ff2d6de53 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts @@ -0,0 +1,21 @@ +import { css } from '@emotion/react'; +import type { SearchHeaderProps } from '../../../../components/Atoms/SearchHeader'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the SearchHeader component +const searchHeaderStyleScript = ({ theme }: SearchHeaderProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// SearchHeader component props +export const searchHeader: ThemeComponent<'searchHeader', SearchHeaderProps> = { + default: { + props: { + themeStyleScript: searchHeaderStyleScript, + titleText: (data) => `Search Results${data.search?.query?.string ? ` for "${data.search.query.string}"` : ''}`, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts new file mode 100644 index 000000000..813d43244 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { SkeletonProps } from '../../../../components/Atoms/Skeleton'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Skeleton component +const skeletonStyleScript = ({ theme }: SkeletonProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Skeleton component props +export const skeleton: ThemeComponent<'skeleton', SkeletonProps> = { + default: { + props: { + themeStyleScript: skeletonStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/index.ts b/packages/snap-preact/components/src/themes/pike/components/index.ts new file mode 100644 index 000000000..b431ef8ce --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/index.ts @@ -0,0 +1,34 @@ +import { atoms } from './atoms'; +import { molecules } from './molecules'; +import { organisms } from './organisms'; +import { templates } from './templates'; + +import type { ThemeComponentsRestricted } from '../../../providers'; + +export const components: ThemeComponentsRestricted = { + ...atoms.default, + ...molecules.default, + ...organisms.default, + ...templates.default, +}; + +export const mobileComponents: ThemeComponentsRestricted = { + ...atoms.mobile, + ...molecules.mobile, + ...organisms.mobile, + ...templates.mobile, +}; + +export const tabletComponents: ThemeComponentsRestricted = { + ...atoms.tablet, + ...molecules.tablet, + ...organisms.tablet, + ...templates.tablet, +}; + +export const desktopComponents: ThemeComponentsRestricted = { + ...atoms.desktop, + ...molecules.desktop, + ...organisms.desktop, + ...templates.desktop, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts new file mode 100644 index 000000000..735f97e95 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts @@ -0,0 +1,29 @@ +import { css } from '@emotion/react'; +import type { CarouselProps } from '../../../../components/Molecules/Carousel'; +import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; + +// CSS in JS style script for the Carousel component +const carouselStyleScript = ({ theme }: CarouselProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Carousel component props +export const carousel: ThemeComponent<'carousel', CarouselProps> = { + default: { + props: { + themeStyleScript: carouselStyleScript, + }, + components: { + '*carousel icon.prev': { + icon: customVariables.icons.arrowLeft, + }, + '*carousel icon.next': { + icon: customVariables.icons.arrowRight, + }, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts new file mode 100644 index 000000000..9a5a073bb --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -0,0 +1,22 @@ +import { css } from '@emotion/react'; +import type { CheckboxProps } from '../../../../components/Molecules/Checkbox'; +import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; + +// CSS in JS style script for the Checkbox component +const checkboxStyleScript = ({ theme }: CheckboxProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Checkbox component props +export const checkbox: ThemeComponent<'checkbox', CheckboxProps> = { + default: { + props: { + themeStyleScript: checkboxStyleScript, + icon: customVariables.icons.check, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts new file mode 100644 index 000000000..861c875a0 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { ErrorHandlerProps } from '../../../../components/Molecules/ErrorHandler'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the ErrorHandler component +const errorHandlerStyleScript = ({ theme }: ErrorHandlerProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// ErrorHandler component props +export const errorHandler: ThemeComponent<'errorHandler', ErrorHandlerProps> = { + default: { + props: { + themeStyleScript: errorHandlerStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts new file mode 100644 index 000000000..3e23ed291 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -0,0 +1,21 @@ +import { css } from '@emotion/react'; +import type { FacetGridOptionsProps } from '../../../../components/Molecules/FacetGridOptions'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the FacetGridOptions component +const facetGridOptionsStyleScript = ({ theme }: FacetGridOptionsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// FacetGridOptions component props +export const facetGridOptions: ThemeComponent<'facetGridOptions', FacetGridOptionsProps> = { + default: { + props: { + themeStyleScript: facetGridOptionsStyleScript, + columns: 5, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts new file mode 100644 index 000000000..1b19447ce --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { FacetHierarchyOptionsProps } from '../../../../components/Molecules/FacetHierarchyOptions'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the FacetHierarchyOptions component +const facetHierarchyOptionsStyleScript = ({ theme }: FacetHierarchyOptionsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// FacetHierarchyOptions component props +export const facetHierarchyOptions: ThemeComponent<'facetHierarchyOptions', FacetHierarchyOptionsProps> = { + default: { + props: { + themeStyleScript: facetHierarchyOptionsStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts new file mode 100644 index 000000000..1629d2a71 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { FacetListOptionsProps } from '../../../../components/Molecules/FacetListOptions'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the FacetListOptions component +const facetListOptionsStyleScript = ({ theme }: FacetListOptionsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// FacetListOptions component props +export const facetListOptions: ThemeComponent<'facetListOptions', FacetListOptionsProps> = { + default: { + props: { + themeStyleScript: facetListOptionsStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts new file mode 100644 index 000000000..683654fd9 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -0,0 +1,21 @@ +import { css } from '@emotion/react'; +import type { FacetPaletteOptionsProps } from '../../../../components/Molecules/FacetPaletteOptions'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the FacetPaletteOptions component +const facetPaletteStyleScript = ({ theme }: FacetPaletteOptionsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// FacetPaletteOptions component props +export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPaletteOptionsProps> = { + default: { + props: { + themeStyleScript: facetPaletteStyleScript, + columns: 5, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts new file mode 100644 index 000000000..6ede68012 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { FacetSliderProps } from '../../../../components/Molecules/FacetSlider'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the FacetSlider component +const facetSliderStyleScript = ({ theme }: FacetSliderProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// FacetSlider component props +export const facetSlider: ThemeComponent<'facetSlider', FacetSliderProps> = { + default: { + props: { + themeStyleScript: facetSliderStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts new file mode 100644 index 000000000..33b3c3328 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { FilterProps } from '../../../../components/Molecules/Filter'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Filter component +const filterStyleScript = ({ theme }: FilterProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Filter component props +export const filter: ThemeComponent<'filter', FilterProps> = { + default: { + props: { + themeStyleScript: filterStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts new file mode 100644 index 000000000..fc9ff8cd5 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { GridProps } from '../../../../components/Molecules/Grid'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Grid component +const gridStyleScript = ({ theme }: Partial) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Grid component props +export const grid: ThemeComponent<'grid', GridProps> = { + default: { + props: { + themeStyleScript: gridStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts new file mode 100644 index 000000000..3dc603f4f --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts @@ -0,0 +1,150 @@ +import { transformThemeComponent } from '../../../utils/transformThemeComponent'; +import { ThemeResponsiveComplete } from '../../../../providers'; + +// MOLECULES Imports +import { carousel } from './carousel'; +import { checkbox } from './checkbox'; +import { errorHandler } from './errorHandler'; +import { facetGridOptions } from './facetGridOptions'; +import { facetHierarchyOptions } from './facetHierarchyOptions'; +import { facetListOptions } from './facetListOptions'; +import { facetPaletteOptions } from './facetPaletteOptions'; +import { facetSlider } from './facetSlider'; +import { filter } from './filter'; +import { grid } from './grid'; +import { layoutSelector } from './layoutSelector'; +import { list } from './list'; +import { loadMore } from './loadMore'; +import { overlayBadge } from './overlayBadge'; +import { pagination } from './pagination'; +import { radio } from './radio'; +import { radioList } from './radioList'; +import { result } from './result'; +import { searchInput } from './searchInput'; +import { select } from './select'; +import { slideout } from './slideout'; +import { perPage } from './perPage'; +import { rating } from './rating'; +import { sortBy } from './sortBy'; +import { swatches } from './swatches'; +import { variantSelection } from './variantSelection'; +import { terms } from './terms'; + +export const molecules: ThemeResponsiveComplete = { + default: { + ...transformThemeComponent('carousel', carousel.default), + ...transformThemeComponent('checkbox', checkbox.default), + ...transformThemeComponent('errorHandler', errorHandler.default), + ...transformThemeComponent('facetGridOptions', facetGridOptions.default), + ...transformThemeComponent('facetHierarchyOptions', facetHierarchyOptions.default), + ...transformThemeComponent('facetListOptions', facetListOptions.default), + ...transformThemeComponent('facetPaletteOptions', facetPaletteOptions.default), + ...transformThemeComponent('facetSlider', facetSlider.default), + ...transformThemeComponent('filter', filter.default), + ...transformThemeComponent('grid', grid.default), + ...transformThemeComponent('layoutSelector', layoutSelector.default), + ...transformThemeComponent('list', list.default), + ...transformThemeComponent('loadMore', loadMore.default), + ...transformThemeComponent('overlayBadge', overlayBadge.default), + ...transformThemeComponent('pagination', pagination.default), + ...transformThemeComponent('radio', radio.default), + ...transformThemeComponent('radioList', radioList.default), + ...transformThemeComponent('result', result.default), + ...transformThemeComponent('searchInput', searchInput.default), + ...transformThemeComponent('select', select.default), + ...transformThemeComponent('slideout', slideout.default), + ...transformThemeComponent('perPage', perPage.default), + ...transformThemeComponent('rating', rating.default), + ...transformThemeComponent('sortBy', sortBy.default), + ...transformThemeComponent('swatches', swatches.default), + ...transformThemeComponent('variantSelection', variantSelection.default), + ...transformThemeComponent('terms', terms.default), + }, + mobile: { + ...transformThemeComponent('carousel', carousel.mobile), + ...transformThemeComponent('checkbox', checkbox.mobile), + ...transformThemeComponent('errorHandler', errorHandler.mobile), + ...transformThemeComponent('facetGridOptions', facetGridOptions.mobile), + ...transformThemeComponent('facetHierarchyOptions', facetHierarchyOptions.mobile), + ...transformThemeComponent('facetListOptions', facetListOptions.mobile), + ...transformThemeComponent('facetPaletteOptions', facetPaletteOptions.mobile), + ...transformThemeComponent('facetSlider', facetSlider.mobile), + ...transformThemeComponent('filter', filter.mobile), + ...transformThemeComponent('grid', grid.mobile), + ...transformThemeComponent('layoutSelector', layoutSelector.mobile), + ...transformThemeComponent('list', list.mobile), + ...transformThemeComponent('loadMore', loadMore.mobile), + ...transformThemeComponent('overlayBadge', overlayBadge.mobile), + ...transformThemeComponent('pagination', pagination.mobile), + ...transformThemeComponent('radio', radio.mobile), + ...transformThemeComponent('radioList', radioList.mobile), + ...transformThemeComponent('result', result.mobile), + ...transformThemeComponent('searchInput', searchInput.mobile), + ...transformThemeComponent('select', select.mobile), + ...transformThemeComponent('slideout', slideout.mobile), + ...transformThemeComponent('perPage', perPage.mobile), + ...transformThemeComponent('rating', rating.mobile), + ...transformThemeComponent('sortBy', sortBy.mobile), + ...transformThemeComponent('swatches', swatches.mobile), + ...transformThemeComponent('variantSelection', variantSelection.mobile), + ...transformThemeComponent('terms', terms.mobile), + }, + tablet: { + ...transformThemeComponent('carousel', carousel.tablet), + ...transformThemeComponent('checkbox', checkbox.tablet), + ...transformThemeComponent('errorHandler', errorHandler.tablet), + ...transformThemeComponent('facetGridOptions', facetGridOptions.tablet), + ...transformThemeComponent('facetHierarchyOptions', facetHierarchyOptions.tablet), + ...transformThemeComponent('facetListOptions', facetListOptions.tablet), + ...transformThemeComponent('facetPaletteOptions', facetPaletteOptions.tablet), + ...transformThemeComponent('facetSlider', facetSlider.tablet), + ...transformThemeComponent('filter', filter.tablet), + ...transformThemeComponent('grid', grid.tablet), + ...transformThemeComponent('layoutSelector', layoutSelector.tablet), + ...transformThemeComponent('list', list.tablet), + ...transformThemeComponent('loadMore', loadMore.tablet), + ...transformThemeComponent('overlayBadge', overlayBadge.tablet), + ...transformThemeComponent('pagination', pagination.tablet), + ...transformThemeComponent('radio', radio.tablet), + ...transformThemeComponent('radioList', radioList.tablet), + ...transformThemeComponent('result', result.tablet), + ...transformThemeComponent('searchInput', searchInput.tablet), + ...transformThemeComponent('select', select.tablet), + ...transformThemeComponent('slideout', slideout.tablet), + ...transformThemeComponent('perPage', perPage.tablet), + ...transformThemeComponent('rating', rating.tablet), + ...transformThemeComponent('sortBy', sortBy.tablet), + ...transformThemeComponent('swatches', swatches.tablet), + ...transformThemeComponent('variantSelection', variantSelection.tablet), + ...transformThemeComponent('terms', terms.tablet), + }, + desktop: { + ...transformThemeComponent('carousel', carousel.desktop), + ...transformThemeComponent('checkbox', checkbox.desktop), + ...transformThemeComponent('errorHandler', errorHandler.desktop), + ...transformThemeComponent('facetGridOptions', facetGridOptions.desktop), + ...transformThemeComponent('facetHierarchyOptions', facetHierarchyOptions.desktop), + ...transformThemeComponent('facetListOptions', facetListOptions.desktop), + ...transformThemeComponent('facetPaletteOptions', facetPaletteOptions.desktop), + ...transformThemeComponent('facetSlider', facetSlider.desktop), + ...transformThemeComponent('filter', filter.desktop), + ...transformThemeComponent('grid', grid.desktop), + ...transformThemeComponent('layoutSelector', layoutSelector.desktop), + ...transformThemeComponent('list', list.desktop), + ...transformThemeComponent('loadMore', loadMore.desktop), + ...transformThemeComponent('overlayBadge', overlayBadge.desktop), + ...transformThemeComponent('pagination', pagination.desktop), + ...transformThemeComponent('radio', radio.desktop), + ...transformThemeComponent('radioList', radioList.desktop), + ...transformThemeComponent('result', result.desktop), + ...transformThemeComponent('searchInput', searchInput.desktop), + ...transformThemeComponent('select', select.desktop), + ...transformThemeComponent('slideout', slideout.desktop), + ...transformThemeComponent('perPage', perPage.desktop), + ...transformThemeComponent('rating', rating.desktop), + ...transformThemeComponent('sortBy', sortBy.desktop), + ...transformThemeComponent('swatches', swatches.desktop), + ...transformThemeComponent('variantSelection', variantSelection.desktop), + ...transformThemeComponent('terms', terms.desktop), + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts new file mode 100644 index 000000000..ee671378c --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { LayoutSelectorProps } from '../../../../components/Molecules/LayoutSelector'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the LayoutSelector component +const layoutSelectorStyleScript = ({ theme }: LayoutSelectorProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// LayoutSelector component props +export const layoutSelector: ThemeComponent<'layoutSelector', LayoutSelectorProps> = { + default: { + props: { + themeStyleScript: layoutSelectorStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts new file mode 100644 index 000000000..e3ff38a13 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { ListProps } from '../../../../components/Molecules/List'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the List component +const listStyleScript = ({ theme }: ListProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// List component props +export const list: ThemeComponent<'list', ListProps> = { + default: { + props: { + themeStyleScript: listStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts new file mode 100644 index 000000000..bc875ecb5 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { LoadMoreProps } from '../../../../components/Molecules/LoadMore'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the LoadMore component +const loadMoreStyleScript = ({ theme }: LoadMoreProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// LoadMore component props +export const loadMore: ThemeComponent<'loadMore', LoadMoreProps> = { + default: { + props: { + themeStyleScript: loadMoreStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts new file mode 100644 index 000000000..63cd12042 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { OverlayBadgeProps } from '../../../../components/Molecules/OverlayBadge'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Search component +const overlayBadgeStyleScript = ({ theme }: OverlayBadgeProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// OverlayBadge component props +export const overlayBadge: ThemeComponent<'overlayBadge', OverlayBadgeProps> = { + default: { + props: { + themeStyleScript: overlayBadgeStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts new file mode 100644 index 000000000..6d7d3a60b --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { PaginationProps } from '../../../../components/Molecules/Pagination'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Pagination component +const paginationStyleScript = ({ theme }: PaginationProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Pagination component props +export const pagination: ThemeComponent<'pagination', PaginationProps> = { + default: { + props: { + themeStyleScript: paginationStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/perPage.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/perPage.ts new file mode 100644 index 000000000..eab331672 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/perPage.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { PerPageProps } from '../../../../components/Molecules/PerPage'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the PerPage component +const perPageStyleScript = ({ theme }: PerPageProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// PerPage component props +export const perPage: ThemeComponent<'perPage', PerPageProps> = { + default: { + props: { + themeStyleScript: perPageStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts new file mode 100644 index 000000000..c9674f025 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { RadioProps } from '../../../../components/Molecules/Radio'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Radio component +const radioStyleScript = ({ theme }: RadioProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Radio component props +export const radio: ThemeComponent<'radio', RadioProps> = { + default: { + props: { + themeStyleScript: radioStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts new file mode 100644 index 000000000..7022d2a14 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { RadioListProps } from '../../../../components/Molecules/RadioList'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the RadioList component +const radioListStyleScript = ({ theme }: RadioListProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// RadioList component props +export const radioList: ThemeComponent<'radioList', RadioListProps> = { + default: { + props: { + themeStyleScript: radioListStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts new file mode 100644 index 000000000..1e0ec87f3 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { RatingProps } from '../../../../components/Molecules/Rating'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Rating component +const ratingStyleScript = ({ theme }: RatingProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Rating component props +export const rating: ThemeComponent<'rating', RatingProps> = { + default: { + props: { + themeStyleScript: ratingStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts new file mode 100644 index 000000000..49e929a9d --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { ResultProps } from '../../../../components/Molecules/Result'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Result component +const resultStyleScript = ({ theme }: ResultProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Result component props +export const result: ThemeComponent<'result', ResultProps> = { + default: { + props: { + themeStyleScript: resultStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts new file mode 100644 index 000000000..b1f9f379f --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { SearchInputProps } from '../../../../components/Molecules/SearchInput'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the SearchInput component +const searchInputStyleScript = ({ theme }: SearchInputProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// SearchInput component props +export const searchInput: ThemeComponent<'searchInput', SearchInputProps> = { + default: { + props: { + themeStyleScript: searchInputStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts new file mode 100644 index 000000000..015d97379 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -0,0 +1,23 @@ +import { css } from '@emotion/react'; +import type { SelectProps } from '../../../../components/Molecules/Select'; +import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; + +// CSS in JS style script for the Select component +const selectStyleScript = ({ theme }: SelectProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Select component props +export const select: ThemeComponent<'select', SelectProps> = { + default: { + props: { + themeStyleScript: selectStyleScript, + iconOpen: customVariables?.icons?.arrowDown, + iconClose: customVariables.icons.arrowUp, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts new file mode 100644 index 000000000..bae2c8c4b --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { SlideoutProps } from '../../../../components/Molecules/Slideout'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Slideout component +const slideoutStyleScript = ({ theme }: SlideoutProps & { isActive: boolean }) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Slideout component props +export const slideout: ThemeComponent<'slideout', SlideoutProps> = { + default: { + props: { + themeStyleScript: slideoutStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/sortBy.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/sortBy.ts new file mode 100644 index 000000000..125b78efb --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/sortBy.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { SortByProps } from '../../../../components/Molecules/SortBy'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the SortBy component +const sortByStyleScript = ({ theme }: SortByProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// SortBy component props +export const sortBy: ThemeComponent<'sortBy', SortByProps> = { + default: { + props: { + themeStyleScript: sortByStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts new file mode 100644 index 000000000..2ff9c52df --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { SwatchesProps } from '../../../../components/Molecules/Swatches'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Swatches component +const swatchesStyleScript = ({ theme }: SwatchesProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Swatches component props +export const swatches: ThemeComponent<'swatches', SwatchesProps> = { + default: { + props: { + themeStyleScript: swatchesStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts new file mode 100644 index 000000000..1587e60ca --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { TermsProps } from '../../../../components/Molecules/Terms'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Terms component +const termsStyleScript = ({ theme }: TermsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Terms component props +export const terms: ThemeComponent<'terms', TermsProps> = { + default: { + props: { + styleScript: termsStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts new file mode 100644 index 000000000..5f40c226d --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { VariantSelectionProps } from '../../../../components/Molecules/VariantSelection'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Swatches component +const variantSelectionStyleScript = ({ theme }: VariantSelectionProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// VariantSelection component props +export const variantSelection: ThemeComponent<'variantSelection', VariantSelectionProps> = { + default: { + props: { + themeStyleScript: variantSelectionStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts new file mode 100644 index 000000000..c7bd2aa63 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -0,0 +1,25 @@ +import { css } from '@emotion/react'; +import type { FacetProps } from '../../../../components/Organisms/Facet'; +import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; + +// CSS in JS style script for the Facet component +const facetStyleScript = ({ theme }: FacetProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Facet component props +export const facet: ThemeComponent<'facet', FacetProps> = { + default: { + props: { + themeStyleScript: facetStyleScript, + iconCollapse: customVariables.icons.arrowUp, + iconExpand: customVariables.icons.arrowDown, + iconOverflowMore: customVariables.icons.plus, + iconOverflowLess: customVariables.icons.minus, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts new file mode 100644 index 000000000..2c8855982 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts @@ -0,0 +1,36 @@ +import { css } from '@emotion/react'; +import type { FacetsHorizontalProps } from '../../../../components/Organisms/FacetsHorizontal'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Facets component +const facetsHorizontalStyleScript = ({ theme }: FacetsHorizontalProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// FacetsHorizontal component props +export const facetsHorizontal: ThemeComponent<'facetsHorizontal', FacetsHorizontalProps> = { + default: { + props: { + themeStyleScript: facetsHorizontalStyleScript, + limit: 9, + }, + }, + mobile: { + props: { + limit: 0, + }, + }, + tablet: { + props: { + limit: 5, + }, + }, + desktop: { + props: { + limit: 7, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts new file mode 100644 index 000000000..5aabe30a6 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts @@ -0,0 +1,23 @@ +import { css } from '@emotion/react'; +import type { FilterSummaryProps } from '../../../../components/Organisms/FilterSummary'; +import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; + +// CSS in JS style script for the FilterSummary component +const filterSummaryStyleScript = ({ theme }: FilterSummaryProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// FilterSummary component props +export const filterSummary: ThemeComponent<'filterSummary', FilterSummaryProps> = { + default: { + props: { + themeStyleScript: filterSummaryStyleScript, + clearAllIcon: customVariables.icons.close, + filterIcon: customVariables.icons.close, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts new file mode 100644 index 000000000..7cae13446 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts @@ -0,0 +1,55 @@ +import { transformThemeComponent } from '../../../utils/transformThemeComponent'; +import { ThemeResponsiveComplete } from '../../../../providers'; + +// ORGANISMS Imports +import { facet } from './facet'; +import { facetsHorizontal } from './facetsHorizontal'; +import { filterSummary } from './filterSummary'; +import { mobileSidebar } from './mobileSidebar'; +import { noResults } from './noResults'; +import { sidebar } from './sidebar'; +import { termsList } from './termsList'; +import { toolbar } from './toolbar'; + +export const organisms: ThemeResponsiveComplete = { + default: { + ...transformThemeComponent('facet', facet.default), + ...transformThemeComponent('facetsHorizontal', facetsHorizontal.default), + ...transformThemeComponent('filterSummary', filterSummary.default), + ...transformThemeComponent('mobileSidebar', mobileSidebar.default), + ...transformThemeComponent('noResults', noResults.default), + ...transformThemeComponent('sidebar', sidebar.default), + ...transformThemeComponent('toolbar', toolbar.default), + ...transformThemeComponent('termsList', termsList.default), + }, + mobile: { + ...transformThemeComponent('facet', facet.mobile), + ...transformThemeComponent('facetsHorizontal', facetsHorizontal.mobile), + ...transformThemeComponent('filterSummary', filterSummary.mobile), + ...transformThemeComponent('mobileSidebar', mobileSidebar.mobile), + ...transformThemeComponent('noResults', noResults.mobile), + ...transformThemeComponent('sidebar', sidebar.mobile), + ...transformThemeComponent('toolbar', toolbar.mobile), + ...transformThemeComponent('termsList', termsList.mobile), + }, + tablet: { + ...transformThemeComponent('facet', facet.tablet), + ...transformThemeComponent('facetsHorizontal', facetsHorizontal.tablet), + ...transformThemeComponent('filterSummary', filterSummary.tablet), + ...transformThemeComponent('mobileSidebar', mobileSidebar.tablet), + ...transformThemeComponent('noResults', noResults.tablet), + ...transformThemeComponent('sidebar', sidebar.tablet), + ...transformThemeComponent('toolbar', toolbar.tablet), + ...transformThemeComponent('termsList', termsList.tablet), + }, + desktop: { + ...transformThemeComponent('facet', facet.desktop), + ...transformThemeComponent('facetsHorizontal', facetsHorizontal.desktop), + ...transformThemeComponent('filterSummary', filterSummary.desktop), + ...transformThemeComponent('mobileSidebar', mobileSidebar.desktop), + ...transformThemeComponent('noResults', noResults.desktop), + ...transformThemeComponent('sidebar', sidebar.desktop), + ...transformThemeComponent('toolbar', toolbar.desktop), + ...transformThemeComponent('termsList', termsList.desktop), + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts new file mode 100644 index 000000000..3e7a7d3e8 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -0,0 +1,27 @@ +import { css } from '@emotion/react'; +import type { MobileSidebarProps } from '../../../../components/Organisms/MobileSidebar'; +import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; + +// CSS in JS style script for the MobileSidebar component +const mobileSidebarStyleScript = ({ theme }: MobileSidebarProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// MobileSidebar component props +export const mobileSidebar: ThemeComponent<'mobileSidebar', MobileSidebarProps> = { + default: { + props: { + themeStyleScript: mobileSidebarStyleScript, + openButtonIcon: customVariables.icons.filter, + }, + components: { + '*mobileSidebar button.close': { + icon: 'heart', + }, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts new file mode 100644 index 000000000..bf08e5558 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { NoResultsProps } from '../../../../components/Organisms/NoResults'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the NoResults component +const noResultsStyleScript = ({ theme }: NoResultsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// NoResults component props +export const noResults: ThemeComponent<'noResults', NoResultsProps> = { + default: { + props: { + themeStyleScript: noResultsStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts new file mode 100644 index 000000000..c6b3a1042 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { SidebarProps } from '../../../../components/Organisms/Sidebar'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Sidebar component +const sidebarStyleScript = ({ theme }: SidebarProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Sidebar component props +export const sidebar: ThemeComponent<'sidebar', SidebarProps> = { + default: { + props: { + themeStyleScript: sidebarStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts new file mode 100644 index 000000000..17cfce0f3 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { TermsListProps } from '../../../../components/Organisms/TermsList'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Terms component +const termsListStyleScript = ({ theme }: TermsListProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// TermsList component props +export const termsList: ThemeComponent<'termsList', TermsListProps> = { + default: { + props: { + styleScript: termsListStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts new file mode 100644 index 000000000..67002e969 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts @@ -0,0 +1,17 @@ +import { css } from '@emotion/react'; +import { ThemeComponent } from '../../../../providers'; +import { ToolbarProps } from '../../../../components/Organisms/Toolbar'; + +// CSS in JS style script for the Toolbar component +const toolbarStyleScript = () => { + return css({}); +}; + +// Toolbar component props +export const toolbar: ThemeComponent<'toolbar', ToolbarProps> = { + default: { + props: { + themeStyleScript: toolbarStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts new file mode 100644 index 000000000..f97ce46b7 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { AutocompleteTemplateProps } from '../../../../components/Templates/AutocompleteTemplate'; +import { autocompleteThemeComponentProps } from '../../../themeComponents/autocompleteTemplate'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Search component +const autocompleteTemplateStyleScript = ({ theme }: AutocompleteTemplateProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// AutocompleteTemplate component props come from Template export +export const autocompleteTemplate: ThemeComponent<'autocompleteTemplate', AutocompleteTemplateProps> = { + default: { + props: { + ...autocompleteThemeComponentProps.default?.props, + themeStyleScript: autocompleteTemplateStyleScript, + }, + components: autocompleteThemeComponentProps.default?.components, + }, + mobile: autocompleteThemeComponentProps.mobile, + desktop: autocompleteThemeComponentProps.desktop, + tablet: autocompleteThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/index.ts b/packages/snap-preact/components/src/themes/pike/components/templates/index.ts new file mode 100644 index 000000000..634847b63 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/index.ts @@ -0,0 +1,80 @@ +import { transformThemeComponent } from '../../../utils/transformThemeComponent'; +import { ThemeResponsiveComplete } from '../../../../providers'; + +// TEMPLATES +import { autocompleteTemplate } from './autocompleteTemplate'; +import { recommendation } from './recommendation'; +import { recommendationBundle } from './recommendationBundle'; +import { recommendationBundleEasyAdd } from './recommendationBundleEasyAdd'; +import { recommendationBundleList } from './recommendationBundleList'; +import { recommendationBundleVertical } from './recommendationBundleVertical'; +import { recommendationGrid } from './recommendationGrid'; +import { recommendationEmail } from './recommendationEmail'; +import { search } from './search'; +import { searchHorizontal } from './searchHorizontal'; +import { searchBoca } from './searchBoca'; +import { searchSnapnco } from './searchSnapnco'; +import { searchSnappy } from './searchSnappy'; + +export const templates: ThemeResponsiveComplete = { + default: { + ...transformThemeComponent('autocompleteTemplate', autocompleteTemplate.default), + ...transformThemeComponent('recommendation', recommendation.default), + ...transformThemeComponent('recommendationBundle', recommendationBundle.default), + ...transformThemeComponent('recommendationBundleEasyAdd', recommendationBundleEasyAdd.default), + ...transformThemeComponent('recommendationBundleList', recommendationBundleList.default), + ...transformThemeComponent('recommendationBundleVertical', recommendationBundleVertical.default), + ...transformThemeComponent('recommendationGrid', recommendationGrid.default), + ...transformThemeComponent('recommendationEmail', recommendationEmail.default), + ...transformThemeComponent('search', search.default), + ...transformThemeComponent('searchBoca', searchBoca.default), + ...transformThemeComponent('searchSnapnco', searchSnapnco.default), + ...transformThemeComponent('searchSnappy', searchSnappy.default), + ...transformThemeComponent('searchHorizontal', searchHorizontal.default), + }, + mobile: { + ...transformThemeComponent('autocompleteTemplate', autocompleteTemplate.mobile), + ...transformThemeComponent('recommendation', recommendation.mobile), + ...transformThemeComponent('recommendationBundle', recommendationBundle.mobile), + ...transformThemeComponent('recommendationBundleEasyAdd', recommendationBundleEasyAdd.mobile), + ...transformThemeComponent('recommendationBundleList', recommendationBundleList.mobile), + ...transformThemeComponent('recommendationBundleVertical', recommendationBundleVertical.mobile), + ...transformThemeComponent('recommendationGrid', recommendationGrid.mobile), + ...transformThemeComponent('recommendationEmail', recommendationEmail.mobile), + ...transformThemeComponent('search', search.mobile), + ...transformThemeComponent('searchBoca', searchBoca.mobile), + ...transformThemeComponent('searchSnapnco', searchSnapnco.mobile), + ...transformThemeComponent('searchSnappy', searchSnappy.mobile), + ...transformThemeComponent('searchHorizontal', searchHorizontal.mobile), + }, + tablet: { + ...transformThemeComponent('autocompleteTemplate', autocompleteTemplate.tablet), + ...transformThemeComponent('recommendation', recommendation.tablet), + ...transformThemeComponent('recommendationBundle', recommendationBundle.tablet), + ...transformThemeComponent('recommendationBundleEasyAdd', recommendationBundleEasyAdd.tablet), + ...transformThemeComponent('recommendationBundleList', recommendationBundleList.tablet), + ...transformThemeComponent('recommendationBundleVertical', recommendationBundleVertical.tablet), + ...transformThemeComponent('recommendationGrid', recommendationGrid.tablet), + ...transformThemeComponent('recommendationEmail', recommendationEmail.tablet), + ...transformThemeComponent('search', search.tablet), + ...transformThemeComponent('searchBoca', searchBoca.tablet), + ...transformThemeComponent('searchSnapnco', searchSnapnco.tablet), + ...transformThemeComponent('searchSnappy', searchSnappy.tablet), + ...transformThemeComponent('searchHorizontal', searchHorizontal.tablet), + }, + desktop: { + ...transformThemeComponent('autocompleteTemplate', autocompleteTemplate.desktop), + ...transformThemeComponent('recommendation', recommendation.desktop), + ...transformThemeComponent('recommendationBundle', recommendationBundle.desktop), + ...transformThemeComponent('recommendationBundleEasyAdd', recommendationBundleEasyAdd.desktop), + ...transformThemeComponent('recommendationBundleList', recommendationBundleList.desktop), + ...transformThemeComponent('recommendationBundleVertical', recommendationBundleVertical.desktop), + ...transformThemeComponent('recommendationGrid', recommendationGrid.desktop), + ...transformThemeComponent('recommendationEmail', recommendationEmail.desktop), + ...transformThemeComponent('search', search.desktop), + ...transformThemeComponent('searchBoca', searchBoca.desktop), + ...transformThemeComponent('searchSnapnco', searchSnapnco.desktop), + ...transformThemeComponent('searchSnappy', searchSnappy.desktop), + ...transformThemeComponent('searchHorizontal', searchHorizontal.desktop), + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts new file mode 100644 index 000000000..2edd96171 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { RecommendationProps } from '../../../../components/Templates/Recommendation'; +import { recommendationThemeComponentProps } from '../../../themeComponents/recommendation'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Recommendation component +const recommendationStyleScript = ({ theme }: RecommendationProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Recommendation component props come from Template export +export const recommendation: ThemeComponent<'recommendation', RecommendationProps> = { + default: { + props: { + ...recommendationThemeComponentProps.default?.props, + themeStyleScript: recommendationStyleScript, + }, + components: recommendationThemeComponentProps.default?.components, + }, + mobile: recommendationThemeComponentProps.mobile, + desktop: recommendationThemeComponentProps.desktop, + tablet: recommendationThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts new file mode 100644 index 000000000..def550f3d --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts @@ -0,0 +1,29 @@ +import { css } from '@emotion/react'; +import type { RecommendationBundleProps } from '../../../../components/Templates/RecommendationBundle'; +import { recommendationBundleThemeComponentProps } from '../../../themeComponents/recommendationBundle'; +import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; + +// CSS in JS style script for the RecommendationBundle component +const recommendationBundleStyleScript = ({ theme }: any) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// RecommendationBundle component props come from Template export +export const recommendationBundle: ThemeComponent<'recommendationBundle', RecommendationBundleProps> = { + default: { + props: { + ...recommendationBundleThemeComponentProps.default?.props, + themeStyleScript: recommendationBundleStyleScript, + separatorIcon: customVariables.icons.plus, + ctaIcon: customVariables.icons.bag, + }, + components: recommendationBundleThemeComponentProps.default?.components, + }, + mobile: recommendationBundleThemeComponentProps.mobile, + desktop: recommendationBundleThemeComponentProps.desktop, + tablet: recommendationBundleThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts new file mode 100644 index 000000000..735060e52 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { RecommendationBundleEasyAddProps } from '../../../../components/Templates/RecommendationBundleEasyAdd'; +import { recommendationBundleEasyAddThemeComponentProps } from '../../../themeComponents/recommendationBundleEasyAdd'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the RecommendationBundle component +const recommendationBundleEasyAddStyleScript = ({ theme }: RecommendationBundleEasyAddProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// RecommendationBundleEasyAdd component props come from Template export +export const recommendationBundleEasyAdd: ThemeComponent<'recommendationBundleEasyAdd', RecommendationBundleEasyAddProps> = { + default: { + props: { + ...recommendationBundleEasyAddThemeComponentProps.default?.props, + themeStyleScript: recommendationBundleEasyAddStyleScript, + }, + components: recommendationBundleEasyAddThemeComponentProps.default?.components, + }, + mobile: recommendationBundleEasyAddThemeComponentProps.mobile, + desktop: recommendationBundleEasyAddThemeComponentProps.desktop, + tablet: recommendationBundleEasyAddThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleList.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleList.ts new file mode 100644 index 000000000..aa987c3b5 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleList.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { RecommendationBundleListProps } from '../../../../components/Templates/RecommendationBundleList'; +import { recommendationBundleListThemeComponentProps } from '../../../themeComponents/recommendationBundleList'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the RecommendationBundle component +const recommendationBundleListStyleScript = ({ theme }: RecommendationBundleListProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// RecommendationBundleList component props come from Template export +export const recommendationBundleList: ThemeComponent<'recommendationBundleList', RecommendationBundleListProps> = { + default: { + props: { + ...recommendationBundleListThemeComponentProps.default?.props, + themeStyleScript: recommendationBundleListStyleScript, + }, + components: recommendationBundleListThemeComponentProps.default?.components, + }, + mobile: recommendationBundleListThemeComponentProps.mobile, + desktop: recommendationBundleListThemeComponentProps.desktop, + tablet: recommendationBundleListThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts new file mode 100644 index 000000000..a849609e9 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { RecommendationBundleVerticalProps } from '../../../../components/Templates/RecommendationBundleVertical'; +import { recommendationBundleVerticalThemeComponentProps } from '../../../themeComponents/recommendationBundleVertical'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the RecommendationBundle component +const recommendationBundleVerticalStyleScript = ({ theme }: RecommendationBundleVerticalProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// RecommendationBundleVertical component props come from Template export +export const recommendationBundleVertical: ThemeComponent<'recommendationBundleVertical', RecommendationBundleVerticalProps> = { + default: { + props: { + ...recommendationBundleVerticalThemeComponentProps.default?.props, + themeStyleScript: recommendationBundleVerticalStyleScript, + }, + components: recommendationBundleVerticalThemeComponentProps.default?.components, + }, + mobile: recommendationBundleVerticalThemeComponentProps.mobile, + desktop: recommendationBundleVerticalThemeComponentProps.desktop, + tablet: recommendationBundleVerticalThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationEmail.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationEmail.ts new file mode 100644 index 000000000..f610ea66d --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationEmail.ts @@ -0,0 +1,28 @@ +import { css } from '@emotion/react'; +import type { RecommendationEmailProps } from '../../../../components/Templates/RecommendationEmail'; +import { recommendationEmailThemeComponentProps } from '../../../themeComponents/recommendationEmail'; +import { ThemeComponent } from '../../../../providers'; + +console.log(recommendationEmailThemeComponentProps); + +// CSS in JS style script for the Email component +const emailStyleScripts = ({ theme }: RecommendationEmailProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Facet component props +export const recommendationEmail: ThemeComponent<'recommendationEmail', RecommendationEmailProps> = { + default: { + props: { + ...recommendationEmailThemeComponentProps.default?.props, + themeStyleScript: emailStyleScripts, + }, + components: recommendationEmailThemeComponentProps.default?.components, + }, + mobile: recommendationEmailThemeComponentProps.mobile, + desktop: recommendationEmailThemeComponentProps.desktop, + tablet: recommendationEmailThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts new file mode 100644 index 000000000..5b5b2ee4b --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { RecommendationGridProps } from '../../../../components/Templates/RecommendationGrid'; +import { recommendationGridThemeComponentProps } from '../../../themeComponents/recommendationGrid'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the RecommendationBundle component +const recommendationGridStyleScript = ({ theme }: RecommendationGridProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// RecommendationGrid component props come from Template export +export const recommendationGrid: ThemeComponent<'recommendationGrid', RecommendationGridProps> = { + default: { + props: { + ...recommendationGridThemeComponentProps.default?.props, + themeStyleScript: recommendationGridStyleScript, + }, + components: recommendationGridThemeComponentProps.default?.components, + }, + mobile: recommendationGridThemeComponentProps.mobile, + desktop: recommendationGridThemeComponentProps.desktop, + tablet: recommendationGridThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts new file mode 100644 index 000000000..41cd26fff --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts @@ -0,0 +1,31 @@ +import { css } from '@emotion/react'; +import type { SearchProps } from '../../../../components/Templates/Search'; +import { searchThemeComponentProps } from '../../../themeComponents/search'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Search component +const searchStyleScript = ({ theme }: SearchProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Search component props come from Template export +export const search: ThemeComponent<'search', SearchProps> = { + default: { + props: { + ...searchThemeComponentProps.default?.props, + themeStyleScript: searchStyleScript, + }, + components: { + ...searchThemeComponentProps.default?.components, + '*search button.sidebar-toggle': { + icon: 'heart', + }, + }, + }, + mobile: searchThemeComponentProps.mobile, + desktop: searchThemeComponentProps.desktop, + tablet: searchThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts new file mode 100644 index 000000000..40186f8e6 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts @@ -0,0 +1,31 @@ +import { css } from '@emotion/react'; +import type { SearchBocaProps } from '../../../../components/Templates/SearchBoca'; +import { searchBocaThemeComponentProps } from '../../../themeComponents/searchBoca'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Search component +const searchBocaStyleScript = ({ theme }: SearchBocaProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Search component props come from Template export +export const searchBoca: ThemeComponent<'searchBoca', SearchBocaProps> = { + default: { + props: { + ...searchBocaThemeComponentProps.default?.props, + themeStyleScript: searchBocaStyleScript, + }, + components: { + ...searchBocaThemeComponentProps.default?.components, + '*searchBoca button.sidebar-toggle': { + icon: 'heart', + }, + }, + }, + mobile: searchBocaThemeComponentProps.mobile, + desktop: searchBocaThemeComponentProps.desktop, + tablet: searchBocaThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts new file mode 100644 index 000000000..57a461e08 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { SearchHorizontalProps } from '../../../../components/Templates/SearchHorizontal'; +import { searchHorizontalThemeComponentProps } from '../../../themeComponents/searchHorizontal'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Search component +const searchHorizontalStyleScript = ({ theme }: SearchHorizontalProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Search component props come from Template export +export const searchHorizontal: ThemeComponent<'searchHorizontal', SearchHorizontalProps> = { + default: { + props: { + ...searchHorizontalThemeComponentProps.default?.props, + themeStyleScript: searchHorizontalStyleScript, + }, + components: searchHorizontalThemeComponentProps.default?.components, + }, + mobile: searchHorizontalThemeComponentProps.mobile, + desktop: searchHorizontalThemeComponentProps.desktop, + tablet: searchHorizontalThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts new file mode 100644 index 000000000..e9d3b1bd2 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { SearchSnapncoProps } from '../../../../components/Templates/SearchSnapnco'; +import { searchSnapncoThemeComponentProps } from '../../../themeComponents/searchSnapnco'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Search component +const searchSnapncoStyleScript = ({ theme }: SearchSnapncoProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Search component props come from Template export +export const searchSnapnco: ThemeComponent<'searchSnapnco', SearchSnapncoProps> = { + default: { + props: { + ...searchSnapncoThemeComponentProps.default?.props, + themeStyleScript: searchSnapncoStyleScript, + }, + components: searchSnapncoThemeComponentProps.default?.components, + }, + mobile: searchSnapncoThemeComponentProps.mobile, + desktop: searchSnapncoThemeComponentProps.desktop, + tablet: searchSnapncoThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts new file mode 100644 index 000000000..3a3d79777 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts @@ -0,0 +1,26 @@ +import { css } from '@emotion/react'; +import type { SearchSnappyProps } from '../../../../components/Templates/SearchSnappy'; +import { searchSnappyThemeComponentProps } from '../../../themeComponents/searchSnappy'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Search component +const searchSnappyStyleScript = ({ theme }: SearchSnappyProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// Search component props come from Template export +export const searchSnappy: ThemeComponent<'searchSnappy', SearchSnappyProps> = { + default: { + props: { + ...searchSnappyThemeComponentProps.default?.props, + themeStyleScript: searchSnappyStyleScript, + }, + components: searchSnappyThemeComponentProps.default?.components, + }, + mobile: searchSnappyThemeComponentProps.mobile, + desktop: searchSnappyThemeComponentProps.desktop, + tablet: searchSnappyThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts new file mode 100644 index 000000000..4076bfc3a --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -0,0 +1,30 @@ +import { IconType } from '../../components/Atoms/Icon'; + +export const customVariables: { + colors: { + [color: string]: string; + }; + icons: { + [icon: string]: IconType; + }; +} = { + colors: { + white: '#ffffff', + black: '#000000', + gray: '#f8f8f8', + gray02: '#ebebeb', + }, + icons: { + arrowLeft: 'chevron-left', + arrowRight: 'chevron-right', + arrowDown: 'chevron-down', + arrowUp: 'chevron-up', + bag: 'bag', + check: 'square', + close: 'close', + minus: 'minus', + plus: 'plus', + filter: 'filter', + sort: 'sort', + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/pike.ts b/packages/snap-preact/components/src/themes/pike/pike.ts new file mode 100644 index 000000000..13b1db133 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/pike.ts @@ -0,0 +1,24 @@ +import { ThemeComplete, ThemeVariables } from '../../providers'; +import { components } from './components'; +import { responsive } from './responsive'; + +const pikeVariables: ThemeVariables = { + breakpoints: { + mobile: 767, + tablet: 991, + desktop: 1199, + }, + colors: { + text: '#515151', + primary: '#1d4990', + secondary: '#00aeef', + accent: '#2154a5', + }, +}; + +export const pike: Partial = { + name: 'pike', + variables: pikeVariables, + components, + responsive, +}; diff --git a/packages/snap-preact/components/src/themes/pike/responsive.ts b/packages/snap-preact/components/src/themes/pike/responsive.ts new file mode 100644 index 000000000..61ae08bfe --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/responsive.ts @@ -0,0 +1,8 @@ +import { ThemeResponsive } from '../../providers/theme'; +import { mobileComponents, tabletComponents, desktopComponents } from './components'; + +export const responsive: ThemeResponsive = { + mobile: mobileComponents, + tablet: tabletComponents, + desktop: desktopComponents, +}; From 6a9f71688ed22d964a27318c9ee30a0e8702d9eb Mon Sep 17 00:00:00 2001 From: adria Date: Fri, 16 May 2025 17:50:28 -0600 Subject: [PATCH 003/118] working on pike --- .../themes/pike/components/atoms/button.ts | 30 ++++++- .../themes/pike/components/atoms/dropdown.ts | 4 +- .../src/themes/pike/components/atoms/icon.ts | 11 ++- .../src/themes/pike/components/atoms/image.ts | 4 +- .../pike/components/atoms/loadingBar.ts | 4 +- .../src/themes/pike/components/atoms/price.ts | 4 +- .../pike/components/atoms/searchHeader.ts | 4 +- .../themes/pike/components/atoms/skeleton.ts | 4 +- .../pike/components/molecules/carousel.ts | 4 +- .../pike/components/molecules/checkbox.ts | 30 ++++++- .../pike/components/molecules/errorHandler.ts | 4 +- .../components/molecules/facetGridOptions.ts | 4 +- .../molecules/facetHierarchyOptions.ts | 4 +- .../components/molecules/facetListOptions.ts | 4 +- .../molecules/facetPaletteOptions.ts | 4 +- .../pike/components/molecules/facetSlider.ts | 4 +- .../pike/components/molecules/filter.ts | 4 +- .../themes/pike/components/molecules/grid.ts | 4 +- .../components/molecules/layoutSelector.ts | 4 +- .../themes/pike/components/molecules/list.ts | 4 +- .../pike/components/molecules/loadMore.ts | 4 +- .../pike/components/molecules/overlayBadge.ts | 4 +- .../pike/components/molecules/pagination.ts | 4 +- .../pike/components/molecules/perPage.ts | 4 +- .../themes/pike/components/molecules/radio.ts | 4 +- .../pike/components/molecules/radioList.ts | 4 +- .../pike/components/molecules/rating.ts | 4 +- .../pike/components/molecules/result.ts | 4 +- .../pike/components/molecules/searchInput.ts | 4 +- .../pike/components/molecules/select.ts | 85 ++++++++++++++++++- .../pike/components/molecules/slideout.ts | 4 +- .../pike/components/molecules/sortBy.ts | 4 +- .../pike/components/molecules/swatches.ts | 4 +- .../themes/pike/components/molecules/terms.ts | 4 +- .../components/molecules/variantSelection.ts | 4 +- .../themes/pike/components/organisms/facet.ts | 22 ++++- .../components/organisms/facetsHorizontal.ts | 4 +- .../components/organisms/filterSummary.ts | 4 +- .../components/organisms/mobileSidebar.ts | 6 +- .../pike/components/organisms/noResults.ts | 4 +- .../pike/components/organisms/sidebar.ts | 4 +- .../pike/components/organisms/termsList.ts | 4 +- .../pike/components/organisms/toolbar.ts | 5 +- .../templates/autocompleteTemplate.ts | 4 +- .../components/templates/recommendation.ts | 4 +- .../templates/recommendationBundle.ts | 4 +- .../templates/recommendationBundleEasyAdd.ts | 4 +- .../templates/recommendationBundleList.ts | 4 +- .../templates/recommendationBundleVertical.ts | 4 +- .../templates/recommendationEmail.ts | 12 --- .../templates/recommendationGrid.ts | 4 +- .../pike/components/templates/search.ts | 16 +++- .../pike/components/templates/searchBoca.ts | 16 +++- .../components/templates/searchHorizontal.ts | 21 ++++- .../components/templates/searchSnapnco.ts | 21 ++++- .../pike/components/templates/searchSnappy.ts | 21 ++++- .../components/src/themes/pike/custom.ts | 19 ++++- .../components/src/themes/pike/pike.ts | 4 +- 58 files changed, 349 insertions(+), 142 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index 45644647f..71bf0d9d9 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -1,13 +1,37 @@ import { css } from '@emotion/react'; import type { ButtonProps } from '../../../../components/Atoms/Button'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the Button component -const buttonStyleScript = ({ theme }: ButtonProps) => { +const buttonStyleScript = (props: ButtonProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; + const buttonColor = new Color(props?.backgroundColor || variables?.colors?.primary); + const fontColor = buttonColor.isDark() || buttonColor.hex() == '#00AEEF' ? Color('#ffffff') : Color('#000000'); - return css({}); + return css({ + padding: `0 ${customVariables.sizes.spacing}px`, + borderColor: buttonColor.hex(), + color: fontColor.hex(), + fontWeight: customVariables.fonts.weight01, + textAlign: 'center', + height: `${customVariables.sizes.height}px`, + lineHeight: `${customVariables.sizes.height}px`, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { + backgroundColor: buttonColor.hex() || customVariables.colors.black, + }, + '&.ss__button--disabled': { + opacity: 0.65, + '&, .ss__button__content, .ss__icon': { + cursor: 'not-allowed', + }, + }, + }); }; // Button component props diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts index f26eaec5d..37520d82b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts @@ -3,9 +3,9 @@ import type { DropdownProps } from '../../../../components/Atoms/Dropdown'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Dropdown component -const dropdownStyleScript = ({ theme }: DropdownProps) => { +const dropdownStyleScript = (props: DropdownProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts index e9a7367c0..bb70f22b0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts @@ -1,13 +1,17 @@ import { css } from '@emotion/react'; import type { IconProps } from '../../../../components/Atoms/Icon'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the Icon component -const iconStyleScript = ({ theme }: IconProps) => { +const iconStyleScript = (props: IconProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; - return css({}); + return css({ + fill: 'currentColor', + stroke: 'currentColor', + }); }; // Icon component props @@ -15,6 +19,7 @@ export const icon: ThemeComponent<'icon', IconProps> = { default: { props: { themeStyleScript: iconStyleScript, + size: customVariables.sizes.icons, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts index ae520c279..e2fdfef26 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts @@ -3,9 +3,9 @@ import type { ImageProps } from '../../../../components/Atoms/Image'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Image component -const imageStyleScript = ({ theme }: ImageProps & { visibility: React.CSSProperties['visibility'] }) => { +const imageStyleScript = (props: ImageProps & { visibility: React.CSSProperties['visibility'] }) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts index cf5a781c0..9b86103e1 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts @@ -3,9 +3,9 @@ import type { LoadingBarProps } from '../../../../components/Atoms/Loading'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the LoadingBar component -const loadingBarStyleScript = ({ theme }: LoadingBarProps) => { +const loadingBarStyleScript = (props: LoadingBarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts index df3c2e516..10ee1c2a5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts @@ -3,9 +3,9 @@ import type { PriceProps } from '../../../../components/Atoms/Price'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Price component -const priceStyleScript = ({ theme }: PriceProps) => { +const priceStyleScript = (props: PriceProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts index ff2d6de53..a1832fdf6 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts @@ -3,9 +3,9 @@ import type { SearchHeaderProps } from '../../../../components/Atoms/SearchHeade import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the SearchHeader component -const searchHeaderStyleScript = ({ theme }: SearchHeaderProps) => { +const searchHeaderStyleScript = (props: SearchHeaderProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts index 813d43244..4e85796c2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts @@ -3,9 +3,9 @@ import type { SkeletonProps } from '../../../../components/Atoms/Skeleton'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Skeleton component -const skeletonStyleScript = ({ theme }: SkeletonProps) => { +const skeletonStyleScript = (props: SkeletonProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts index 735f97e95..27ea7db15 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts @@ -4,9 +4,9 @@ import { ThemeComponent } from '../../../../providers'; import { customVariables } from '../../custom'; // CSS in JS style script for the Carousel component -const carouselStyleScript = ({ theme }: CarouselProps) => { +const carouselStyleScript = (props: CarouselProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index 9a5a073bb..0c0ca7ab3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -2,13 +2,36 @@ import { css } from '@emotion/react'; import type { CheckboxProps } from '../../../../components/Molecules/Checkbox'; import { ThemeComponent } from '../../../../providers'; import { customVariables } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the Checkbox component -const checkboxStyleScript = ({ theme }: CheckboxProps) => { +const checkboxStyleScript = (props: CheckboxProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; + const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); + const activeBorderColor = new Color(customVariables.colors.gray02).darken(0.055); + const borderColor = props?.checked ? activeBorderColor.hex() : customVariables.colors.gray02; - return css({}); + return css( + props?.native + ? { + position: 'relative', + top: '-1px', + width: '16px', + height: '16px', + border: `1px solid ${customVariables.colors.gray02}`, + } + : { + position: 'relative', + top: '-1px', + backgroundColor: backgroundColor.hex(), + borderColor: borderColor, + '.ss__icon': { + width: '8px', + height: '8px', + }, + } + ); }; // Checkbox component props @@ -17,6 +40,7 @@ export const checkbox: ThemeComponent<'checkbox', CheckboxProps> = { props: { themeStyleScript: checkboxStyleScript, icon: customVariables.icons.check, + size: '14px', }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts index 861c875a0..a57892505 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts @@ -3,9 +3,9 @@ import type { ErrorHandlerProps } from '../../../../components/Molecules/ErrorHa import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the ErrorHandler component -const errorHandlerStyleScript = ({ theme }: ErrorHandlerProps) => { +const errorHandlerStyleScript = (props: ErrorHandlerProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 3e23ed291..35d20b856 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -3,9 +3,9 @@ import type { FacetGridOptionsProps } from '../../../../components/Molecules/Fac import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the FacetGridOptions component -const facetGridOptionsStyleScript = ({ theme }: FacetGridOptionsProps) => { +const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index 1b19447ce..7853fe011 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -3,9 +3,9 @@ import type { FacetHierarchyOptionsProps } from '../../../../components/Molecule import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the FacetHierarchyOptions component -const facetHierarchyOptionsStyleScript = ({ theme }: FacetHierarchyOptionsProps) => { +const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 1629d2a71..8e89a10b4 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -3,9 +3,9 @@ import type { FacetListOptionsProps } from '../../../../components/Molecules/Fac import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the FacetListOptions component -const facetListOptionsStyleScript = ({ theme }: FacetListOptionsProps) => { +const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 683654fd9..dcc31e9c1 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -3,9 +3,9 @@ import type { FacetPaletteOptionsProps } from '../../../../components/Molecules/ import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the FacetPaletteOptions component -const facetPaletteStyleScript = ({ theme }: FacetPaletteOptionsProps) => { +const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index 6ede68012..909cb77ec 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -3,9 +3,9 @@ import type { FacetSliderProps } from '../../../../components/Molecules/FacetSli import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the FacetSlider component -const facetSliderStyleScript = ({ theme }: FacetSliderProps) => { +const facetSliderStyleScript = (props: FacetSliderProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts index 33b3c3328..e2a7e660a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts @@ -3,9 +3,9 @@ import type { FilterProps } from '../../../../components/Molecules/Filter'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Filter component -const filterStyleScript = ({ theme }: FilterProps) => { +const filterStyleScript = (props: FilterProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts index fc9ff8cd5..cc4a5c7c2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts @@ -3,9 +3,9 @@ import type { GridProps } from '../../../../components/Molecules/Grid'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Grid component -const gridStyleScript = ({ theme }: Partial) => { +const gridStyleScript = (props: Partial) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts index ee671378c..e82316c4f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -3,9 +3,9 @@ import type { LayoutSelectorProps } from '../../../../components/Molecules/Layou import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the LayoutSelector component -const layoutSelectorStyleScript = ({ theme }: LayoutSelectorProps) => { +const layoutSelectorStyleScript = (props: LayoutSelectorProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts index e3ff38a13..13945223e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -3,9 +3,9 @@ import type { ListProps } from '../../../../components/Molecules/List'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the List component -const listStyleScript = ({ theme }: ListProps) => { +const listStyleScript = (props: ListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts index bc875ecb5..bb74be4dd 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts @@ -3,9 +3,9 @@ import type { LoadMoreProps } from '../../../../components/Molecules/LoadMore'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the LoadMore component -const loadMoreStyleScript = ({ theme }: LoadMoreProps) => { +const loadMoreStyleScript = (props: LoadMoreProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts index 63cd12042..4554c25e3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts @@ -3,9 +3,9 @@ import type { OverlayBadgeProps } from '../../../../components/Molecules/Overlay import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Search component -const overlayBadgeStyleScript = ({ theme }: OverlayBadgeProps) => { +const overlayBadgeStyleScript = (props: OverlayBadgeProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts index 6d7d3a60b..9eb1ef1ed 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts @@ -3,9 +3,9 @@ import type { PaginationProps } from '../../../../components/Molecules/Paginatio import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Pagination component -const paginationStyleScript = ({ theme }: PaginationProps) => { +const paginationStyleScript = (props: PaginationProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/perPage.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/perPage.ts index eab331672..e7564b97a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/perPage.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/perPage.ts @@ -3,9 +3,9 @@ import type { PerPageProps } from '../../../../components/Molecules/PerPage'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the PerPage component -const perPageStyleScript = ({ theme }: PerPageProps) => { +const perPageStyleScript = (props: PerPageProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index c9674f025..739393860 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -3,9 +3,9 @@ import type { RadioProps } from '../../../../components/Molecules/Radio'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Radio component -const radioStyleScript = ({ theme }: RadioProps) => { +const radioStyleScript = (props: RadioProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts index 7022d2a14..5f5c0daf2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -3,9 +3,9 @@ import type { RadioListProps } from '../../../../components/Molecules/RadioList' import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the RadioList component -const radioListStyleScript = ({ theme }: RadioListProps) => { +const radioListStyleScript = (props: RadioListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts index 1e0ec87f3..9a64603e0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts @@ -3,9 +3,9 @@ import type { RatingProps } from '../../../../components/Molecules/Rating'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Rating component -const ratingStyleScript = ({ theme }: RatingProps) => { +const ratingStyleScript = (props: RatingProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts index 49e929a9d..33eabf1b5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts @@ -3,9 +3,9 @@ import type { ResultProps } from '../../../../components/Molecules/Result'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Result component -const resultStyleScript = ({ theme }: ResultProps) => { +const resultStyleScript = (props: ResultProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index b1f9f379f..8acaa2bec 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -3,9 +3,9 @@ import type { SearchInputProps } from '../../../../components/Molecules/SearchIn import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the SearchInput component -const searchInputStyleScript = ({ theme }: SearchInputProps) => { +const searchInputStyleScript = (props: SearchInputProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 015d97379..04134d136 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -2,13 +2,90 @@ import { css } from '@emotion/react'; import type { SelectProps } from '../../../../components/Molecules/Select'; import { ThemeComponent } from '../../../../providers'; import { customVariables } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the Select component -const selectStyleScript = ({ theme }: SelectProps) => { +const selectStyleScript = (props: SelectProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; + const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); + const spacingHalf = customVariables.sizes.spacing / 2; + const spacingQuarter = customVariables.sizes.spacing / 4; - return css({}); + return css( + props?.native + ? { + flexFlow: 'row nowrap', + display: 'flex', + alignItems: 'center', + gap: `${spacingQuarter}px`, + padding: `0 ${spacingHalf}px`, + height: `${customVariables.sizes.height}px`, + lineHeight: `${customVariables.sizes.height}px`, + border: `1px solid ${customVariables.colors.gray02}`, + color: variables?.colors?.text, + '&, .ss__select__select': { + backgroundColor: backgroundColor.hex(), + }, + '.ss__select__label': { + fontWeight: customVariables.fonts.weight01, + }, + '.ss__select__select': { + border: `1px solid ${backgroundColor.hex()}`, + color: 'inherit', + cursor: 'pointer', + }, + } + : { + '.ss__dropdown__button .ss__button, .ss__dropdown__content .ss__select__select': { + backgroundColor: backgroundColor.hex(), + border: `1px solid ${customVariables.colors.gray02}`, + color: variables?.colors?.text, + }, + '.ss__dropdown__button': { + '.ss__button': { + padding: `0 ${spacingHalf}px`, + '.ss__select__selection': { + paddingRight: `${spacingQuarter}px`, + fontWeight: 'normal', + }, + '.ss__select__dropdown__button__icon': { + transition: 'transform ease .5s', + }, + }, + }, + '.ss__dropdown__content': { + marginTop: `${spacingHalf}px`, + '.ss__select__select': { + padding: `${spacingHalf}px`, + '.ss__select__select__option': { + padding: 0, + margin: `0 0 ${spacingQuarter}px 0`, + color: 'inherit', + '&:last-child': { + marginButtom: '0', + }, + '&:hover': { + backgroundColor: 'transparent', + }, + }, + '.ss__select__select__option--selected': { + fontWeight: customVariables.fonts.weight01, + color: variables?.colors?.primary || customVariables.colors.black, + }, + }, + }, + '.ss__dropdown--open': { + '.ss__dropdown__button': { + '.ss__button': { + '.ss__select__dropdown__button__icon': { + transform: 'rotate(180deg)', + }, + }, + }, + }, + } + ); }; // Select component props @@ -17,7 +94,7 @@ export const select: ThemeComponent<'select', SelectProps> = { props: { themeStyleScript: selectStyleScript, iconOpen: customVariables?.icons?.arrowDown, - iconClose: customVariables.icons.arrowUp, + iconClose: customVariables.icons.arrowDown, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts index bae2c8c4b..c78303100 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts @@ -3,9 +3,9 @@ import type { SlideoutProps } from '../../../../components/Molecules/Slideout'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Slideout component -const slideoutStyleScript = ({ theme }: SlideoutProps & { isActive: boolean }) => { +const slideoutStyleScript = (props: SlideoutProps & { isActive: boolean }) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/sortBy.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/sortBy.ts index 125b78efb..efb530cae 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/sortBy.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/sortBy.ts @@ -3,9 +3,9 @@ import type { SortByProps } from '../../../../components/Molecules/SortBy'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the SortBy component -const sortByStyleScript = ({ theme }: SortByProps) => { +const sortByStyleScript = (props: SortByProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts index 2ff9c52df..be1de9d6e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts @@ -3,9 +3,9 @@ import type { SwatchesProps } from '../../../../components/Molecules/Swatches'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Swatches component -const swatchesStyleScript = ({ theme }: SwatchesProps) => { +const swatchesStyleScript = (props: SwatchesProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts index 1587e60ca..3e182088f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts @@ -3,9 +3,9 @@ import type { TermsProps } from '../../../../components/Molecules/Terms'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Terms component -const termsStyleScript = ({ theme }: TermsProps) => { +const termsStyleScript = (props: TermsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts index 5f40c226d..6040f9450 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts @@ -3,9 +3,9 @@ import type { VariantSelectionProps } from '../../../../components/Molecules/Var import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Swatches component -const variantSelectionStyleScript = ({ theme }: VariantSelectionProps) => { +const variantSelectionStyleScript = (props: VariantSelectionProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index c7bd2aa63..9258675c0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -4,11 +4,25 @@ import { ThemeComponent } from '../../../../providers'; import { customVariables } from '../../custom'; // CSS in JS style script for the Facet component -const facetStyleScript = ({ theme }: FacetProps) => { +const facetStyleScript = (props: FacetProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; - return css({}); + return css({ + '&.ss__facet--collapsed': { + '.ss__facet__header': { + '.ss__icon': { + transform: 'rotate(0deg)', + }, + }, + }, + '.ss__facet__header': { + '.ss__icon': { + transition: 'transform ease .5s', + transform: 'rotate(180deg)', + }, + }, + }); }; // Facet component props @@ -16,7 +30,7 @@ export const facet: ThemeComponent<'facet', FacetProps> = { default: { props: { themeStyleScript: facetStyleScript, - iconCollapse: customVariables.icons.arrowUp, + iconCollapse: customVariables.icons.arrowDown, iconExpand: customVariables.icons.arrowDown, iconOverflowMore: customVariables.icons.plus, iconOverflowLess: customVariables.icons.minus, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts index 2c8855982..80c8dd9d8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts @@ -3,9 +3,9 @@ import type { FacetsHorizontalProps } from '../../../../components/Organisms/Fac import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Facets component -const facetsHorizontalStyleScript = ({ theme }: FacetsHorizontalProps) => { +const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts index 5aabe30a6..895663508 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts @@ -4,9 +4,9 @@ import { ThemeComponent } from '../../../../providers'; import { customVariables } from '../../custom'; // CSS in JS style script for the FilterSummary component -const filterSummaryStyleScript = ({ theme }: FilterSummaryProps) => { +const filterSummaryStyleScript = (props: FilterSummaryProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index 3e7a7d3e8..57a87bcb2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -4,9 +4,9 @@ import { ThemeComponent } from '../../../../providers'; import { customVariables } from '../../custom'; // CSS in JS style script for the MobileSidebar component -const mobileSidebarStyleScript = ({ theme }: MobileSidebarProps) => { +const mobileSidebarStyleScript = (props: MobileSidebarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; @@ -20,7 +20,7 @@ export const mobileSidebar: ThemeComponent<'mobileSidebar', MobileSidebarProps> }, components: { '*mobileSidebar button.close': { - icon: 'heart', + icon: customVariables.icons.close, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts index bf08e5558..faf68ba38 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts @@ -3,9 +3,9 @@ import type { NoResultsProps } from '../../../../components/Organisms/NoResults' import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the NoResults component -const noResultsStyleScript = ({ theme }: NoResultsProps) => { +const noResultsStyleScript = (props: NoResultsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts index c6b3a1042..d951c0b63 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts @@ -3,9 +3,9 @@ import type { SidebarProps } from '../../../../components/Organisms/Sidebar'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Sidebar component -const sidebarStyleScript = ({ theme }: SidebarProps) => { +const sidebarStyleScript = (props: SidebarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts index 17cfce0f3..46cc31ec6 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts @@ -3,9 +3,9 @@ import type { TermsListProps } from '../../../../components/Organisms/TermsList' import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Terms component -const termsListStyleScript = ({ theme }: TermsListProps) => { +const termsListStyleScript = (props: TermsListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts index 67002e969..f9b9364cf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts @@ -3,7 +3,10 @@ import { ThemeComponent } from '../../../../providers'; import { ToolbarProps } from '../../../../components/Organisms/Toolbar'; // CSS in JS style script for the Toolbar component -const toolbarStyleScript = () => { +const toolbarStyleScript = (props: ToolbarProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts index f97ce46b7..84d76e758 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts @@ -4,9 +4,9 @@ import { autocompleteThemeComponentProps } from '../../../themeComponents/autoco import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Search component -const autocompleteTemplateStyleScript = ({ theme }: AutocompleteTemplateProps) => { +const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts index 2edd96171..45fdcf15f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts @@ -4,9 +4,9 @@ import { recommendationThemeComponentProps } from '../../../themeComponents/reco import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Recommendation component -const recommendationStyleScript = ({ theme }: RecommendationProps) => { +const recommendationStyleScript = (props: RecommendationProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts index def550f3d..314faa9fe 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts @@ -5,9 +5,9 @@ import { ThemeComponent } from '../../../../providers'; import { customVariables } from '../../custom'; // CSS in JS style script for the RecommendationBundle component -const recommendationBundleStyleScript = ({ theme }: any) => { +const recommendationBundleStyleScript = (props: RecommendationBundleProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts index 735060e52..745b888eb 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts @@ -4,9 +4,9 @@ import { recommendationBundleEasyAddThemeComponentProps } from '../../../themeCo import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the RecommendationBundle component -const recommendationBundleEasyAddStyleScript = ({ theme }: RecommendationBundleEasyAddProps) => { +const recommendationBundleEasyAddStyleScript = (props: RecommendationBundleEasyAddProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleList.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleList.ts index aa987c3b5..34132fd1a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleList.ts @@ -4,9 +4,9 @@ import { recommendationBundleListThemeComponentProps } from '../../../themeCompo import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the RecommendationBundle component -const recommendationBundleListStyleScript = ({ theme }: RecommendationBundleListProps) => { +const recommendationBundleListStyleScript = (props: RecommendationBundleListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts index a849609e9..5e41ecf9b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts @@ -4,9 +4,9 @@ import { recommendationBundleVerticalThemeComponentProps } from '../../../themeC import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the RecommendationBundle component -const recommendationBundleVerticalStyleScript = ({ theme }: RecommendationBundleVerticalProps) => { +const recommendationBundleVerticalStyleScript = (props: RecommendationBundleVerticalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationEmail.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationEmail.ts index f610ea66d..7d5c2ac51 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationEmail.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationEmail.ts @@ -1,24 +1,12 @@ -import { css } from '@emotion/react'; import type { RecommendationEmailProps } from '../../../../components/Templates/RecommendationEmail'; import { recommendationEmailThemeComponentProps } from '../../../themeComponents/recommendationEmail'; import { ThemeComponent } from '../../../../providers'; -console.log(recommendationEmailThemeComponentProps); - -// CSS in JS style script for the Email component -const emailStyleScripts = ({ theme }: RecommendationEmailProps) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; - - return css({}); -}; - // Facet component props export const recommendationEmail: ThemeComponent<'recommendationEmail', RecommendationEmailProps> = { default: { props: { ...recommendationEmailThemeComponentProps.default?.props, - themeStyleScript: emailStyleScripts, }, components: recommendationEmailThemeComponentProps.default?.components, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts index 5b5b2ee4b..22fb34b50 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts @@ -4,9 +4,9 @@ import { recommendationGridThemeComponentProps } from '../../../themeComponents/ import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the RecommendationBundle component -const recommendationGridStyleScript = ({ theme }: RecommendationGridProps) => { +const recommendationGridStyleScript = (props: RecommendationGridProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; return css({}); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts index 41cd26fff..dae710fe2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts @@ -2,13 +2,21 @@ import { css } from '@emotion/react'; import type { SearchProps } from '../../../../components/Templates/Search'; import { searchThemeComponentProps } from '../../../themeComponents/search'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the Search component -const searchStyleScript = ({ theme }: SearchProps) => { +const searchStyleScript = (props: SearchProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__button--sidebar-toggle-button-wrapper .ss__button': { + '.ss__icon': { + width: '16px', + height: '16px', + }, + }, + }); }; // Search component props come from Template export @@ -21,7 +29,7 @@ export const search: ThemeComponent<'search', SearchProps> = { components: { ...searchThemeComponentProps.default?.components, '*search button.sidebar-toggle': { - icon: 'heart', + icon: customVariables.icons.filter, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts index 40186f8e6..40e1f45bf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts @@ -2,13 +2,21 @@ import { css } from '@emotion/react'; import type { SearchBocaProps } from '../../../../components/Templates/SearchBoca'; import { searchBocaThemeComponentProps } from '../../../themeComponents/searchBoca'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the Search component -const searchBocaStyleScript = ({ theme }: SearchBocaProps) => { +const searchBocaStyleScript = (props: SearchBocaProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__button--sidebar-toggle-button-wrapper .ss__button': { + '.ss__icon': { + width: '16px', + height: '16px', + }, + }, + }); }; // Search component props come from Template export @@ -21,7 +29,7 @@ export const searchBoca: ThemeComponent<'searchBoca', SearchBocaProps> = { components: { ...searchBocaThemeComponentProps.default?.components, '*searchBoca button.sidebar-toggle': { - icon: 'heart', + icon: customVariables.icons.filter, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts index 57a461e08..1efcd4437 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts @@ -2,13 +2,21 @@ import { css } from '@emotion/react'; import type { SearchHorizontalProps } from '../../../../components/Templates/SearchHorizontal'; import { searchHorizontalThemeComponentProps } from '../../../themeComponents/searchHorizontal'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the Search component -const searchHorizontalStyleScript = ({ theme }: SearchHorizontalProps) => { +const searchHorizontalStyleScript = (props: SearchHorizontalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__button--sidebar-toggle-button-wrapper .ss__button': { + '.ss__icon': { + width: '16px', + height: '16px', + }, + }, + }); }; // Search component props come from Template export @@ -18,7 +26,12 @@ export const searchHorizontal: ThemeComponent<'searchHorizontal', SearchHorizont ...searchHorizontalThemeComponentProps.default?.props, themeStyleScript: searchHorizontalStyleScript, }, - components: searchHorizontalThemeComponentProps.default?.components, + components: { + ...searchHorizontalThemeComponentProps.default?.components, + '*searchHorizontal button.sidebar-toggle': { + icon: customVariables.icons.filter, + }, + }, }, mobile: searchHorizontalThemeComponentProps.mobile, desktop: searchHorizontalThemeComponentProps.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts index e9d3b1bd2..f67deb1b0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts @@ -2,13 +2,21 @@ import { css } from '@emotion/react'; import type { SearchSnapncoProps } from '../../../../components/Templates/SearchSnapnco'; import { searchSnapncoThemeComponentProps } from '../../../themeComponents/searchSnapnco'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the Search component -const searchSnapncoStyleScript = ({ theme }: SearchSnapncoProps) => { +const searchSnapncoStyleScript = (props: SearchSnapncoProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__button--sidebar-toggle-button-wrapper .ss__button': { + '.ss__icon': { + width: '16px', + height: '16px', + }, + }, + }); }; // Search component props come from Template export @@ -18,7 +26,12 @@ export const searchSnapnco: ThemeComponent<'searchSnapnco', SearchSnapncoProps> ...searchSnapncoThemeComponentProps.default?.props, themeStyleScript: searchSnapncoStyleScript, }, - components: searchSnapncoThemeComponentProps.default?.components, + components: { + ...searchSnapncoThemeComponentProps.default?.components, + '*searchSnapnco button.sidebar-toggle': { + icon: customVariables.icons.filter, + }, + }, }, mobile: searchSnapncoThemeComponentProps.mobile, desktop: searchSnapncoThemeComponentProps.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts index 3a3d79777..236bbb23f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts @@ -2,13 +2,21 @@ import { css } from '@emotion/react'; import type { SearchSnappyProps } from '../../../../components/Templates/SearchSnappy'; import { searchSnappyThemeComponentProps } from '../../../themeComponents/searchSnappy'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the Search component -const searchSnappyStyleScript = ({ theme }: SearchSnappyProps) => { +const searchSnappyStyleScript = (props: SearchSnappyProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = theme?.variables; + const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__button--sidebar-toggle-button-wrapper .ss__button': { + '.ss__icon': { + width: '16px', + height: '16px', + }, + }, + }); }; // Search component props come from Template export @@ -18,7 +26,12 @@ export const searchSnappy: ThemeComponent<'searchSnappy', SearchSnappyProps> = { ...searchSnappyThemeComponentProps.default?.props, themeStyleScript: searchSnappyStyleScript, }, - components: searchSnappyThemeComponentProps.default?.components, + components: { + ...searchSnappyThemeComponentProps.default?.components, + '*searchSnappy button.sidebar-toggle': { + icon: customVariables.icons.filter, + }, + }, }, mobile: searchSnappyThemeComponentProps.mobile, desktop: searchSnappyThemeComponentProps.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 4076bfc3a..ad913b1a2 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -4,16 +4,28 @@ export const customVariables: { colors: { [color: string]: string; }; + fonts: { + [font: string]: any; + }; icons: { [icon: string]: IconType; }; + sizes: { + [size: string]: any; + }; } = { colors: { white: '#ffffff', black: '#000000', - gray: '#f8f8f8', + gray01: '#f8f8f8', gray02: '#ebebeb', }, + fonts: { + weight01: 700, // main font weight + weight02: 700, // header font weight + style: false, + transform: false, + }, icons: { arrowLeft: 'chevron-left', arrowRight: 'chevron-right', @@ -27,4 +39,9 @@ export const customVariables: { filter: 'filter', sort: 'sort', }, + sizes: { + height: 35, // refers to height for button and dropdown sizes + icons: 12, + spacing: 20, + }, }; diff --git a/packages/snap-preact/components/src/themes/pike/pike.ts b/packages/snap-preact/components/src/themes/pike/pike.ts index 13b1db133..62cf5f17e 100644 --- a/packages/snap-preact/components/src/themes/pike/pike.ts +++ b/packages/snap-preact/components/src/themes/pike/pike.ts @@ -10,8 +10,8 @@ const pikeVariables: ThemeVariables = { }, colors: { text: '#515151', - primary: '#1d4990', - secondary: '#00aeef', + primary: '#00aeef', + secondary: '#1d4990', accent: '#2154a5', }, }; From ef0512e198cfd37cbd95ff2644ca0b4c4f5f8ded Mon Sep 17 00:00:00 2001 From: adria Date: Sat, 17 May 2025 18:26:16 -0600 Subject: [PATCH 004/118] fix linting --- .../snap-preact-demo/templates/src/index.ts | 14 +- .../themes/pike/components/atoms/button.ts | 56 ++++-- .../pike/components/atoms/loadingBar.ts | 7 +- .../src/themes/pike/components/atoms/price.ts | 11 +- .../pike/components/atoms/searchHeader.ts | 27 ++- .../themes/pike/components/atoms/skeleton.ts | 2 + .../pike/components/molecules/checkbox.ts | 66 ++++--- .../components/molecules/layoutSelector.ts | 62 ++++++- .../themes/pike/components/molecules/radio.ts | 58 +++++- .../pike/components/molecules/select.ts | 170 ++++++++++-------- .../components/organisms/mobileSidebar.ts | 3 + .../pike/components/templates/search.ts | 12 +- .../pike/components/templates/searchBoca.ts | 12 +- .../components/templates/searchHorizontal.ts | 12 +- .../components/templates/searchSnapnco.ts | 12 +- .../pike/components/templates/searchSnappy.ts | 12 +- .../components/src/themes/pike/custom.ts | 14 +- 17 files changed, 379 insertions(+), 171 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index ee506142c..182c3c20b 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -11,12 +11,12 @@ let config: SnapTemplatesConfig = { currency: 'usd', platform: 'other', }, - components: { - result: { - CustomResult: async () => (await import('./components/Result')).CustomResult, - EmailResult: async () => (await import('./components/Result')).CustomResult, - }, - }, + // components: { + // result: { + // CustomResult: async () => (await import('./components/Result')).CustomResult, + // EmailResult: async () => (await import('./components/Result')).CustomResult, + // }, + // }, themes: { global: { extends: 'pike', @@ -42,7 +42,7 @@ let config: SnapTemplatesConfig = { email: { Email: { component: 'RecommendationEmail', - resultComponent: 'EmailResult', + //resultComponent: 'EmailResult', }, }, default: { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index 71bf0d9d9..4ec7f6ba2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -9,29 +9,53 @@ const buttonStyleScript = (props: ButtonProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const buttonColor = new Color(props?.backgroundColor || variables?.colors?.primary); - const fontColor = buttonColor.isDark() || buttonColor.hex() == '#00AEEF' ? Color('#ffffff') : Color('#000000'); + const fontColor = + buttonColor.isDark() || buttonColor.hex() == '#00AEEF' ? Color(customVariables.colors.white) : Color(customVariables.colors.black); - return css({ - padding: `0 ${customVariables.sizes.spacing}px`, - borderColor: buttonColor.hex(), - color: fontColor.hex(), - fontWeight: customVariables.fonts.weight01, - textAlign: 'center', - height: `${customVariables.sizes.height}px`, - lineHeight: `${customVariables.sizes.height}px`, - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { - backgroundColor: buttonColor.hex() || customVariables.colors.black, - }, + // shared button styles + const disabledStyles = css({ '&.ss__button--disabled': { opacity: 0.65, - '&, .ss__button__content, .ss__icon': { + '&, & *': { cursor: 'not-allowed', }, }, }); + + // default styles + const defaultStyles = css([ + { + padding: `0 ${customVariables.spacing.x4}px`, + borderColor: buttonColor.hex(), + color: fontColor.hex(), + fontWeight: customVariables.fonts.weight01, + textAlign: 'center', + height: `${customVariables.sizes.height}px`, + lineHeight: `${customVariables.sizes.height}px`, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { + backgroundColor: buttonColor.hex() || customVariables.colors.black, + }, + }, + disabledStyles, + ]); + + // native styles + const nativeStyles = css([ + { + cursor: 'pointer', + border: `1px solid ${customVariables.colors.gray02}`, + '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { + color: variables?.colors?.text, + backgroundColor: customVariables.colors.white, + }, + }, + disabledStyles, + ]); + + return props?.native ? nativeStyles : defaultStyles; }; // Button component props diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts index 9b86103e1..02dc00cb1 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/loadingBar.ts @@ -7,7 +7,12 @@ const loadingBarStyleScript = (props: LoadingBarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + background: variables?.colors?.primary, + '.ss__loading-bar__bar': { + background: variables?.colors?.secondary, + }, + }); }; // LoadingBar component props diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts index 10ee1c2a5..b7ff7c857 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts @@ -1,13 +1,22 @@ import { css } from '@emotion/react'; import type { PriceProps } from '../../../../components/Atoms/Price'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the Price component const priceStyleScript = (props: PriceProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + color: props?.name == 'price' ? variables?.colors?.primary : variables?.colors?.text, + '&:not(.ss__price--strike)': { + fontWeight: customVariables.fonts.weight01, + }, + '&.ss__price--strike': { + color: variables?.colors?.text, + }, + }); }; // Price component props diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts index a1832fdf6..7a5da9042 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts @@ -1,13 +1,32 @@ import { css } from '@emotion/react'; import type { SearchHeaderProps } from '../../../../components/Atoms/SearchHeader'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the SearchHeader component const searchHeaderStyleScript = (props: SearchHeaderProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + h3: { + margin: 0, + fontWeight: customVariables.fonts.weight02, + color: variables?.colors?.secondary, + }, + h5: { + margin: `${customVariables.spacing.x2}px 0 0 0`, + fontSize: '16px', + fontWeight: 400, + color: variables?.colors?.text, + }, + em: { + fontStyle: 'normal', + }, + '.ss__query': { + color: variables?.colors?.primary, + }, + }); }; // SearchHeader component props @@ -15,7 +34,11 @@ export const searchHeader: ThemeComponent<'searchHeader', SearchHeaderProps> = { default: { props: { themeStyleScript: searchHeaderStyleScript, - titleText: (data) => `Search Results${data.search?.query?.string ? ` for "${data.search.query.string}"` : ''}`, + titleText: (data) => { + const search = data?.search; + const query = search?.query?.string ? ` for "${search.query.string}"` : ``; + return search?.matchType == 'expanded' ? `We couldn't find an exact match${query}, but here's something similar:` : `Search Results${query}`; + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts index 4e85796c2..878e07763 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts @@ -1,6 +1,7 @@ import { css } from '@emotion/react'; import type { SkeletonProps } from '../../../../components/Atoms/Skeleton'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; // CSS in JS style script for the Skeleton component const skeletonStyleScript = (props: SkeletonProps) => { @@ -15,6 +16,7 @@ export const skeleton: ThemeComponent<'skeleton', SkeletonProps> = { default: { props: { themeStyleScript: skeletonStyleScript, + backgroundColor: customVariables.colors.gray02, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index 0c0ca7ab3..851b73196 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -9,29 +9,53 @@ const checkboxStyleScript = (props: CheckboxProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); + const borderColor = new Color(customVariables.colors.gray02); + const activeIconColor = new Color(variables?.colors?.primary || customVariables.colors.black); const activeBorderColor = new Color(customVariables.colors.gray02).darken(0.055); - const borderColor = props?.checked ? activeBorderColor.hex() : customVariables.colors.gray02; - return css( - props?.native - ? { - position: 'relative', - top: '-1px', - width: '16px', - height: '16px', - border: `1px solid ${customVariables.colors.gray02}`, - } - : { - position: 'relative', - top: '-1px', - backgroundColor: backgroundColor.hex(), - borderColor: borderColor, - '.ss__icon': { - width: '8px', - height: '8px', - }, - } - ); + // shared checkbox styles + const sharedStyles = css({ + position: 'relative', + top: '-1px', + }); + const disabledStyles = css({ + '&.ss__checkbox--disabled': { + opacity: 0.65, + '&, & *': { + cursor: 'not-allowed', + }, + }, + }); + + // default styles + const defaultStyles = css([ + sharedStyles, + { + backgroundColor: backgroundColor.hex(), + border: `1px solid ${props?.checked ? activeBorderColor.hex() : borderColor.hex()}`, + '.ss__icon': { + width: '8px', + height: '8px', + fill: props?.checked ? activeIconColor.hex() : '', + stroke: props?.checked ? activeIconColor.hex() : '', + }, + }, + disabledStyles, + ]); + + // native styles + const nativeStyles = css([ + sharedStyles, + { + width: '16px', + height: '16px', + border: `1px solid ${customVariables.colors.gray02}`, + cursor: 'pointer', + }, + disabledStyles, + ]); + + return props?.native ? nativeStyles : defaultStyles; }; // Checkbox component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts index e82316c4f..fceaef743 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -1,13 +1,54 @@ import { css } from '@emotion/react'; import type { LayoutSelectorProps } from '../../../../components/Molecules/LayoutSelector'; import { ThemeComponent } from '../../../../providers'; +// import { customVariables } from '../../custom'; +// import Color from 'color'; // CSS in JS style script for the LayoutSelector component const layoutSelectorStyleScript = (props: LayoutSelectorProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + //const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); + //const activeColor = new Color(variables?.colors?.primary || customVariables.colors.black); + //const activeIconColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color('#ffffff') : Color('#000000'); - return css({}); + return css({ + // '&.ss__list' : { + // '.ss__list__options': { + // '.ss__list__option': { + // border: `1px solid ${customVariables.colors.gray02}`, + // backgroundColor: backgroundColor.hex(), + // height: `${customVariables.sizes.height}px`, + // lineHeight: `${customVariables.sizes.height}px`, + // padding: `0 ${customVariables.spacing.x2}px`, + // }, + // '.ss__list__option--selected': { + // borderColor: activeColor.hex(), + // backgroundColor: activeColor.hex(), + // color: activeIconColor.hex(), + // '&, *': { + // cursor: 'text', + // } + // } + // } + // }, + // '&.ss__radio-list': { + // '.ss__radio-list__options-wrapper': { + // display: 'flex', + // flexFlow: 'row wrap', + // alignItems: 'center', + // gap: `${customVariables.spacing.x2}px`, + // '.ss__radio-list__option': { + // padding: 0, + // } + // }, + // }, + // '&.ss__select': { + // '.ss__select__dropdown .ss__select__dropdown__button .ss__button__content': { + // gap: `${customVariables.spacing.x2}px`, + // }, + // }, + }); }; // LayoutSelector component props @@ -15,6 +56,25 @@ export const layoutSelector: ThemeComponent<'layoutSelector', LayoutSelectorProp default: { props: { themeStyleScript: layoutSelectorStyleScript, + type: 'radio', + }, + components: { + // '*layoutSelector list': { + // hideTitleText: true, + // }, + // '*layoutSelector list icon': { + // size: '16px', + // }, + // '*layoutSelector radioList': { + // hideTitleText: true, + // }, + // '*layoutSelector radioList icon': { + // size: '16px', + // }, + // '*layoutSelector select': { + // hideSelection: true, + // separator: '', + // }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index 739393860..37143b258 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -1,13 +1,62 @@ import { css } from '@emotion/react'; import type { RadioProps } from '../../../../components/Molecules/Radio'; import { ThemeComponent } from '../../../../providers'; +import { customVariables } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the Radio component const radioStyleScript = (props: RadioProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); + const borderColor = new Color(customVariables.colors.gray02); + const activeIconColor = new Color(variables?.colors?.primary || customVariables.colors.black); + const activeBorderColor = new Color(customVariables.colors.gray02).darken(0.055); - return css({}); + // shared radio styles + const disabledStyles = css({ + '&.ss__radio--disabled': { + opacity: 0.65, + '&, & *': { + cursor: 'not-allowed', + }, + }, + }); + + // default styles + const defaultStyles = css([ + { + position: 'relative', + top: '-1px', + backgroundColor: backgroundColor.hex(), + border: `1px solid ${props?.checked ? activeBorderColor.hex() : borderColor.hex()}`, + '&, & .ss__icon': { + borderRadius: '50%', + }, + '.ss__icon': { + display: props?.checked ? '' : 'none', + fill: props?.checked ? activeIconColor.hex() : '', + stroke: props?.checked ? activeIconColor.hex() : '', + }, + }, + disabledStyles, + ]); + + // native styles + const nativeStyles = css([ + { + lineHeight: 0, + '.ss__radio__input': { + width: '16px', + height: '16px', + border: `1px solid ${customVariables.colors.gray02}`, + cursor: 'pointer', + }, + }, + disabledStyles, + ]); + + return props?.native ? nativeStyles : defaultStyles; }; // Radio component props @@ -15,6 +64,13 @@ export const radio: ThemeComponent<'radio', RadioProps> = { default: { props: { themeStyleScript: radioStyleScript, + size: '14px', + }, + components: { + '*radio icon': { + icon: 'square', + size: '8px', + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 04134d136..a7778997a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -2,90 +2,102 @@ import { css } from '@emotion/react'; import type { SelectProps } from '../../../../components/Molecules/Select'; import { ThemeComponent } from '../../../../providers'; import { customVariables } from '../../custom'; -import Color from 'color'; +//import Color from 'color'; // CSS in JS style script for the Select component const selectStyleScript = (props: SelectProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); - const spacingHalf = customVariables.sizes.spacing / 2; - const spacingQuarter = customVariables.sizes.spacing / 4; + // const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); + // const nativeIcon = ``; - return css( - props?.native - ? { - flexFlow: 'row nowrap', - display: 'flex', - alignItems: 'center', - gap: `${spacingQuarter}px`, - padding: `0 ${spacingHalf}px`, - height: `${customVariables.sizes.height}px`, - lineHeight: `${customVariables.sizes.height}px`, - border: `1px solid ${customVariables.colors.gray02}`, - color: variables?.colors?.text, - '&, .ss__select__select': { - backgroundColor: backgroundColor.hex(), - }, - '.ss__select__label': { - fontWeight: customVariables.fonts.weight01, - }, - '.ss__select__select': { - border: `1px solid ${backgroundColor.hex()}`, - color: 'inherit', - cursor: 'pointer', - }, - } - : { - '.ss__dropdown__button .ss__button, .ss__dropdown__content .ss__select__select': { - backgroundColor: backgroundColor.hex(), - border: `1px solid ${customVariables.colors.gray02}`, - color: variables?.colors?.text, - }, - '.ss__dropdown__button': { - '.ss__button': { - padding: `0 ${spacingHalf}px`, - '.ss__select__selection': { - paddingRight: `${spacingQuarter}px`, - fontWeight: 'normal', - }, - '.ss__select__dropdown__button__icon': { - transition: 'transform ease .5s', - }, - }, - }, - '.ss__dropdown__content': { - marginTop: `${spacingHalf}px`, - '.ss__select__select': { - padding: `${spacingHalf}px`, - '.ss__select__select__option': { - padding: 0, - margin: `0 0 ${spacingQuarter}px 0`, - color: 'inherit', - '&:last-child': { - marginButtom: '0', - }, - '&:hover': { - backgroundColor: 'transparent', - }, - }, - '.ss__select__select__option--selected': { - fontWeight: customVariables.fonts.weight01, - color: variables?.colors?.primary || customVariables.colors.black, - }, - }, - }, - '.ss__dropdown--open': { - '.ss__dropdown__button': { - '.ss__button': { - '.ss__select__dropdown__button__icon': { - transform: 'rotate(180deg)', - }, - }, - }, - }, - } - ); + // // shared styles for select menus + // const sharedStyles = css({ + // border: `1px solid ${customVariables.colors.gray02}`, + // color: variables?.colors?.text, + // backgroundColor: backgroundColor.hex(), + // }); + + return css({}); + + // return css( + // props?.native ? { + // // display: 'flex', + // // flexFlow: 'row nowrap', + // // alignItems: 'center', + // // gap: `${customVariables.spacing.x1}px`, + // // padding: `0 ${customVariables.spacing.x4 + customVariables.sizes.icons}px 0 ${customVariables.spacing.x2}px`, + // // height: `${customVariables.sizes.height}px`, + // // lineHeight: `${customVariables.sizes.height}px`, + // // ...sharedStyles, + // // backgroundImage: `url(data:image/svg+xml;base64,${btoa(nativeIcon)})`, + // // backgroundPosition: `right ${customVariables.spacing.x2}px center`, + // // backgroundRepeat: 'no-repeat', + // // backgroundSize: `${customVariables.sizes.icons}px ${customVariables.sizes.icons}px`, + // // '.ss__select__label': { + // // fontWeight: customVariables.fonts.weight01, + // // }, + // // '.ss__select__select': { + // // backgroundColor: 'transparent', + // // border: 'none', + // // appearance: 'none', + // // color: 'inherit', + // // cursor: 'pointer', + // // '&[disabled]': { + // // cursor: 'not-allowed', + // // }, + // // '&::-ms-expand': { + // // display: 'none', + // // } + // // }, + // } : { + // // '.ss__dropdown__button .ss__button, .ss__dropdown__content .ss__select__select': { + // // ...sharedStyles, + // // }, + // // '.ss__dropdown__button': { + // // '.ss__button': { + // // padding: `0 ${customVariables.spacing.x2}px`, + // // '.ss__select__selection': { + // // paddingRight: `${customVariables.spacing.x1}px`, + // // fontWeight: 'normal', + // // }, + // // '.ss__select__dropdown__button__icon': { + // // transition: 'transform ease .5s', + // // }, + // // }, + // // }, + // // '.ss__dropdown__content': { + // // marginTop: `${customVariables.spacing.x2}px`, + // // '.ss__select__select': { + // // padding: `${customVariables.spacing.x2}px`, + // // '.ss__select__select__option': { + // // padding: 0, + // // margin: `0 0 ${customVariables.spacing.x1}px 0`, + // // color: 'inherit', + // // '&:last-child': { + // // marginButtom: '0', + // // }, + // // '&:hover': { + // // backgroundColor: 'transparent', + // // }, + // // }, + // // '.ss__select__select__option--selected': { + // // fontWeight: customVariables.fonts.weight01, + // // color: variables?.colors?.primary || customVariables.colors.black, + // // }, + // // }, + // // }, + // // '.ss__dropdown--open': { + // // '.ss__dropdown__button': { + // // '.ss__button': { + // // '.ss__select__dropdown__button__icon': { + // // transform: 'rotate(180deg)', + // // }, + // // }, + // // }, + // // }, + // } + // ); }; // Select component props @@ -93,7 +105,7 @@ export const select: ThemeComponent<'select', SelectProps> = { default: { props: { themeStyleScript: selectStyleScript, - iconOpen: customVariables?.icons?.arrowDown, + iconOpen: customVariables.icons.arrowDown, iconClose: customVariables.icons.arrowDown, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index 57a87bcb2..1659b46f1 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -22,6 +22,9 @@ export const mobileSidebar: ThemeComponent<'mobileSidebar', MobileSidebarProps> '*mobileSidebar button.close': { icon: customVariables.icons.close, }, + '*mobileSidebar button.slideout icon': { + size: '16px', + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts index dae710fe2..ac69e2598 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts @@ -9,14 +9,7 @@ const searchStyleScript = (props: SearchProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({ - '.ss__button--sidebar-toggle-button-wrapper .ss__button': { - '.ss__icon': { - width: '16px', - height: '16px', - }, - }, - }); + return css({}); }; // Search component props come from Template export @@ -31,6 +24,9 @@ export const search: ThemeComponent<'search', SearchProps> = { '*search button.sidebar-toggle': { icon: customVariables.icons.filter, }, + '*search button.sidebar-toggle icon': { + size: '16px', + }, }, }, mobile: searchThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts index 40e1f45bf..f136e09d7 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts @@ -9,14 +9,7 @@ const searchBocaStyleScript = (props: SearchBocaProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({ - '.ss__button--sidebar-toggle-button-wrapper .ss__button': { - '.ss__icon': { - width: '16px', - height: '16px', - }, - }, - }); + return css({}); }; // Search component props come from Template export @@ -31,6 +24,9 @@ export const searchBoca: ThemeComponent<'searchBoca', SearchBocaProps> = { '*searchBoca button.sidebar-toggle': { icon: customVariables.icons.filter, }, + '*searchBoca button.sidebar-toggle icon': { + size: '16px', + }, }, }, mobile: searchBocaThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts index 1efcd4437..2eb1126e8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts @@ -9,14 +9,7 @@ const searchHorizontalStyleScript = (props: SearchHorizontalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({ - '.ss__button--sidebar-toggle-button-wrapper .ss__button': { - '.ss__icon': { - width: '16px', - height: '16px', - }, - }, - }); + return css({}); }; // Search component props come from Template export @@ -31,6 +24,9 @@ export const searchHorizontal: ThemeComponent<'searchHorizontal', SearchHorizont '*searchHorizontal button.sidebar-toggle': { icon: customVariables.icons.filter, }, + '*searchHorizontal button.sidebar-toggle icon': { + size: '16px', + }, }, }, mobile: searchHorizontalThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts index f67deb1b0..230aae893 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts @@ -9,14 +9,7 @@ const searchSnapncoStyleScript = (props: SearchSnapncoProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({ - '.ss__button--sidebar-toggle-button-wrapper .ss__button': { - '.ss__icon': { - width: '16px', - height: '16px', - }, - }, - }); + return css({}); }; // Search component props come from Template export @@ -31,6 +24,9 @@ export const searchSnapnco: ThemeComponent<'searchSnapnco', SearchSnapncoProps> '*searchSnapnco button.sidebar-toggle': { icon: customVariables.icons.filter, }, + '*searchSnapnco button.sidebar-toggle icon': { + size: '16px', + }, }, }, mobile: searchSnapncoThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts index 236bbb23f..f105703ec 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts @@ -9,14 +9,7 @@ const searchSnappyStyleScript = (props: SearchSnappyProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({ - '.ss__button--sidebar-toggle-button-wrapper .ss__button': { - '.ss__icon': { - width: '16px', - height: '16px', - }, - }, - }); + return css({}); }; // Search component props come from Template export @@ -31,6 +24,9 @@ export const searchSnappy: ThemeComponent<'searchSnappy', SearchSnappyProps> = { '*searchSnappy button.sidebar-toggle': { icon: customVariables.icons.filter, }, + '*searchSnappy button.sidebar-toggle icon': { + size: '16px', + }, }, }, mobile: searchSnappyThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index ad913b1a2..9c9a2bd9f 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -13,6 +13,9 @@ export const customVariables: { sizes: { [size: string]: any; }; + spacing: { + [size: string]: number; + }; } = { colors: { white: '#ffffff', @@ -40,8 +43,15 @@ export const customVariables: { sort: 'sort', }, sizes: { - height: 35, // refers to height for button and dropdown sizes + height: 33, // refers to height for button and dropdown sizes icons: 12, - spacing: 20, + }, + spacing: { + x1: 5, + x2: 10, + x3: 15, + x4: 20, + x5: 25, + x6: 30, }, }; From 53591c1299b0b33465f7134790221bea76be6721 Mon Sep 17 00:00:00 2001 From: adria Date: Sun, 18 May 2025 11:21:42 -0600 Subject: [PATCH 005/118] styling for lists and selects --- .../themes/pike/components/atoms/button.ts | 19 +- .../src/themes/pike/components/atoms/icon.ts | 4 +- .../src/themes/pike/components/atoms/price.ts | 4 +- .../pike/components/atoms/searchHeader.ts | 6 +- .../themes/pike/components/atoms/skeleton.ts | 4 +- .../pike/components/molecules/carousel.ts | 6 +- .../pike/components/molecules/checkbox.ts | 14 +- .../components/molecules/layoutSelector.ts | 112 +++++----- .../themes/pike/components/molecules/list.ts | 34 ++- .../themes/pike/components/molecules/radio.ts | 14 +- .../pike/components/molecules/radioList.ts | 34 ++- .../pike/components/molecules/select.ts | 201 ++++++++++-------- .../themes/pike/components/organisms/facet.ts | 10 +- .../components/organisms/filterSummary.ts | 6 +- .../components/organisms/mobileSidebar.ts | 6 +- .../templates/recommendationBundle.ts | 6 +- .../pike/components/templates/search.ts | 4 +- .../pike/components/templates/searchBoca.ts | 4 +- .../components/templates/searchHorizontal.ts | 4 +- .../components/templates/searchSnapnco.ts | 4 +- .../pike/components/templates/searchSnappy.ts | 4 +- .../components/src/themes/pike/custom.ts | 2 +- 22 files changed, 289 insertions(+), 213 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index 4ec7f6ba2..cefc5f42d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { ButtonProps } from '../../../../components/Atoms/Button'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; import Color from 'color'; // CSS in JS style script for the Button component @@ -9,8 +9,7 @@ const buttonStyleScript = (props: ButtonProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const buttonColor = new Color(props?.backgroundColor || variables?.colors?.primary); - const fontColor = - buttonColor.isDark() || buttonColor.hex() == '#00AEEF' ? Color(customVariables.colors.white) : Color(customVariables.colors.black); + const fontColor = buttonColor.isDark() || buttonColor.hex() == '#00AEEF' ? Color(custom.colors.white) : Color(custom.colors.black); // shared button styles const disabledStyles = css({ @@ -25,18 +24,18 @@ const buttonStyleScript = (props: ButtonProps) => { // default styles const defaultStyles = css([ { - padding: `0 ${customVariables.spacing.x4}px`, + padding: `0 ${custom.spacing.x4}px`, borderColor: buttonColor.hex(), color: fontColor.hex(), - fontWeight: customVariables.fonts.weight01, + fontWeight: custom.fonts.weight01, textAlign: 'center', - height: `${customVariables.sizes.height}px`, - lineHeight: `${customVariables.sizes.height}px`, + height: `${custom.sizes.height}px`, + lineHeight: `${custom.sizes.height}px`, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { - backgroundColor: buttonColor.hex() || customVariables.colors.black, + backgroundColor: buttonColor.hex() || custom.colors.black, }, }, disabledStyles, @@ -46,10 +45,10 @@ const buttonStyleScript = (props: ButtonProps) => { const nativeStyles = css([ { cursor: 'pointer', - border: `1px solid ${customVariables.colors.gray02}`, + border: `1px solid ${custom.colors.gray02}`, '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { color: variables?.colors?.text, - backgroundColor: customVariables.colors.white, + backgroundColor: custom.colors.white, }, }, disabledStyles, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts index bb70f22b0..1fefcc025 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { IconProps } from '../../../../components/Atoms/Icon'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Icon component const iconStyleScript = (props: IconProps) => { @@ -19,7 +19,7 @@ export const icon: ThemeComponent<'icon', IconProps> = { default: { props: { themeStyleScript: iconStyleScript, - size: customVariables.sizes.icons, + size: custom.sizes.icons, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts index b7ff7c857..129859859 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { PriceProps } from '../../../../components/Atoms/Price'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Price component const priceStyleScript = (props: PriceProps) => { @@ -11,7 +11,7 @@ const priceStyleScript = (props: PriceProps) => { return css({ color: props?.name == 'price' ? variables?.colors?.primary : variables?.colors?.text, '&:not(.ss__price--strike)': { - fontWeight: customVariables.fonts.weight01, + fontWeight: custom.fonts.weight01, }, '&.ss__price--strike': { color: variables?.colors?.text, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts index 7a5da9042..8319247c8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { SearchHeaderProps } from '../../../../components/Atoms/SearchHeader'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the SearchHeader component const searchHeaderStyleScript = (props: SearchHeaderProps) => { @@ -11,11 +11,11 @@ const searchHeaderStyleScript = (props: SearchHeaderProps) => { return css({ h3: { margin: 0, - fontWeight: customVariables.fonts.weight02, + fontWeight: custom.fonts.weight02, color: variables?.colors?.secondary, }, h5: { - margin: `${customVariables.spacing.x2}px 0 0 0`, + margin: `${custom.spacing.x2}px 0 0 0`, fontSize: '16px', fontWeight: 400, color: variables?.colors?.text, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts index 878e07763..7959b2c3a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { SkeletonProps } from '../../../../components/Atoms/Skeleton'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Skeleton component const skeletonStyleScript = (props: SkeletonProps) => { @@ -16,7 +16,7 @@ export const skeleton: ThemeComponent<'skeleton', SkeletonProps> = { default: { props: { themeStyleScript: skeletonStyleScript, - backgroundColor: customVariables.colors.gray02, + backgroundColor: custom.colors.gray02, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts index 27ea7db15..b31f0da25 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { CarouselProps } from '../../../../components/Molecules/Carousel'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Carousel component const carouselStyleScript = (props: CarouselProps) => { @@ -19,10 +19,10 @@ export const carousel: ThemeComponent<'carousel', CarouselProps> = { }, components: { '*carousel icon.prev': { - icon: customVariables.icons.arrowLeft, + icon: custom.icons.arrowLeft, }, '*carousel icon.next': { - icon: customVariables.icons.arrowRight, + icon: custom.icons.arrowRight, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index 851b73196..08ed7bb33 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -1,17 +1,17 @@ import { css } from '@emotion/react'; import type { CheckboxProps } from '../../../../components/Molecules/Checkbox'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; import Color from 'color'; // CSS in JS style script for the Checkbox component const checkboxStyleScript = (props: CheckboxProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); - const borderColor = new Color(customVariables.colors.gray02); - const activeIconColor = new Color(variables?.colors?.primary || customVariables.colors.black); - const activeBorderColor = new Color(customVariables.colors.gray02).darken(0.055); + const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); + const borderColor = new Color(custom.colors.gray02); + const activeIconColor = new Color(variables?.colors?.primary || custom.colors.black); + const activeBorderColor = new Color(custom.colors.gray02).darken(0.055); // shared checkbox styles const sharedStyles = css({ @@ -49,7 +49,7 @@ const checkboxStyleScript = (props: CheckboxProps) => { { width: '16px', height: '16px', - border: `1px solid ${customVariables.colors.gray02}`, + border: `1px solid ${custom.colors.gray02}`, cursor: 'pointer', }, disabledStyles, @@ -63,7 +63,7 @@ export const checkbox: ThemeComponent<'checkbox', CheckboxProps> = { default: { props: { themeStyleScript: checkboxStyleScript, - icon: customVariables.icons.check, + icon: custom.icons.check, size: '14px', }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts index fceaef743..0c404df71 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -1,54 +1,56 @@ import { css } from '@emotion/react'; import type { LayoutSelectorProps } from '../../../../components/Molecules/LayoutSelector'; import { ThemeComponent } from '../../../../providers'; -// import { customVariables } from '../../custom'; -// import Color from 'color'; +import { custom } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the LayoutSelector component const layoutSelectorStyleScript = (props: LayoutSelectorProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - //const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); - //const activeColor = new Color(variables?.colors?.primary || customVariables.colors.black); - //const activeIconColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color('#ffffff') : Color('#000000'); + const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); + const activeColor = new Color(variables?.colors?.primary || custom.colors.black); + const activeIconColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color('#ffffff') : Color('#000000'); - return css({ - // '&.ss__list' : { - // '.ss__list__options': { - // '.ss__list__option': { - // border: `1px solid ${customVariables.colors.gray02}`, - // backgroundColor: backgroundColor.hex(), - // height: `${customVariables.sizes.height}px`, - // lineHeight: `${customVariables.sizes.height}px`, - // padding: `0 ${customVariables.spacing.x2}px`, - // }, - // '.ss__list__option--selected': { - // borderColor: activeColor.hex(), - // backgroundColor: activeColor.hex(), - // color: activeIconColor.hex(), - // '&, *': { - // cursor: 'text', - // } - // } - // } - // }, - // '&.ss__radio-list': { - // '.ss__radio-list__options-wrapper': { - // display: 'flex', - // flexFlow: 'row wrap', - // alignItems: 'center', - // gap: `${customVariables.spacing.x2}px`, - // '.ss__radio-list__option': { - // padding: 0, - // } - // }, - // }, - // '&.ss__select': { - // '.ss__select__dropdown .ss__select__dropdown__button .ss__button__content': { - // gap: `${customVariables.spacing.x2}px`, - // }, - // }, + // dropdown styles + const dropdownStyles = css({ + '.ss__dropdown': { + '.ss__dropdown__button .ss__button__content': { + gap: `${custom.spacing.x2}px`, + }, + }, }); + + // list styles + const listStyles = css({ + '.ss__list__options': { + display: 'flex', + '.ss__list__option': { + border: `1px solid ${custom.colors.gray02}`, + backgroundColor: backgroundColor.hex(), + height: `${custom.sizes.height}px`, + lineHeight: `${custom.sizes.height}px`, + padding: `0 ${custom.spacing.x2}px`, + margin: 0, + }, + '.ss__list__option--selected': { + borderColor: activeColor.hex(), + backgroundColor: activeColor.hex(), + color: activeIconColor.hex(), + '&, *': { + cursor: 'text', + }, + }, + }, + }); + + if (props?.type == 'dropdown') { + return dropdownStyles; + } else if (props?.type == 'list') { + return listStyles; + } else { + return css({}); + } }; // LayoutSelector component props @@ -56,25 +58,19 @@ export const layoutSelector: ThemeComponent<'layoutSelector', LayoutSelectorProp default: { props: { themeStyleScript: layoutSelectorStyleScript, - type: 'radio', + type: 'list', }, components: { - // '*layoutSelector list': { - // hideTitleText: true, - // }, - // '*layoutSelector list icon': { - // size: '16px', - // }, - // '*layoutSelector radioList': { - // hideTitleText: true, - // }, - // '*layoutSelector radioList icon': { - // size: '16px', - // }, - // '*layoutSelector select': { - // hideSelection: true, - // separator: '', - // }, + '*layoutSelector select': { + hideSelection: true, + separator: '', + }, + '*layoutSelector list': { + hideTitleText: true, + }, + '*layoutSelector radioList': { + hideTitleText: true, + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts index 13945223e..1dc67e053 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -1,13 +1,45 @@ import { css } from '@emotion/react'; import type { ListProps } from '../../../../components/Molecules/List'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the List component const listStyleScript = (props: ListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const activeColor = new Color(variables?.colors?.primary || custom.colors.black); - return css({}); + return css({ + '&, .ss__list__options': { + display: 'block', + }, + '.ss__list__title, .ss__list__options .ss__list__option': { + margin: `0 0 ${custom.spacing.x2}px 0`, + }, + '.ss__list__title': { + display: 'block', + fontSize: '14px', + fontWeight: custom.fonts.weight02, + lineHeight: 1, + }, + '.ss__list__options': { + '.ss__list__option': { + gap: `${custom.spacing.x2}px`, + '.ss__list__option__icon': { + width: '16px', + height: '16px', + }, + '&:last-child': { + marginBottom: 0, + }, + }, + '.ss__list__option--selected': { + fontWeight: custom.fonts.weight01, + color: activeColor.hex(), + }, + }, + }); }; // List component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index 37143b258..57c34811c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -1,17 +1,17 @@ import { css } from '@emotion/react'; import type { RadioProps } from '../../../../components/Molecules/Radio'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; import Color from 'color'; // CSS in JS style script for the Radio component const radioStyleScript = (props: RadioProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); - const borderColor = new Color(customVariables.colors.gray02); - const activeIconColor = new Color(variables?.colors?.primary || customVariables.colors.black); - const activeBorderColor = new Color(customVariables.colors.gray02).darken(0.055); + const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); + const borderColor = new Color(custom.colors.gray02); + const activeIconColor = new Color(variables?.colors?.primary || custom.colors.black); + const activeBorderColor = new Color(custom.colors.gray02).darken(0.055); // shared radio styles const disabledStyles = css({ @@ -26,8 +26,6 @@ const radioStyleScript = (props: RadioProps) => { // default styles const defaultStyles = css([ { - position: 'relative', - top: '-1px', backgroundColor: backgroundColor.hex(), border: `1px solid ${props?.checked ? activeBorderColor.hex() : borderColor.hex()}`, '&, & .ss__icon': { @@ -49,7 +47,7 @@ const radioStyleScript = (props: RadioProps) => { '.ss__radio__input': { width: '16px', height: '16px', - border: `1px solid ${customVariables.colors.gray02}`, + border: `1px solid ${custom.colors.gray02}`, cursor: 'pointer', }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts index 5f5c0daf2..5d30474ec 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -1,13 +1,44 @@ import { css } from '@emotion/react'; import type { RadioListProps } from '../../../../components/Molecules/RadioList'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the RadioList component const radioListStyleScript = (props: RadioListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__radio-list__title, .ss__radio-list__options-wrapper .ss__radio-list__option': { + padding: 0, + margin: `0 0 ${custom.spacing.x2}px 0`, + }, + '.ss__radio-list__title': { + display: 'block', + fontSize: '14px', + fontWeight: custom.fonts.weight02, + lineHeight: 1, + }, + '.ss__radio-list__options-wrapper': { + '.ss__radio-list__option': { + gap: `${custom.spacing.x2}px`, + '.ss__radio-list__option__icon, .ss__radio-list__option__label': { + padding: 0, + }, + '.ss__radio-list__option__icon': { + width: '16px', + height: '16px', + }, + '&:last-child': { + marginBottom: 0, + }, + }, + '.ss__radio-list__option--selected': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary || custom.colors.black, + }, + }, + }); }; // RadioList component props @@ -15,6 +46,7 @@ export const radioList: ThemeComponent<'radioList', RadioListProps> = { default: { props: { themeStyleScript: radioListStyleScript, + hideOptionLabels: false, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index a7778997a..bfefd0a68 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -1,103 +1,117 @@ import { css } from '@emotion/react'; import type { SelectProps } from '../../../../components/Molecules/Select'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; -//import Color from 'color'; +import { custom } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the Select component const selectStyleScript = (props: SelectProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - // const backgroundColor = new Color(customVariables.colors.gray02).lighten(0.055); - // const nativeIcon = ``; + const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); + const nativeIcon = ``; - // // shared styles for select menus - // const sharedStyles = css({ - // border: `1px solid ${customVariables.colors.gray02}`, - // color: variables?.colors?.text, - // backgroundColor: backgroundColor.hex(), - // }); + // shared styles for select menus + const sharedStyles = css({ + border: `1px solid ${custom.colors.gray02}`, + color: variables?.colors?.text, + backgroundColor: backgroundColor.hex(), + }); - return css({}); + // default styles + const defaultStyles = css([ + { + '.ss__dropdown': { + '.ss__dropdown__button .ss__button, .ss__dropdown__content .ss__select__select': { + ...sharedStyles, + }, + '.ss__dropdown__button': { + '.ss__button': { + padding: `0 ${custom.spacing.x2}px`, + '.ss__select__selection': { + paddingRight: `${custom.spacing.x1}px`, + fontWeight: 'normal', + }, + '.ss__select__dropdown__button__icon': { + transition: 'transform ease .5s', + }, + }, + }, + '.ss__dropdown__content': { + marginTop: `${custom.spacing.x2}px`, + '.ss__select__select': { + padding: `${custom.spacing.x2}px`, + '.ss__select__select__option': { + padding: 0, + margin: `0 0 ${custom.spacing.x2}px 0`, + color: 'inherit', + '.ss__icon': { + width: '16px', + height: '16px', + }, + '&:last-child': { + marginBottom: '0', + }, + '&:hover': { + backgroundColor: 'transparent', + }, + }, + '.ss__select__select__option--selected': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary || custom.colors.black, + }, + }, + }, + }, + '.ss__dropdown--open': { + '.ss__dropdown__button': { + '.ss__button': { + '.ss__select__dropdown__button__icon': { + transform: 'rotate(180deg)', + }, + }, + }, + }, + }, + ]); + + // native styles + const nativeStyles = css([ + sharedStyles, + { + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + gap: `${custom.spacing.x1}px`, + padding: `0 ${custom.spacing.x4 + custom.sizes.icons}px 0 ${custom.spacing.x2}px`, + height: `${custom.sizes.height}px`, + lineHeight: `${custom.sizes.height}px`, + backgroundImage: `url(data:image/svg+xml;base64,${btoa(nativeIcon)})`, + backgroundPosition: `right ${custom.spacing.x2}px center`, + backgroundRepeat: 'no-repeat', + backgroundSize: `${custom.sizes.icons}px ${custom.sizes.icons}px`, + '.ss__select__label': { + fontWeight: custom.fonts.weight01, + }, + '.ss__select__select': { + backgroundColor: 'transparent', + border: 'none', + appearance: 'none', + color: 'inherit', + cursor: 'pointer', + '&[disabled]': { + cursor: 'not-allowed', + }, + '&::-ms-expand': { + display: 'none', + }, + }, + }, + ]); - // return css( - // props?.native ? { - // // display: 'flex', - // // flexFlow: 'row nowrap', - // // alignItems: 'center', - // // gap: `${customVariables.spacing.x1}px`, - // // padding: `0 ${customVariables.spacing.x4 + customVariables.sizes.icons}px 0 ${customVariables.spacing.x2}px`, - // // height: `${customVariables.sizes.height}px`, - // // lineHeight: `${customVariables.sizes.height}px`, - // // ...sharedStyles, - // // backgroundImage: `url(data:image/svg+xml;base64,${btoa(nativeIcon)})`, - // // backgroundPosition: `right ${customVariables.spacing.x2}px center`, - // // backgroundRepeat: 'no-repeat', - // // backgroundSize: `${customVariables.sizes.icons}px ${customVariables.sizes.icons}px`, - // // '.ss__select__label': { - // // fontWeight: customVariables.fonts.weight01, - // // }, - // // '.ss__select__select': { - // // backgroundColor: 'transparent', - // // border: 'none', - // // appearance: 'none', - // // color: 'inherit', - // // cursor: 'pointer', - // // '&[disabled]': { - // // cursor: 'not-allowed', - // // }, - // // '&::-ms-expand': { - // // display: 'none', - // // } - // // }, - // } : { - // // '.ss__dropdown__button .ss__button, .ss__dropdown__content .ss__select__select': { - // // ...sharedStyles, - // // }, - // // '.ss__dropdown__button': { - // // '.ss__button': { - // // padding: `0 ${customVariables.spacing.x2}px`, - // // '.ss__select__selection': { - // // paddingRight: `${customVariables.spacing.x1}px`, - // // fontWeight: 'normal', - // // }, - // // '.ss__select__dropdown__button__icon': { - // // transition: 'transform ease .5s', - // // }, - // // }, - // // }, - // // '.ss__dropdown__content': { - // // marginTop: `${customVariables.spacing.x2}px`, - // // '.ss__select__select': { - // // padding: `${customVariables.spacing.x2}px`, - // // '.ss__select__select__option': { - // // padding: 0, - // // margin: `0 0 ${customVariables.spacing.x1}px 0`, - // // color: 'inherit', - // // '&:last-child': { - // // marginButtom: '0', - // // }, - // // '&:hover': { - // // backgroundColor: 'transparent', - // // }, - // // }, - // // '.ss__select__select__option--selected': { - // // fontWeight: customVariables.fonts.weight01, - // // color: variables?.colors?.primary || customVariables.colors.black, - // // }, - // // }, - // // }, - // // '.ss__dropdown--open': { - // // '.ss__dropdown__button': { - // // '.ss__button': { - // // '.ss__select__dropdown__button__icon': { - // // transform: 'rotate(180deg)', - // // }, - // // }, - // // }, - // // }, - // } - // ); + return props?.native ? nativeStyles : defaultStyles; }; // Select component props @@ -105,8 +119,13 @@ export const select: ThemeComponent<'select', SelectProps> = { default: { props: { themeStyleScript: selectStyleScript, - iconOpen: customVariables.icons.arrowDown, - iconClose: customVariables.icons.arrowDown, + iconOpen: custom.icons.arrowDown, + iconClose: custom.icons.arrowDown, + }, + components: { + '*select dropdown button': { + native: false, + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index 9258675c0..31c0ffb2f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { FacetProps } from '../../../../components/Organisms/Facet'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Facet component const facetStyleScript = (props: FacetProps) => { @@ -30,10 +30,10 @@ export const facet: ThemeComponent<'facet', FacetProps> = { default: { props: { themeStyleScript: facetStyleScript, - iconCollapse: customVariables.icons.arrowDown, - iconExpand: customVariables.icons.arrowDown, - iconOverflowMore: customVariables.icons.plus, - iconOverflowLess: customVariables.icons.minus, + iconCollapse: custom.icons.arrowDown, + iconExpand: custom.icons.arrowDown, + iconOverflowMore: custom.icons.plus, + iconOverflowLess: custom.icons.minus, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts index 895663508..33e54572e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { FilterSummaryProps } from '../../../../components/Organisms/FilterSummary'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the FilterSummary component const filterSummaryStyleScript = (props: FilterSummaryProps) => { @@ -16,8 +16,8 @@ export const filterSummary: ThemeComponent<'filterSummary', FilterSummaryProps> default: { props: { themeStyleScript: filterSummaryStyleScript, - clearAllIcon: customVariables.icons.close, - filterIcon: customVariables.icons.close, + clearAllIcon: custom.icons.close, + filterIcon: custom.icons.close, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index 1659b46f1..e2b26a82b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -1,7 +1,7 @@ import { css } from '@emotion/react'; import type { MobileSidebarProps } from '../../../../components/Organisms/MobileSidebar'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the MobileSidebar component const mobileSidebarStyleScript = (props: MobileSidebarProps) => { @@ -16,11 +16,11 @@ export const mobileSidebar: ThemeComponent<'mobileSidebar', MobileSidebarProps> default: { props: { themeStyleScript: mobileSidebarStyleScript, - openButtonIcon: customVariables.icons.filter, + openButtonIcon: custom.icons.filter, }, components: { '*mobileSidebar button.close': { - icon: customVariables.icons.close, + icon: custom.icons.close, }, '*mobileSidebar button.slideout icon': { size: '16px', diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts index 314faa9fe..68c4311a8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import type { RecommendationBundleProps } from '../../../../components/Templates/RecommendationBundle'; import { recommendationBundleThemeComponentProps } from '../../../themeComponents/recommendationBundle'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the RecommendationBundle component const recommendationBundleStyleScript = (props: RecommendationBundleProps) => { @@ -18,8 +18,8 @@ export const recommendationBundle: ThemeComponent<'recommendationBundle', Recomm props: { ...recommendationBundleThemeComponentProps.default?.props, themeStyleScript: recommendationBundleStyleScript, - separatorIcon: customVariables.icons.plus, - ctaIcon: customVariables.icons.bag, + separatorIcon: custom.icons.plus, + ctaIcon: custom.icons.bag, }, components: recommendationBundleThemeComponentProps.default?.components, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts index ac69e2598..1670b9041 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import type { SearchProps } from '../../../../components/Templates/Search'; import { searchThemeComponentProps } from '../../../themeComponents/search'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component const searchStyleScript = (props: SearchProps) => { @@ -22,7 +22,7 @@ export const search: ThemeComponent<'search', SearchProps> = { components: { ...searchThemeComponentProps.default?.components, '*search button.sidebar-toggle': { - icon: customVariables.icons.filter, + icon: custom.icons.filter, }, '*search button.sidebar-toggle icon': { size: '16px', diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts index f136e09d7..8c71f7229 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import type { SearchBocaProps } from '../../../../components/Templates/SearchBoca'; import { searchBocaThemeComponentProps } from '../../../themeComponents/searchBoca'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component const searchBocaStyleScript = (props: SearchBocaProps) => { @@ -22,7 +22,7 @@ export const searchBoca: ThemeComponent<'searchBoca', SearchBocaProps> = { components: { ...searchBocaThemeComponentProps.default?.components, '*searchBoca button.sidebar-toggle': { - icon: customVariables.icons.filter, + icon: custom.icons.filter, }, '*searchBoca button.sidebar-toggle icon': { size: '16px', diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts index 2eb1126e8..5bd215296 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import type { SearchHorizontalProps } from '../../../../components/Templates/SearchHorizontal'; import { searchHorizontalThemeComponentProps } from '../../../themeComponents/searchHorizontal'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component const searchHorizontalStyleScript = (props: SearchHorizontalProps) => { @@ -22,7 +22,7 @@ export const searchHorizontal: ThemeComponent<'searchHorizontal', SearchHorizont components: { ...searchHorizontalThemeComponentProps.default?.components, '*searchHorizontal button.sidebar-toggle': { - icon: customVariables.icons.filter, + icon: custom.icons.filter, }, '*searchHorizontal button.sidebar-toggle icon': { size: '16px', diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts index 230aae893..b6fa50c32 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import type { SearchSnapncoProps } from '../../../../components/Templates/SearchSnapnco'; import { searchSnapncoThemeComponentProps } from '../../../themeComponents/searchSnapnco'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component const searchSnapncoStyleScript = (props: SearchSnapncoProps) => { @@ -22,7 +22,7 @@ export const searchSnapnco: ThemeComponent<'searchSnapnco', SearchSnapncoProps> components: { ...searchSnapncoThemeComponentProps.default?.components, '*searchSnapnco button.sidebar-toggle': { - icon: customVariables.icons.filter, + icon: custom.icons.filter, }, '*searchSnapnco button.sidebar-toggle icon': { size: '16px', diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts index f105703ec..2c9012ac3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import type { SearchSnappyProps } from '../../../../components/Templates/SearchSnappy'; import { searchSnappyThemeComponentProps } from '../../../themeComponents/searchSnappy'; import { ThemeComponent } from '../../../../providers'; -import { customVariables } from '../../custom'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component const searchSnappyStyleScript = (props: SearchSnappyProps) => { @@ -22,7 +22,7 @@ export const searchSnappy: ThemeComponent<'searchSnappy', SearchSnappyProps> = { components: { ...searchSnappyThemeComponentProps.default?.components, '*searchSnappy button.sidebar-toggle': { - icon: customVariables.icons.filter, + icon: custom.icons.filter, }, '*searchSnappy button.sidebar-toggle icon': { size: '16px', diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 9c9a2bd9f..5ccfe5d29 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -1,6 +1,6 @@ import { IconType } from '../../components/Atoms/Icon'; -export const customVariables: { +export const custom: { colors: { [color: string]: string; }; From a3974fb7835381384e1e2cbe581fdc97c19ee04e Mon Sep 17 00:00:00 2001 From: adria Date: Mon, 19 May 2025 17:58:14 -0600 Subject: [PATCH 006/118] facet styles --- .../src/themes/pike/components/atoms/price.ts | 1 + .../pike/components/molecules/checkbox.ts | 4 +- .../molecules/facetHierarchyOptions.ts | 38 ++++++++++++++++++- .../components/molecules/facetListOptions.ts | 27 ++++++++++++- .../themes/pike/components/molecules/list.ts | 4 -- .../themes/pike/components/molecules/radio.ts | 4 +- .../pike/components/molecules/radioList.ts | 4 -- .../pike/components/molecules/searchInput.ts | 28 +++++++++++++- .../pike/components/molecules/select.ts | 7 ++-- .../themes/pike/components/organisms/facet.ts | 37 ++++++++++++++++++ .../components/organisms/mobileSidebar.ts | 3 -- .../pike/components/templates/search.ts | 3 -- .../pike/components/templates/searchBoca.ts | 3 -- .../components/templates/searchHorizontal.ts | 3 -- .../components/templates/searchSnapnco.ts | 3 -- .../pike/components/templates/searchSnappy.ts | 3 -- .../components/src/themes/pike/custom.ts | 2 +- 17 files changed, 136 insertions(+), 38 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts index 129859859..ca7ff0e82 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts @@ -15,6 +15,7 @@ const priceStyleScript = (props: PriceProps) => { }, '&.ss__price--strike': { color: variables?.colors?.text, + opacity: 0.805, }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index 08ed7bb33..755f425ac 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -47,8 +47,8 @@ const checkboxStyleScript = (props: CheckboxProps) => { const nativeStyles = css([ sharedStyles, { - width: '16px', - height: '16px', + width: `${custom.sizes.icon}px`, + height: `${custom.sizes.icon}px`, border: `1px solid ${custom.colors.gray02}`, cursor: 'pointer', }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index 7853fe011..de3c3601e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -1,13 +1,49 @@ import { css } from '@emotion/react'; import type { FacetHierarchyOptionsProps } from '../../../../components/Molecules/FacetHierarchyOptions'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the FacetHierarchyOptions component const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__facet-hierarchy-options__option': { + padding: 0, + margin: `0 0 ${custom.spacing.x2}px 0`, + color: variables?.colors?.text, + '.ss__facet-hierarchy-options__option__value': { + margin: 0, + '.ss__facet-hierarchy-options__option__value__count': { + position: 'relative', + top: '-1px', + margin: `0 0 0 ${custom.spacing.x1}px`, + opacity: 0.805, + }, + }, + '&:last-child': { + marginBottom: 0, + }, + }, + '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--return': { + '&:before': { + position: 'relative', + top: '-2px', + padding: `0 ${custom.spacing.x1}px 0 0`, + fontSize: '24px', + color: 'inherit', + lineHeight: 0, + }, + }, + '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary || custom.colors.black, + '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { + paddingLeft: `${custom.spacing.x6}px`, + }, + }, + }); }; // FacetHierarchyOptions component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 8e89a10b4..453fd3305 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -1,13 +1,38 @@ import { css } from '@emotion/react'; import type { FacetListOptionsProps } from '../../../../components/Molecules/FacetListOptions'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the FacetListOptions component const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const isHorizontal = props?.horizontal ? true : false; - return css({}); + return css({ + gap: isHorizontal ? `${custom.spacing.x2}px` : '', + '.ss__facet-list-options__option': { + padding: 0, + margin: isHorizontal ? `` : `0 0 ${custom.spacing.x2}px 0`, + color: variables?.colors?.text, + '.ss__facet-list-options__option__value': { + margin: props?.hideCheckbox ? `` : `0 0 0 ${custom.spacing.x2}px`, + '.ss__facet-list-options__option__value__count': { + position: 'relative', + top: '-1px', + margin: `0 0 0 ${custom.spacing.x1}px`, + opacity: 0.805, + }, + }, + '&:last-child': { + marginBottom: isHorizontal ? '' : 0, + }, + }, + '.ss__facet-list-options__option.ss__facet-list-options__option--filtered': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary || custom.colors.black, + }, + }); }; // FacetListOptions component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts index 1dc67e053..b40b5b5b5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -26,10 +26,6 @@ const listStyleScript = (props: ListProps) => { '.ss__list__options': { '.ss__list__option': { gap: `${custom.spacing.x2}px`, - '.ss__list__option__icon': { - width: '16px', - height: '16px', - }, '&:last-child': { marginBottom: 0, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index 57c34811c..d08fc3fe1 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -45,8 +45,8 @@ const radioStyleScript = (props: RadioProps) => { { lineHeight: 0, '.ss__radio__input': { - width: '16px', - height: '16px', + width: `${custom.sizes.icon}px`, + height: `${custom.sizes.icon}px`, border: `1px solid ${custom.colors.gray02}`, cursor: 'pointer', }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts index 5d30474ec..581116c0b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -25,10 +25,6 @@ const radioListStyleScript = (props: RadioListProps) => { '.ss__radio-list__option__icon, .ss__radio-list__option__label': { padding: 0, }, - '.ss__radio-list__option__icon': { - width: '16px', - height: '16px', - }, '&:last-child': { marginBottom: 0, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index 8acaa2bec..cb3b9f9b5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -1,13 +1,39 @@ import { css } from '@emotion/react'; import type { SearchInputProps } from '../../../../components/Molecules/SearchInput'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the SearchInput component const searchInputStyleScript = (props: SearchInputProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '&.ss__search-input': { + margin: `0 0 ${custom.spacing.x4}px`, + border: `1px solid ${custom.colors.gray02}`, + backgroundColor: `${custom.colors.gray01}`, + '.ss__icon': { + padding: `0 0 0 ${custom.spacing.x2}px`, + }, + '.ss__search-input__input': { + padding: `0 ${custom.spacing.x2}px`, + backgroundColor: `inherit`, + color: variables?.colors?.text, + height: `${custom.sizes.height}px`, + lineHeight: `${custom.sizes.height}px`, + '&::-webkit-input-placeholder': { + opacity: 0.5, + }, + '&::-ms-input-placeholder': { + opacity: 0.5, + }, + '&::placeholder': { + opacity: 0.5, + }, + }, + }, + }); }; // SearchInput component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index bfefd0a68..58953d116 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -47,10 +47,6 @@ const selectStyleScript = (props: SelectProps) => { padding: 0, margin: `0 0 ${custom.spacing.x2}px 0`, color: 'inherit', - '.ss__icon': { - width: '16px', - height: '16px', - }, '&:last-child': { marginBottom: '0', }, @@ -126,6 +122,9 @@ export const select: ThemeComponent<'select', SelectProps> = { '*select dropdown button': { native: false, }, + '*select dropdown icon': { + size: '12px', + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index 31c0ffb2f..5db7099c3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -7,8 +7,10 @@ import { custom } from '../../custom'; const facetStyleScript = (props: FacetProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + console.log('facet props', props); return css({ + margin: ` 0 0 ${custom.spacing.x6}px 0`, '&.ss__facet--collapsed': { '.ss__facet__header': { '.ss__icon': { @@ -17,9 +19,43 @@ const facetStyleScript = (props: FacetProps) => { }, }, '.ss__facet__header': { + margin: ` 0 0 ${custom.spacing.x4}px 0`, + padding: ` 0 0 ${custom.spacing.x2}px 0`, + gap: `${custom.spacing.x2}px`, + borderBottomColor: variables?.colors?.primary, + fontSize: '16px', + fontWeight: custom.fonts.weight02, + color: variables?.colors?.secondary, '.ss__icon': { transition: 'transform ease .5s', transform: 'rotate(180deg)', + width: '12px', + height: '12px', + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, + }, + '.ss__facet__options': { + marginTop: 0, + maxHeight: `490px`, + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + }, + '.ss__facet__show-more-less': { + margin: `${custom.spacing.x2}px 0 0 0`, + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + '.ss__icon': { + width: '10px', + height: '10px', }, }, }); @@ -34,6 +70,7 @@ export const facet: ThemeComponent<'facet', FacetProps> = { iconExpand: custom.icons.arrowDown, iconOverflowMore: custom.icons.plus, iconOverflowLess: custom.icons.minus, + searchable: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index e2b26a82b..3334447a1 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -22,9 +22,6 @@ export const mobileSidebar: ThemeComponent<'mobileSidebar', MobileSidebarProps> '*mobileSidebar button.close': { icon: custom.icons.close, }, - '*mobileSidebar button.slideout icon': { - size: '16px', - }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts index 1670b9041..0e252dcfa 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts @@ -24,9 +24,6 @@ export const search: ThemeComponent<'search', SearchProps> = { '*search button.sidebar-toggle': { icon: custom.icons.filter, }, - '*search button.sidebar-toggle icon': { - size: '16px', - }, }, }, mobile: searchThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts index 8c71f7229..0d9476da1 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts @@ -24,9 +24,6 @@ export const searchBoca: ThemeComponent<'searchBoca', SearchBocaProps> = { '*searchBoca button.sidebar-toggle': { icon: custom.icons.filter, }, - '*searchBoca button.sidebar-toggle icon': { - size: '16px', - }, }, }, mobile: searchBocaThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts index 5bd215296..e4a551c44 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts @@ -24,9 +24,6 @@ export const searchHorizontal: ThemeComponent<'searchHorizontal', SearchHorizont '*searchHorizontal button.sidebar-toggle': { icon: custom.icons.filter, }, - '*searchHorizontal button.sidebar-toggle icon': { - size: '16px', - }, }, }, mobile: searchHorizontalThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts index b6fa50c32..10e5eb8fe 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts @@ -24,9 +24,6 @@ export const searchSnapnco: ThemeComponent<'searchSnapnco', SearchSnapncoProps> '*searchSnapnco button.sidebar-toggle': { icon: custom.icons.filter, }, - '*searchSnapnco button.sidebar-toggle icon': { - size: '16px', - }, }, }, mobile: searchSnapncoThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts index 2c9012ac3..fc2585211 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts @@ -24,9 +24,6 @@ export const searchSnappy: ThemeComponent<'searchSnappy', SearchSnappyProps> = { '*searchSnappy button.sidebar-toggle': { icon: custom.icons.filter, }, - '*searchSnappy button.sidebar-toggle icon': { - size: '16px', - }, }, }, mobile: searchSnappyThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 5ccfe5d29..a4cb6a950 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -44,7 +44,7 @@ export const custom: { }, sizes: { height: 33, // refers to height for button and dropdown sizes - icons: 12, + icons: 16, }, spacing: { x1: 5, From 715cb25d8dc7e06cd24b4a5ca65af30e3c12d32f Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 21 May 2025 07:42:43 -0600 Subject: [PATCH 007/118] facet grid and some fixes --- .../themes/pike/components/atoms/button.ts | 2 +- .../pike/components/molecules/checkbox.ts | 2 +- .../components/molecules/facetGridOptions.ts | 57 ++++++++++++++++++- .../molecules/facetHierarchyOptions.ts | 2 +- .../components/molecules/facetListOptions.ts | 2 +- .../components/molecules/layoutSelector.ts | 2 +- .../themes/pike/components/molecules/list.ts | 2 +- .../themes/pike/components/molecules/radio.ts | 2 +- .../pike/components/molecules/radioList.ts | 2 +- .../pike/components/molecules/select.ts | 2 +- .../themes/pike/components/organisms/facet.ts | 1 - 11 files changed, 65 insertions(+), 11 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index cefc5f42d..901be05a9 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -35,7 +35,7 @@ const buttonStyleScript = (props: ButtonProps) => { textOverflow: 'ellipsis', whiteSpace: 'nowrap', '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { - backgroundColor: buttonColor.hex() || custom.colors.black, + backgroundColor: buttonColor.hex(), }, }, disabledStyles, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index 755f425ac..d026b61e5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -10,7 +10,7 @@ const checkboxStyleScript = (props: CheckboxProps) => { const variables = props?.theme?.variables; const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); const borderColor = new Color(custom.colors.gray02); - const activeIconColor = new Color(variables?.colors?.primary || custom.colors.black); + const activeIconColor = new Color(variables?.colors?.primary); const activeBorderColor = new Color(custom.colors.gray02).darken(0.055); // shared checkbox styles diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 35d20b856..ac9b34fc0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -1,13 +1,67 @@ import { css } from '@emotion/react'; import type { FacetGridOptionsProps } from '../../../../components/Molecules/FacetGridOptions'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the FacetGridOptions component const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); + const activeColor = new Color(variables?.colors?.primary); + const fontColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color(custom.colors.white) : Color(custom.colors.black); - return css({}); + return css({ + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(52px, 1fr))', + gap: custom.spacing.x1, + alignItems: 'center', + '.ss__facet-grid-options__option': { + position: 'relative', + height: 0, + paddingBottom: '100%', + '&, &:before, .ss__facet-grid-options__option__value': { + display: 'block', + }, + '&:before, .ss__facet-grid-options__option__value': { + position: 'absolute', + margin: 'auto', + }, + '&:before': { + content: '""', + display: 'block', + width: '100%', + height: '100%', + backgroundColor: backgroundColor.hex(), + border: `1px solid ${custom.colors.gray02}`, + boxSizing: 'border-box', + }, + '.ss__facet-grid-options__option__value': { + top: '50%', + left: 0, + right: 0, + zIndex: 2, + transform: 'translateY(-50%)', + maxWidth: `calc(100% - ${custom.spacing.x2}px)`, + maxHeight: `calc(100% - ${custom.spacing.x2}px)`, + overflow: 'hidden', + textAlign: 'center', + fontSize: '0.75rem', + color: variables?.colors?.text, + }, + }, + '.ss__facet-grid-options__option.ss__facet-grid-options__option--filtered': { + '&:before': { + backgroundColor: activeColor.hex(), + borderColor: activeColor.hex(), + }, + '.ss__facet-grid-options__option__value': { + fontWeight: custom.fonts.weight01, + color: fontColor.hex(), + }, + }, + }); }; // FacetGridOptions component props @@ -16,6 +70,7 @@ export const facetGridOptions: ThemeComponent<'facetGridOptions', FacetGridOptio props: { themeStyleScript: facetGridOptionsStyleScript, columns: 5, + disableStyles: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index de3c3601e..5f3de6d4a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -38,7 +38,7 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => }, '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { fontWeight: custom.fonts.weight01, - color: variables?.colors?.primary || custom.colors.black, + color: variables?.colors?.primary, '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { paddingLeft: `${custom.spacing.x6}px`, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 453fd3305..94034f559 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -30,7 +30,7 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { }, '.ss__facet-list-options__option.ss__facet-list-options__option--filtered': { fontWeight: custom.fonts.weight01, - color: variables?.colors?.primary || custom.colors.black, + color: variables?.colors?.primary, }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts index 0c404df71..a79116af7 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -9,7 +9,7 @@ const layoutSelectorStyleScript = (props: LayoutSelectorProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); - const activeColor = new Color(variables?.colors?.primary || custom.colors.black); + const activeColor = new Color(variables?.colors?.primary); const activeIconColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color('#ffffff') : Color('#000000'); // dropdown styles diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts index b40b5b5b5..13832b447 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -8,7 +8,7 @@ import Color from 'color'; const listStyleScript = (props: ListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const activeColor = new Color(variables?.colors?.primary || custom.colors.black); + const activeColor = new Color(variables?.colors?.primary); return css({ '&, .ss__list__options': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index d08fc3fe1..9861c22ee 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -10,7 +10,7 @@ const radioStyleScript = (props: RadioProps) => { const variables = props?.theme?.variables; const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); const borderColor = new Color(custom.colors.gray02); - const activeIconColor = new Color(variables?.colors?.primary || custom.colors.black); + const activeIconColor = new Color(variables?.colors?.primary); const activeBorderColor = new Color(custom.colors.gray02).darken(0.055); // shared radio styles diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts index 581116c0b..105dfb5d5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -31,7 +31,7 @@ const radioListStyleScript = (props: RadioListProps) => { }, '.ss__radio-list__option--selected': { fontWeight: custom.fonts.weight01, - color: variables?.colors?.primary || custom.colors.black, + color: variables?.colors?.primary, }, }, }); diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 58953d116..565967b24 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -56,7 +56,7 @@ const selectStyleScript = (props: SelectProps) => { }, '.ss__select__select__option--selected': { fontWeight: custom.fonts.weight01, - color: variables?.colors?.primary || custom.colors.black, + color: variables?.colors?.primary, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index 5db7099c3..cfe80b698 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -7,7 +7,6 @@ import { custom } from '../../custom'; const facetStyleScript = (props: FacetProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - console.log('facet props', props); return css({ margin: ` 0 0 ${custom.spacing.x6}px 0`, From bb40fa36d694c22e27a352a02e27af174be63733 Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 21 May 2025 09:02:37 -0600 Subject: [PATCH 008/118] working on palette --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../pike/components/molecules/checkbox.ts | 18 ++--- .../components/molecules/facetGridOptions.ts | 11 +-- .../molecules/facetPaletteOptions.ts | 74 ++++++++++++++++++- .../components/molecules/layoutSelector.ts | 3 +- .../themes/pike/components/molecules/list.ts | 4 +- .../themes/pike/components/molecules/radio.ts | 21 +++--- .../pike/components/molecules/select.ts | 4 +- .../components/src/themes/pike/custom.ts | 6 +- 9 files changed, 105 insertions(+), 38 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 182c3c20b..e67213872 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -60,7 +60,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchBoca', + component: 'SearchSnapnco', }, ], }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index d026b61e5..eabe6c169 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -2,16 +2,11 @@ import { css } from '@emotion/react'; import type { CheckboxProps } from '../../../../components/Molecules/Checkbox'; import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; -import Color from 'color'; // CSS in JS style script for the Checkbox component const checkboxStyleScript = (props: CheckboxProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); - const borderColor = new Color(custom.colors.gray02); - const activeIconColor = new Color(variables?.colors?.primary); - const activeBorderColor = new Color(custom.colors.gray02).darken(0.055); // shared checkbox styles const sharedStyles = css({ @@ -31,13 +26,18 @@ const checkboxStyleScript = (props: CheckboxProps) => { const defaultStyles = css([ sharedStyles, { - backgroundColor: backgroundColor.hex(), - border: `1px solid ${props?.checked ? activeBorderColor.hex() : borderColor.hex()}`, + backgroundColor: custom.colors.gray01, + border: `1px solid ${custom.colors.gray02}`, '.ss__icon': { width: '8px', height: '8px', - fill: props?.checked ? activeIconColor.hex() : '', - stroke: props?.checked ? activeIconColor.hex() : '', + }, + '&.ss__checkbox--active': { + borderColor: custom.colors.gray03, + '.ss__icon': { + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, }, }, disabledStyles, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index ac9b34fc0..864e88201 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -8,7 +8,6 @@ import Color from 'color'; const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); const activeColor = new Color(variables?.colors?.primary); const fontColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color(custom.colors.white) : Color(custom.colors.black); @@ -21,6 +20,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { position: 'relative', height: 0, paddingBottom: '100%', + color: variables?.colors?.text, '&, &:before, .ss__facet-grid-options__option__value': { display: 'block', }, @@ -33,7 +33,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { display: 'block', width: '100%', height: '100%', - backgroundColor: backgroundColor.hex(), + backgroundColor: custom.colors.gray01, border: `1px solid ${custom.colors.gray02}`, boxSizing: 'border-box', }, @@ -48,18 +48,15 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { overflow: 'hidden', textAlign: 'center', fontSize: '0.75rem', - color: variables?.colors?.text, }, }, '.ss__facet-grid-options__option.ss__facet-grid-options__option--filtered': { + fontWeight: custom.fonts.weight01, + color: fontColor.hex(), '&:before': { backgroundColor: activeColor.hex(), borderColor: activeColor.hex(), }, - '.ss__facet-grid-options__option__value': { - fontWeight: custom.fonts.weight01, - color: fontColor.hex(), - }, }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index dcc31e9c1..d27e5eccf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -1,13 +1,83 @@ import { css } from '@emotion/react'; import type { FacetPaletteOptionsProps } from '../../../../components/Molecules/FacetPaletteOptions'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; +//import Color from 'color'; // CSS in JS style script for the FacetPaletteOptions component const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + //const activeColor = new Color(variables?.colors?.primary); + //const fontColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color(custom.colors.white) : Color(custom.colors.black); - return css({}); + console.log('props', props); + + // shared palette styles + const sharedStyles = css({ + '.ss__facet-palette-options__option': { + color: variables?.colors?.text, + }, + }); + + // default styles + const defaultStyles = css([ + sharedStyles, + { + display: 'grid', + gridTemplateColumns: 'repeat(auto-fill, minmax(52px, 1fr))', + gap: custom.spacing.x1, + alignItems: 'center', + '.ss__facet-palette-options__option': { + '.ss__facet-palette-options__option__wrapper': { + position: 'relative', + height: 0, + paddingBottom: '100%', + '&:before, .ss__facet-palette-options__option__palette': { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + margin: 'auto', + }, + '&:before, .ss__facet-palette-options__option__palette:before': { + content: '""', + display: 'block', + }, + '&:before': { + zIndex: 2, + }, + '.ss__facet-palette-options__option__palette': { + zIndex: 1, + }, + }, + '.ss__facet-palette-options__option__value': { + display: 'block', + fontSize: '0.75rem', + lineHeight: '0.85rem', + textAlign: 'center', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + margin: `${custom.spacing.x1}px 0 0 0`, + }, + }, + }, + ]); + + // list styles + const listStyles = css([ + sharedStyles, + { + // width: `${custom.sizes.icon}px`, + // height: `${custom.sizes.icon}px`, + // border: `1px solid ${custom.colors.gray02}`, + // cursor: 'pointer', + }, + ]); + + return props?.layout == 'list' ? listStyles : defaultStyles; }; // FacetPaletteOptions component props @@ -16,6 +86,8 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal props: { themeStyleScript: facetPaletteStyleScript, columns: 5, + disableStyles: true, + hideIcon: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts index a79116af7..37be9948a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -8,7 +8,6 @@ import Color from 'color'; const layoutSelectorStyleScript = (props: LayoutSelectorProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); const activeColor = new Color(variables?.colors?.primary); const activeIconColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color('#ffffff') : Color('#000000'); @@ -27,7 +26,7 @@ const layoutSelectorStyleScript = (props: LayoutSelectorProps) => { display: 'flex', '.ss__list__option': { border: `1px solid ${custom.colors.gray02}`, - backgroundColor: backgroundColor.hex(), + backgroundColor: custom.colors.gray01, height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, padding: `0 ${custom.spacing.x2}px`, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts index 13832b447..368b49673 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -2,13 +2,11 @@ import { css } from '@emotion/react'; import type { ListProps } from '../../../../components/Molecules/List'; import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; -import Color from 'color'; // CSS in JS style script for the List component const listStyleScript = (props: ListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const activeColor = new Color(variables?.colors?.primary); return css({ '&, .ss__list__options': { @@ -32,7 +30,7 @@ const listStyleScript = (props: ListProps) => { }, '.ss__list__option--selected': { fontWeight: custom.fonts.weight01, - color: activeColor.hex(), + color: variables?.colors?.primary, }, }, }); diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index 9861c22ee..cb7b974e2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -2,16 +2,11 @@ import { css } from '@emotion/react'; import type { RadioProps } from '../../../../components/Molecules/Radio'; import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; -import Color from 'color'; // CSS in JS style script for the Radio component const radioStyleScript = (props: RadioProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); - const borderColor = new Color(custom.colors.gray02); - const activeIconColor = new Color(variables?.colors?.primary); - const activeBorderColor = new Color(custom.colors.gray02).darken(0.055); // shared radio styles const disabledStyles = css({ @@ -26,15 +21,21 @@ const radioStyleScript = (props: RadioProps) => { // default styles const defaultStyles = css([ { - backgroundColor: backgroundColor.hex(), - border: `1px solid ${props?.checked ? activeBorderColor.hex() : borderColor.hex()}`, + backgroundColor: custom.colors.gray01, + border: `1px solid ${custom.colors.gray02}`, '&, & .ss__icon': { borderRadius: '50%', }, '.ss__icon': { - display: props?.checked ? '' : 'none', - fill: props?.checked ? activeIconColor.hex() : '', - stroke: props?.checked ? activeIconColor.hex() : '', + display: 'none', + }, + '&.ss__radio--active': { + borderColor: custom.colors.gray03, + '.ss__icon': { + display: 'block', + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, }, }, disabledStyles, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 565967b24..64bce827e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -2,13 +2,11 @@ import { css } from '@emotion/react'; import type { SelectProps } from '../../../../components/Molecules/Select'; import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; -import Color from 'color'; // CSS in JS style script for the Select component const selectStyleScript = (props: SelectProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const backgroundColor = new Color(custom.colors.gray02).lighten(0.055); const nativeIcon = ``; @@ -17,7 +15,7 @@ const selectStyleScript = (props: SelectProps) => { const sharedStyles = css({ border: `1px solid ${custom.colors.gray02}`, color: variables?.colors?.text, - backgroundColor: backgroundColor.hex(), + backgroundColor: custom.colors.gray01, }); // default styles diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index a4cb6a950..39f1fc1b2 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -1,4 +1,5 @@ import { IconType } from '../../components/Atoms/Icon'; +import Color from 'color'; export const custom: { colors: { @@ -20,8 +21,9 @@ export const custom: { colors: { white: '#ffffff', black: '#000000', - gray01: '#f8f8f8', - gray02: '#ebebeb', + gray01: '#f8f8f8', // lighter gray: bg color under terms, dropdown, checkboxes + gray02: '#ebebeb', // light gray: borders for autocomplete, dropdown, checkboxes + gray03: `${new Color('#ebebeb').darken(0.055).hex().toLowerCase()}`, // dark gray: active border for checkboxes, palette, etc. }, fonts: { weight01: 700, // main font weight From 0664417671cc288e89a8cd4eb0bbe0c511feb880 Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 21 May 2025 13:15:21 -0600 Subject: [PATCH 009/118] working on palette --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../molecules/facetHierarchyOptions.ts | 1 + .../components/molecules/facetListOptions.ts | 1 + .../molecules/facetPaletteOptions.ts | 173 +++++++++++++++--- .../themes/pike/components/organisms/facet.ts | 1 - .../components/src/themes/pike/custom.ts | 4 + 6 files changed, 153 insertions(+), 29 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index e67213872..fb56bb7f7 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -6,7 +6,7 @@ import { SnapTemplatesConfig } from '@searchspring/snap-preact'; let config: SnapTemplatesConfig = { config: { - siteId: '8uyt2m', + siteId: 'hi0bkd', language: 'en', currency: 'usd', platform: 'other', diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index 5f3de6d4a..5ab9316cb 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -20,6 +20,7 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => top: '-1px', margin: `0 0 0 ${custom.spacing.x1}px`, opacity: 0.805, + fontSize: '0.6rem', }, }, '&:last-child': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 94034f559..81374d3f3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -22,6 +22,7 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { top: '-1px', margin: `0 0 0 ${custom.spacing.x1}px`, opacity: 0.805, + fontSize: '0.6rem', }, }, '&:last-child': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index d27e5eccf..46e12a730 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -2,21 +2,94 @@ import { css } from '@emotion/react'; import type { FacetPaletteOptionsProps } from '../../../../components/Molecules/FacetPaletteOptions'; import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; -//import Color from 'color'; // CSS in JS style script for the FacetPaletteOptions component const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - //const activeColor = new Color(variables?.colors?.primary); - //const fontColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color(custom.colors.white) : Color(custom.colors.black); + const hasCheckbox = !props?.hideCheckbox ? true : false; - console.log('props', props); + // light colors selector + const palettePrefix = '.ss__facet-palette-options__option__palette'; + const lightColors = `${palettePrefix}--white, ${palettePrefix}--ivory, ${palettePrefix}--clear, ${palettePrefix}--transparent`; // shared palette styles + const lightBorderStyles = css({ + borderColor: custom.colors.gray02, + opacity: 1, + visibility: 'visible', + }); const sharedStyles = css({ '.ss__facet-palette-options__option': { color: variables?.colors?.text, + '.ss__facet-palette-options__option__wrapper': { + '&:before, .ss__facet-palette-options__option__palette, .ss__facet-palette-options__option__palette:before': { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + margin: 'auto', + }, + '&:before, .ss__facet-palette-options__option__palette:before': { + content: '""', + display: 'block', + opacity: 0, + visibility: 'hidden', + }, + '&:before': { + zIndex: 2, + border: `1px solid ${custom.colors.black}`, + }, + '.ss__facet-palette-options__option__palette': { + zIndex: 1, + '&:before': { + border: `1px solid ${custom.colors.white}`, + }, + '&:not([style]):before': { + ...lightBorderStyles, + }, + }, + [`${lightColors}`]: { + '&:before': { + ...lightBorderStyles, + }, + }, + }, + '.ss__facet-palette-options__option__value__count': { + position: 'relative', + top: props?.layout == 'list' ? '-1px' : '', + padding: props?.layout == 'list' ? `0 ${custom.spacing.x1}px` : ``, + opacity: 0.805, + fontSize: '0.6rem', + }, + }, + '.ss__facet-palette-options__option.ss__facet-palette-options__option--filtered': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + '.ss__facet-palette-options__option__wrapper': { + '&:before, .ss__facet-palette-options__option__palette:before': { + visibility: 'visible', + }, + '&:before': { + opacity: 0.4, + }, + '.ss__facet-palette-options__option__palette': { + '&:before': { + opacity: 1, + margin: '1px', + borderWidth: props?.layout == 'list' ? `${hasCheckbox ? 2 : 3}px` : `5px`, + }, + '&:not([style]):before': { + borderColor: custom.colors.gray01, + }, + }, + [`${lightColors}`]: { + '&:before': { + borderColor: custom.colors.gray01, + }, + }, + }, }, }); @@ -29,34 +102,16 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { gap: custom.spacing.x1, alignItems: 'center', '.ss__facet-palette-options__option': { + textAlign: 'center', '.ss__facet-palette-options__option__wrapper': { position: 'relative', height: 0, paddingBottom: '100%', - '&:before, .ss__facet-palette-options__option__palette': { - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - margin: 'auto', - }, - '&:before, .ss__facet-palette-options__option__palette:before': { - content: '""', - display: 'block', - }, - '&:before': { - zIndex: 2, - }, - '.ss__facet-palette-options__option__palette': { - zIndex: 1, - }, }, '.ss__facet-palette-options__option__value': { display: 'block', fontSize: '0.75rem', lineHeight: '0.85rem', - textAlign: 'center', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', @@ -67,13 +122,35 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { ]); // list styles + const listSize = hasCheckbox ? 16 : 22; + const listCheckboxSize = 16; + const listPadding = hasCheckbox ? custom.spacing.x4 + listSize + listCheckboxSize : custom.spacing.x2 + listSize; const listStyles = css([ sharedStyles, { - // width: `${custom.sizes.icon}px`, - // height: `${custom.sizes.icon}px`, - // border: `1px solid ${custom.colors.gray02}`, - // cursor: 'pointer', + '.ss__facet-palette-options__option': { + display: 'block', + position: 'relative', + margin: `0 0 ${custom.spacing.x1}px 0`, + padding: `${hasCheckbox ? 0 : custom.spacing.x1 + 'px'} 0 0 ${listPadding}px`, + minHeight: hasCheckbox ? '' : `${listSize}px`, + '&:last-child': { + marginBottom: 0, + }, + '.ss__checkbox, .ss__facet-palette-options__option__wrapper': { + position: 'absolute', + top: `${hasCheckbox ? '2' : '3.5'}px`, + }, + '.ss__checkbox': { + left: 0, + }, + '.ss__facet-palette-options__option__wrapper': { + left: hasCheckbox ? `${listCheckboxSize + custom.spacing.x2}px` : 0, + width: `${listSize}px`, + height: `${listSize}px`, + lineHeight: `${listSize}px`, + }, + }, }, ]); @@ -88,6 +165,48 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal columns: 5, disableStyles: true, hideIcon: true, + //hideCheckbox: false, + hideCount: false, + //layout: 'list', + gridSize: '80px', + gapSize: '10px', + colorMapping: { + brown: { + background: custom.colors.brown, + }, + Brown: { + background: custom.colors.brown, + }, + multi: { + background: custom.colors.rainbow, + }, + Multi: { + background: custom.colors.rainbow, + }, + 'multi-color': { + background: custom.colors.rainbow, + }, + 'Multi-color': { + background: custom.colors.rainbow, + }, + purple: { + background: custom.colors.purple, + }, + Purple: { + background: custom.colors.purple, + }, + rainbow: { + background: custom.colors.rainbow, + }, + Rainbow: { + background: custom.colors.rainbow, + }, + }, + }, + components: { + '*facetPaletteOptions checkbox': { + disableStyles: false, + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index cfe80b698..bcb2bf839 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -69,7 +69,6 @@ export const facet: ThemeComponent<'facet', FacetProps> = { iconExpand: custom.icons.arrowDown, iconOverflowMore: custom.icons.plus, iconOverflowLess: custom.icons.minus, - searchable: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 39f1fc1b2..c7effb6a7 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -24,6 +24,10 @@ export const custom: { gray01: '#f8f8f8', // lighter gray: bg color under terms, dropdown, checkboxes gray02: '#ebebeb', // light gray: borders for autocomplete, dropdown, checkboxes gray03: `${new Color('#ebebeb').darken(0.055).hex().toLowerCase()}`, // dark gray: active border for checkboxes, palette, etc. + brown: '#845329', // for color palette + purple: '#7c368e', // for color palette + rainbow: + 'linear-gradient(rgb(40, 87, 218) 20%, rgb(40, 218, 70) 20%, rgb(40, 218, 70) 40%, rgb(245, 228, 24) 40%, rgb(245, 228, 24) 60%, rgb(242, 133, 0) 60%, rgb(242, 133, 0) 80%, rgb(218, 40, 72) 80%, rgb(218, 40, 72))', // for color palette }, fonts: { weight01: 700, // main font weight From e1b31935c26f9965d969537f6da9043344544d51 Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 21 May 2025 13:33:02 -0600 Subject: [PATCH 010/118] finish palette --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../molecules/facetPaletteOptions.ts | 29 ++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index fb56bb7f7..e67213872 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -6,7 +6,7 @@ import { SnapTemplatesConfig } from '@searchspring/snap-preact'; let config: SnapTemplatesConfig = { config: { - siteId: 'hi0bkd', + siteId: '8uyt2m', language: 'en', currency: 'usd', platform: 'other', diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 46e12a730..4f2c00e92 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -49,6 +49,9 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { '&:not([style]):before': { ...lightBorderStyles, }, + '.ss__icon': { + display: 'none', + }, }, [`${lightColors}`]: { '&:before': { @@ -98,25 +101,33 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { sharedStyles, { display: 'grid', - gridTemplateColumns: 'repeat(auto-fill, minmax(52px, 1fr))', - gap: custom.spacing.x1, + gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, + gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', '.ss__facet-palette-options__option': { textAlign: 'center', + '.ss__checkbox': { + display: 'none', + }, '.ss__facet-palette-options__option__wrapper': { position: 'relative', height: 0, paddingBottom: '100%', }, - '.ss__facet-palette-options__option__value': { + '.ss__facet-palette-options__option__value, .ss__facet-palette-options__option__value__count': { display: 'block', - fontSize: '0.75rem', lineHeight: '0.85rem', - overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', + }, + '.ss__facet-palette-options__option__value': { + fontSize: '0.75rem', + overflow: 'hidden', margin: `${custom.spacing.x1}px 0 0 0`, }, + '.ss__facet-palette-options__option__value__count': { + margin: `${custom.spacing.x1 / 2}px 0 0 0`, + }, }, }, ]); @@ -162,14 +173,10 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal default: { props: { themeStyleScript: facetPaletteStyleScript, - columns: 5, disableStyles: true, hideIcon: true, - //hideCheckbox: false, - hideCount: false, - //layout: 'list', - gridSize: '80px', - gapSize: '10px', + gridSize: '52px', + gapSize: `${custom.spacing.x1}px`, colorMapping: { brown: { background: custom.colors.brown, From 27dcb25dfbd5dd47562cbef13c5b37fa47f8c804 Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 21 May 2025 19:33:14 -0600 Subject: [PATCH 011/118] working on facet slider --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../themes/pike/components/atoms/button.ts | 2 +- .../pike/components/molecules/checkbox.ts | 1 + .../components/molecules/facetGridOptions.ts | 9 +- .../molecules/facetHierarchyOptions.ts | 22 ++-- .../components/molecules/facetListOptions.ts | 28 +++-- .../pike/components/molecules/facetSlider.ts | 104 +++++++++++++++++- .../components/molecules/layoutSelector.ts | 3 +- .../themes/pike/components/molecules/list.ts | 2 +- .../pike/components/molecules/radioList.ts | 2 +- .../pike/components/molecules/select.ts | 2 +- .../themes/pike/components/organisms/facet.ts | 6 + .../components/src/themes/pike/custom.ts | 10 ++ 13 files changed, 164 insertions(+), 29 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index e67213872..b2b442881 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -60,7 +60,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchSnapnco', + component: 'SearchSnappy', }, ], }, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index 901be05a9..8b72d0609 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -9,7 +9,7 @@ const buttonStyleScript = (props: ButtonProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const buttonColor = new Color(props?.backgroundColor || variables?.colors?.primary); - const fontColor = buttonColor.isDark() || buttonColor.hex() == '#00AEEF' ? Color(custom.colors.white) : Color(custom.colors.black); + const fontColor = buttonColor.isDark() || buttonColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); // shared button styles const disabledStyles = css({ diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index eabe6c169..0cd8a1faf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -33,6 +33,7 @@ const checkboxStyleScript = (props: CheckboxProps) => { height: '8px', }, '&.ss__checkbox--active': { + backgroundColor: custom.colors.white, borderColor: custom.colors.gray03, '.ss__icon': { fill: variables?.colors?.primary, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 864e88201..da091c4d2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -9,12 +9,12 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const activeColor = new Color(variables?.colors?.primary); - const fontColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color(custom.colors.white) : Color(custom.colors.black); + const fontColor = activeColor.isDark() || activeColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); return css({ display: 'grid', - gridTemplateColumns: 'repeat(auto-fill, minmax(52px, 1fr))', - gap: custom.spacing.x1, + gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, + gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', '.ss__facet-grid-options__option': { position: 'relative', @@ -66,8 +66,9 @@ export const facetGridOptions: ThemeComponent<'facetGridOptions', FacetGridOptio default: { props: { themeStyleScript: facetGridOptionsStyleScript, - columns: 5, disableStyles: true, + gridSize: '52px', + gapSize: `${custom.spacing.x1}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index 5ab9316cb..4bc1a7d76 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -10,37 +10,40 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => return css({ '.ss__facet-hierarchy-options__option': { - padding: 0, - margin: `0 0 ${custom.spacing.x2}px 0`, + display: 'block', + margin: `0 0 ${custom.spacing.x1}px 0`, color: variables?.colors?.text, + '&:last-child': { + marginBottom: 0, + }, '.ss__facet-hierarchy-options__option__value': { margin: 0, '.ss__facet-hierarchy-options__option__value__count': { position: 'relative', top: '-1px', - margin: `0 0 0 ${custom.spacing.x1}px`, + padding: `0 ${custom.spacing.x1}px`, opacity: 0.805, fontSize: '0.6rem', }, }, - '&:last-child': { - marginBottom: 0, - }, }, '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--return': { '&:before': { + content: '"\\0000ab"', + display: 'inline-block', + height: '24px', position: 'relative', - top: '-2px', + top: '1px', padding: `0 ${custom.spacing.x1}px 0 0`, fontSize: '24px', color: 'inherit', - lineHeight: 0, + lineHeight: '24px', }, }, '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, - '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { + '& ~ .ss__facet-hierarchy-options__option': { paddingLeft: `${custom.spacing.x6}px`, }, }, @@ -52,6 +55,7 @@ export const facetHierarchyOptions: ThemeComponent<'facetHierarchyOptions', Face default: { props: { themeStyleScript: facetHierarchyOptionsStyleScript, + disableStyles: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 81374d3f3..a8c25703b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -7,27 +7,31 @@ import { custom } from '../../custom'; const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const isHorizontal = props?.horizontal ? true : false; return css({ - gap: isHorizontal ? `${custom.spacing.x2}px` : '', '.ss__facet-list-options__option': { - padding: 0, - margin: isHorizontal ? `` : `0 0 ${custom.spacing.x2}px 0`, + display: 'block', + position: 'relative', + margin: `0 0 ${custom.spacing.x1}px 0`, + padding: props?.hideCheckbox ? `` : `0 0 0 ${16 + custom.spacing.x2}px`, color: variables?.colors?.text, + '&:last-child': { + marginBottom: 0, + }, + '.ss__checkbox': { + position: 'absolute', + top: '1px', + left: 0, + }, '.ss__facet-list-options__option__value': { - margin: props?.hideCheckbox ? `` : `0 0 0 ${custom.spacing.x2}px`, '.ss__facet-list-options__option__value__count': { position: 'relative', top: '-1px', - margin: `0 0 0 ${custom.spacing.x1}px`, + padding: `0 ${custom.spacing.x1}px`, opacity: 0.805, fontSize: '0.6rem', }, }, - '&:last-child': { - marginBottom: isHorizontal ? '' : 0, - }, }, '.ss__facet-list-options__option.ss__facet-list-options__option--filtered': { fontWeight: custom.fonts.weight01, @@ -41,6 +45,12 @@ export const facetListOptions: ThemeComponent<'facetListOptions', FacetListOptio default: { props: { themeStyleScript: facetListOptionsStyleScript, + disableStyles: true, + }, + components: { + '*facetListOptions checkbox': { + disableStyles: false, + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index 909cb77ec..f42ce2ff0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -1,13 +1,113 @@ import { css } from '@emotion/react'; import type { FacetSliderProps } from '../../../../components/Molecules/FacetSlider'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the FacetSlider component const facetSliderStyleScript = (props: FacetSliderProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const valuesTop = custom.slider.valuesPosition == 'top' ? true : false; + const valuesLeft = custom.slider.valuesAlign == 'left' ? true : false; + const handleColor = new Color(props?.handleColor || variables?.colors?.primary); + const handleInnerColor = + handleColor.isDark() || handleColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); - return css({}); + // set slider height + const sliderHeight = custom.slider.values + custom.slider.handles + custom.slider.handles / 2 + (props?.showTicks ? 20 : 0); + + // determine when to add top margin to slider + let addTopMargin = false; + if (props?.stickyHandleLabel) { + if (props?.showTicks && !valuesTop) { + addTopMargin = true; + } + } + + return css({ + height: `${sliderHeight}px`, + '&, *': { + boxSizing: 'border-box', + }, + '&, .ss__facet-slider__slider': { + margin: 'auto', + }, + '.ss__facet-slider__slider button, .ss__facet-slider__labels label': { + margin: 0, + padding: 0, + '&:focus': { + outline: 0, + }, + }, + '.ss__facet-slider__slider': { + display: 'block', + top: 0, + width: '100%', + marginTop: addTopMargin ? `${custom.slider.handles / 2}px` : '', + '&, .ss__facet-slider__handles': { + height: `${custom.slider.bar}px`, + }, + '.ss__facet-slider__tick': { + '.ss__facet-slider__tick__label': { + color: props?.tickTextColor || variables?.colors?.text, + }, + }, + '.ss__facet-slider__segment': { + backgroundColor: props?.trackColor || custom.colors.gray01, + border: `1px solid ${props?.trackColor || custom.colors.gray02}`, + borderRadius: `${custom.slider.bar}px`, + }, + '.ss__facet-slider__rail': { + backgroundColor: props?.railColor || variables?.colors?.secondary, + border: `1px solid ${props?.railColor || variables?.colors?.secondary}`, + }, + '.ss__facet-slider__handles': { + position: 'relative', + margin: `0 ${custom.slider.handles / 2 - 1}px`, + button: { + '.ss__facet-slider__handle': { + transform: 'none', + width: `${custom.slider.handles}px`, + height: `${custom.slider.handles}px`, + lineHeight: `${custom.slider.handles}px`, + backgroundColor: handleColor.hex(), + border: `1px solid ${handleColor.hex()}`, + '&:after': { + width: `${custom.slider.handles / 4}px`, + height: `${custom.slider.handles / 4}px`, + backgroundColor: handleInnerColor.hex(), + border: `1px solid ${handleInnerColor.hex()}`, + }, + '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { + top: `${valuesTop ? '-' : ''}${valuesTop ? custom.slider.handles + custom.spacing.x1 : custom.slider.handles + custom.spacing.x5}px`, + fontSize: `${custom.slider.values}px`, + color: props?.valueTextColor || variables?.colors?.text, + }, + }, + }, + }, + }, + '.ss__facet-slider__labels': { + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + justifyContent: valuesLeft ? '' : 'center', + fontSize: `${custom.slider.values}px`, + lineHeight: `${custom.slider.values}px`, + order: valuesTop ? -1 : '', + margin: valuesTop ? `0 0 ${custom.spacing.x2}px 0` : `${custom.spacing.x2}px 0 0 0`, + '.ss__facet-slider__label': { + color: props?.valueTextColor || variables?.colors?.text, + '&:after': { + display: valuesLeft ? 'none' : '', + }, + '& ~ .ss__facet-slider__label': { + marginLeft: valuesLeft ? 'auto' : '', + }, + }, + }, + }); }; // FacetSlider component props @@ -15,6 +115,8 @@ export const facetSlider: ThemeComponent<'facetSlider', FacetSliderProps> = { default: { props: { themeStyleScript: facetSliderStyleScript, + showTicks: true, + stickyHandleLabel: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts index 37be9948a..a2d21cbd2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -9,7 +9,8 @@ const layoutSelectorStyleScript = (props: LayoutSelectorProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const activeColor = new Color(variables?.colors?.primary); - const activeIconColor = activeColor.isDark() || activeColor.hex() == '#00AEEF' ? Color('#ffffff') : Color('#000000'); + const activeIconColor = + activeColor.isDark() || activeColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); // dropdown styles const dropdownStyles = css({ diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts index 368b49673..9c0abd9c2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -13,7 +13,7 @@ const listStyleScript = (props: ListProps) => { display: 'block', }, '.ss__list__title, .ss__list__options .ss__list__option': { - margin: `0 0 ${custom.spacing.x2}px 0`, + margin: `0 0 ${custom.spacing.x1}px 0`, }, '.ss__list__title': { display: 'block', diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts index 105dfb5d5..94fbd59f8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -11,7 +11,7 @@ const radioListStyleScript = (props: RadioListProps) => { return css({ '.ss__radio-list__title, .ss__radio-list__options-wrapper .ss__radio-list__option': { padding: 0, - margin: `0 0 ${custom.spacing.x2}px 0`, + margin: `0 0 ${custom.spacing.x1}px 0`, }, '.ss__radio-list__title': { display: 'block', diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 64bce827e..5155c713c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -43,7 +43,7 @@ const selectStyleScript = (props: SelectProps) => { padding: `${custom.spacing.x2}px`, '.ss__select__select__option': { padding: 0, - margin: `0 0 ${custom.spacing.x2}px 0`, + margin: `0 0 ${custom.spacing.x1}px 0`, color: 'inherit', '&:last-child': { marginBottom: '0', diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index bcb2bf839..fd8d836c2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -17,6 +17,12 @@ const facetStyleScript = (props: FacetProps) => { }, }, }, + '&.ss__facet--slider': { + '.ss__facet__options': { + maxHeight: 'none', + overflow: 'visible', + }, + }, '.ss__facet__header': { margin: ` 0 0 ${custom.spacing.x4}px 0`, padding: ` 0 0 ${custom.spacing.x2}px 0`, diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index c7effb6a7..2f212c291 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -14,6 +14,9 @@ export const custom: { sizes: { [size: string]: any; }; + slider: { + [slider: string]: any; + }; spacing: { [size: string]: number; }; @@ -52,6 +55,13 @@ export const custom: { height: 33, // refers to height for button and dropdown sizes icons: 16, }, + slider: { + handles: 20, // handle size + values: 14, // values size + bar: 6, // bar size + valuesPosition: 'topz', // position of slider values + valuesAlign: 'left', // alignment of slider values + }, spacing: { x1: 5, x2: 10, From 3349eeb931f1b800195d54486faf8dd55988c3ca Mon Sep 17 00:00:00 2001 From: adria Date: Thu, 22 May 2025 11:59:19 -0600 Subject: [PATCH 012/118] push linting fixes --- .../pike/components/molecules/facetSlider.ts | 140 ++++++++++++++---- .../components/src/themes/pike/custom.ts | 3 +- 2 files changed, 111 insertions(+), 32 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index f42ce2ff0..e91e79f50 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -8,31 +8,55 @@ import Color from 'color'; const facetSliderStyleScript = (props: FacetSliderProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const valuesTop = custom.slider.valuesPosition == 'top' ? true : false; - const valuesLeft = custom.slider.valuesAlign == 'left' ? true : false; + const fontColor = props?.valueTextColor || variables?.colors?.text; + //const valuesTop = custom.slider.valuesPosition == 'top' ? true : false; + const valuesSides = custom.slider.valuesAlign == 'sides' ? true : false; const handleColor = new Color(props?.handleColor || variables?.colors?.primary); const handleInnerColor = handleColor.isDark() || handleColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); - // set slider height - const sliderHeight = custom.slider.values + custom.slider.handles + custom.slider.handles / 2 + (props?.showTicks ? 20 : 0); + // // set slider height + // const sliderHeight = custom.slider.values + custom.slider.handles + custom.slider.handles / 2 + (props?.showTicks ? 20 : 0); - // determine when to add top margin to slider - let addTopMargin = false; - if (props?.stickyHandleLabel) { - if (props?.showTicks && !valuesTop) { - addTopMargin = true; - } - } + // // determine when to add top margin to slider + // let addTopMargin = false; + // if (props?.stickyHandleLabel) { + // if (props?.showTicks && !valuesTop) { + // addTopMargin = true; + // } + // } - return css({ - height: `${sliderHeight}px`, + // values font styles + const valuesStyles = css({ + fontSize: `${custom.slider.values}px`, + lineHeight: `${custom.slider.values}px`, + color: fontColor, + }); + + // shared slider styles + const sharedStyles = css({ + //height: `${sliderHeight}px`, + border: `1px solid #ff00ff`, '&, *': { boxSizing: 'border-box', }, '&, .ss__facet-slider__slider': { margin: 'auto', }, + // '&:before': { + // content: '""', + // display: 'block', + // height: '8px', + // backgroundColor: 'red', + // order: 0, + // }, + // '&:after': { + // content: '""', + // display: 'block', + // height: '8px', + // backgroundColor: 'orange', + // order: 2, + // }, '.ss__facet-slider__slider button, .ss__facet-slider__labels label': { margin: 0, padding: 0, @@ -44,10 +68,13 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { display: 'block', top: 0, width: '100%', - marginTop: addTopMargin ? `${custom.slider.handles / 2}px` : '', - '&, .ss__facet-slider__handles': { - height: `${custom.slider.bar}px`, - }, + //border: `1px solid green`, + //marginTop: addTopMargin ? `${custom.slider.handles / 2}px` : '', + //order: 1, + //margin: `7px auto`, + height: `${custom.slider.bar}px`, + // '&, .ss__facet-slider__handles': { + // }, '.ss__facet-slider__tick': { '.ss__facet-slider__tick__label': { color: props?.tickTextColor || variables?.colors?.text, @@ -64,7 +91,8 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }, '.ss__facet-slider__handles': { position: 'relative', - margin: `0 ${custom.slider.handles / 2 - 1}px`, + margin: `0 ${custom.slider.handles / 2 - 2}px`, + height: '100%', button: { '.ss__facet-slider__handle': { transform: 'none', @@ -80,9 +108,8 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { border: `1px solid ${handleInnerColor.hex()}`, }, '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { - top: `${valuesTop ? '-' : ''}${valuesTop ? custom.slider.handles + custom.spacing.x1 : custom.slider.handles + custom.spacing.x5}px`, - fontSize: `${custom.slider.values}px`, - color: props?.valueTextColor || variables?.colors?.text, + //top: `${valuesTop ? '-' : ''}${valuesTop ? custom.slider.handles + custom.spacing.x1 : custom.slider.handles + custom.spacing.x5}px`, + valuesStyles, }, }, }, @@ -92,22 +119,73 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { display: 'flex', flexFlow: 'row nowrap', alignItems: 'center', - justifyContent: valuesLeft ? '' : 'center', - fontSize: `${custom.slider.values}px`, - lineHeight: `${custom.slider.values}px`, - order: valuesTop ? -1 : '', - margin: valuesTop ? `0 0 ${custom.spacing.x2}px 0` : `${custom.spacing.x2}px 0 0 0`, + justifyContent: valuesSides ? '' : 'center', + // order: valuesTop ? -1 : '', + // margin: valuesTop ? `0 0 ${custom.spacing.x2}px 0` : `${custom.spacing.x2}px 0 0 0`, + // order: 3, + // border: `1px solid blue`, '.ss__facet-slider__label': { - color: props?.valueTextColor || variables?.colors?.text, + valuesStyles, '&:after': { - display: valuesLeft ? 'none' : '', + display: valuesSides ? 'none' : '', + padding: `0 ${custom.spacing.x1}px`, }, '& ~ .ss__facet-slider__label': { - marginLeft: valuesLeft ? 'auto' : '', + marginLeft: valuesSides ? 'auto' : '', }, }, }, }); + + // spacing styles for different configurations + // note: default for facet slider is no ticks, no stick handles, values bottom + const spacingStyles = css({ + '.ss__facet-slider__slider': { + margin: `${(custom.slider.handles - custom.slider.bar) / 2}px auto`, + '&, .ss__facet-slider__handles': {}, + '.ss__facet-slider__tick': { + '.ss__facet-slider__tick__label': {}, + }, + '.ss__facet-slider__segment': {}, + '.ss__facet-slider__rail': {}, + '.ss__facet-slider__handles': { + button: { + '.ss__facet-slider__handle': { + '&:after': {}, + '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': {}, + }, + }, + }, + }, + '.ss__facet-slider__labels': { + margin: `${custom.spacing.x2}px 0 0 0`, + '.ss__facet-slider__label': {}, + }, + }); + + // spacingStyles = css({ + // '.ss__facet-slider__slider': { + // '&, .ss__facet-slider__handles': {}, + // '.ss__facet-slider__tick': { + // '.ss__facet-slider__tick__label': {}, + // }, + // '.ss__facet-slider__segment': {}, + // '.ss__facet-slider__rail': {}, + // '.ss__facet-slider__handles': { + // button: { + // '.ss__facet-slider__handle': { + // '&:after': {}, + // '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': {}, + // }, + // }, + // }, + // }, + // '.ss__facet-slider__labels': { + // '.ss__facet-slider__label': {}, + // }, + // }); + + return css([sharedStyles, spacingStyles]); }; // FacetSlider component props @@ -115,8 +193,8 @@ export const facetSlider: ThemeComponent<'facetSlider', FacetSliderProps> = { default: { props: { themeStyleScript: facetSliderStyleScript, - showTicks: true, - stickyHandleLabel: true, + // showTicks: true, + // stickyHandleLabel: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 2f212c291..21d1fd1d3 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -59,8 +59,9 @@ export const custom: { handles: 20, // handle size values: 14, // values size bar: 6, // bar size + ticks: 20, // size of ticks valuesPosition: 'topz', // position of slider values - valuesAlign: 'left', // alignment of slider values + valuesAlign: 'sidesz', // alignment of slider values }, spacing: { x1: 5, From 0e7d9b525238f4a11fb7e390c2ed0e5d3f026abd Mon Sep 17 00:00:00 2001 From: adria Date: Thu, 22 May 2025 17:44:06 -0600 Subject: [PATCH 013/118] more slider work --- .../molecules/facetPaletteOptions.ts | 4 +- .../pike/components/molecules/facetSlider.ts | 99 ++++++++++++++----- 2 files changed, 77 insertions(+), 26 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 4f2c00e92..0da4701a8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -13,12 +13,14 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { const palettePrefix = '.ss__facet-palette-options__option__palette'; const lightColors = `${palettePrefix}--white, ${palettePrefix}--ivory, ${palettePrefix}--clear, ${palettePrefix}--transparent`; - // shared palette styles + // light border styles styles const lightBorderStyles = css({ borderColor: custom.colors.gray02, opacity: 1, visibility: 'visible', }); + + // shared palette styles const sharedStyles = css({ '.ss__facet-palette-options__option': { color: variables?.colors?.text, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index e91e79f50..cff273b6d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -9,8 +9,10 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const fontColor = props?.valueTextColor || variables?.colors?.text; - //const valuesTop = custom.slider.valuesPosition == 'top' ? true : false; + const valuesTop = custom.slider.valuesPosition == 'top' ? true : false; const valuesSides = custom.slider.valuesAlign == 'sides' ? true : false; + const hasTicks = props?.showTicks ? true : false; + const hasStickyHandles = props?.stickyHandleLabel ? true : false; const handleColor = new Color(props?.handleColor || variables?.colors?.primary); const handleInnerColor = handleColor.isDark() || handleColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); @@ -65,6 +67,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }, }, '.ss__facet-slider__slider': { + border: `1px solid green`, display: 'block', top: 0, width: '100%', @@ -72,12 +75,16 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { //marginTop: addTopMargin ? `${custom.slider.handles / 2}px` : '', //order: 1, //margin: `7px auto`, - height: `${custom.slider.bar}px`, + //height: `${custom.slider.bar}px`, // '&, .ss__facet-slider__handles': { // }, + '.ss__facet-slider__segment, .ss__facet-slider__rail, .ss__facet-slider__handles': { + height: `${custom.slider.bar}px`, + }, '.ss__facet-slider__tick': { '.ss__facet-slider__tick__label': { color: props?.tickTextColor || variables?.colors?.text, + lineHeight: 1, }, }, '.ss__facet-slider__segment': { @@ -92,7 +99,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { '.ss__facet-slider__handles': { position: 'relative', margin: `0 ${custom.slider.handles / 2 - 2}px`, - height: '100%', + //height: '100%', button: { '.ss__facet-slider__handle': { transform: 'none', @@ -109,7 +116,9 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }, '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { //top: `${valuesTop ? '-' : ''}${valuesTop ? custom.slider.handles + custom.spacing.x1 : custom.slider.handles + custom.spacing.x5}px`, - valuesStyles, + '&': { + ...valuesStyles, + }, }, }, }, @@ -123,9 +132,11 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { // order: valuesTop ? -1 : '', // margin: valuesTop ? `0 0 ${custom.spacing.x2}px 0` : `${custom.spacing.x2}px 0 0 0`, // order: 3, - // border: `1px solid blue`, + border: `1px solid blue`, '.ss__facet-slider__label': { - valuesStyles, + '&': { + ...valuesStyles, + }, '&:after': { display: valuesSides ? 'none' : '', padding: `0 ${custom.spacing.x1}px`, @@ -137,32 +148,70 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }, }); + // spacing calculations + const handlesMinusBar = custom.slider.handles - custom.slider.bar; + const handlesMinusBarHalf = handlesMinusBar / 2; + const valuesPlusSpacing = custom.slider.values + custom.spacing.x2; + // spacing styles for different configurations // note: default for facet slider is no ticks, no stick handles, values bottom - const spacingStyles = css({ + let spacingStyles = css({ '.ss__facet-slider__slider': { - margin: `${(custom.slider.handles - custom.slider.bar) / 2}px auto`, - '&, .ss__facet-slider__handles': {}, - '.ss__facet-slider__tick': { - '.ss__facet-slider__tick__label': {}, - }, - '.ss__facet-slider__segment': {}, - '.ss__facet-slider__rail': {}, - '.ss__facet-slider__handles': { - button: { - '.ss__facet-slider__handle': { - '&:after': {}, - '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': {}, - }, - }, - }, + margin: `${handlesMinusBarHalf}px auto`, }, '.ss__facet-slider__labels': { margin: `${custom.spacing.x2}px 0 0 0`, - '.ss__facet-slider__label': {}, }, }); + if (valuesTop) { + } else { + if (hasTicks && hasStickyHandles) { + spacingStyles = css({ + '.ss__facet-slider__slider': { + margin: `${handlesMinusBarHalf}px 0 ${custom.slider.ticks + valuesPlusSpacing}px 0`, + '.ss__facet-slider__handles': { + button: { + '.ss__facet-slider__handle': { + '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { + top: 'auto', + bottom: `-${custom.slider.ticks + custom.spacing.x2}px`, + }, + }, + }, + }, + }, + }); + } else if (hasTicks && !hasStickyHandles) { + spacingStyles = css({ + '.ss__facet-slider__slider': { + padding: `${handlesMinusBarHalf}px 0 ${custom.slider.bar + custom.slider.ticks}px 0`, + }, + '.ss__facet-slider__labels': { + margin: `${custom.spacing.x2}px 0 0 0`, + }, + }); + } else if (!hasTicks && hasStickyHandles) { + spacingStyles = css({ + '.ss__facet-slider__slider': { + padding: `${handlesMinusBarHalf}px 0 ${custom.slider.bar + handlesMinusBarHalf + valuesPlusSpacing}px 0`, + '.ss__facet-slider__handles': { + button: { + '.ss__facet-slider__handle': { + '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { + top: 'auto', + bottom: `-${valuesPlusSpacing}px`, + }, + }, + }, + }, + }, + }); + } else { + // don't do anything -- this is the default + } + } + // spacingStyles = css({ // '.ss__facet-slider__slider': { // '&, .ss__facet-slider__handles': {}, @@ -193,8 +242,8 @@ export const facetSlider: ThemeComponent<'facetSlider', FacetSliderProps> = { default: { props: { themeStyleScript: facetSliderStyleScript, - // showTicks: true, - // stickyHandleLabel: true, + showTicks: true, + stickyHandleLabel: true, }, }, }; From 4a60b7dada911bcd80dbdfd70a87faee3096401e Mon Sep 17 00:00:00 2001 From: adria Date: Fri, 23 May 2025 18:20:35 -0600 Subject: [PATCH 014/118] finish slider styles --- .../pike/components/molecules/facetSlider.ts | 179 ++++++------------ .../components/src/themes/pike/custom.ts | 6 +- 2 files changed, 62 insertions(+), 123 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index cff273b6d..3593f7a27 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -17,17 +17,6 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { const handleInnerColor = handleColor.isDark() || handleColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); - // // set slider height - // const sliderHeight = custom.slider.values + custom.slider.handles + custom.slider.handles / 2 + (props?.showTicks ? 20 : 0); - - // // determine when to add top margin to slider - // let addTopMargin = false; - // if (props?.stickyHandleLabel) { - // if (props?.showTicks && !valuesTop) { - // addTopMargin = true; - // } - // } - // values font styles const valuesStyles = css({ fontSize: `${custom.slider.values}px`, @@ -37,28 +26,12 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { // shared slider styles const sharedStyles = css({ - //height: `${sliderHeight}px`, - border: `1px solid #ff00ff`, '&, *': { boxSizing: 'border-box', }, '&, .ss__facet-slider__slider': { margin: 'auto', }, - // '&:before': { - // content: '""', - // display: 'block', - // height: '8px', - // backgroundColor: 'red', - // order: 0, - // }, - // '&:after': { - // content: '""', - // display: 'block', - // height: '8px', - // backgroundColor: 'orange', - // order: 2, - // }, '.ss__facet-slider__slider button, .ss__facet-slider__labels label': { margin: 0, padding: 0, @@ -67,22 +40,23 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }, }, '.ss__facet-slider__slider': { - border: `1px solid green`, display: 'block', top: 0, width: '100%', - //border: `1px solid green`, - //marginTop: addTopMargin ? `${custom.slider.handles / 2}px` : '', - //order: 1, - //margin: `7px auto`, - //height: `${custom.slider.bar}px`, - // '&, .ss__facet-slider__handles': { - // }, + height: `${custom.slider.bar}px`, '.ss__facet-slider__segment, .ss__facet-slider__rail, .ss__facet-slider__handles': { - height: `${custom.slider.bar}px`, + height: '100%', }, '.ss__facet-slider__tick': { + '&:before, .ss__facet-slider__tick__label': { + transform: 'translate(-50%, 0)', + }, + '&:before': { + top: `${custom.slider.ticks / 2}px`, + backgroundColor: custom.colors.gray03, + }, '.ss__facet-slider__tick__label': { + top: `${custom.slider.ticks}px`, color: props?.tickTextColor || variables?.colors?.text, lineHeight: 1, }, @@ -99,7 +73,6 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { '.ss__facet-slider__handles': { position: 'relative', margin: `0 ${custom.slider.handles / 2 - 2}px`, - //height: '100%', button: { '.ss__facet-slider__handle': { transform: 'none', @@ -115,7 +88,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { border: `1px solid ${handleInnerColor.hex()}`, }, '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { - //top: `${valuesTop ? '-' : ''}${valuesTop ? custom.slider.handles + custom.spacing.x1 : custom.slider.handles + custom.spacing.x5}px`, + backgroundColor: 'transparent', '&': { ...valuesStyles, }, @@ -129,10 +102,6 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { flexFlow: 'row nowrap', alignItems: 'center', justifyContent: valuesSides ? '' : 'center', - // order: valuesTop ? -1 : '', - // margin: valuesTop ? `0 0 ${custom.spacing.x2}px 0` : `${custom.spacing.x2}px 0 0 0`, - // order: 3, - border: `1px solid blue`, '.ss__facet-slider__label': { '&': { ...valuesStyles, @@ -148,92 +117,64 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }, }); - // spacing calculations - const handlesMinusBar = custom.slider.handles - custom.slider.bar; - const handlesMinusBarHalf = handlesMinusBar / 2; - const valuesPlusSpacing = custom.slider.values + custom.spacing.x2; + // spacing and size calculations + const handlesSizeHalf = (custom.slider.handles - custom.slider.bar) / 2; + const handlesSpacing = custom.slider.handles + custom.spacing.x2; + const ticksSpacing = custom.slider.ticks + custom.spacing.x1; + const stickySpacing = custom.slider.values + custom.spacing.x2; + const handlesPlusSticky = handlesSizeHalf + stickySpacing; + const ticksPlusSticky = ticksSpacing + stickySpacing; // spacing styles for different configurations // note: default for facet slider is no ticks, no stick handles, values bottom - let spacingStyles = css({ - '.ss__facet-slider__slider': { - margin: `${handlesMinusBarHalf}px auto`, - }, - '.ss__facet-slider__labels': { - margin: `${custom.spacing.x2}px 0 0 0`, - }, - }); + let spacingStyles = css({}); - if (valuesTop) { - } else { - if (hasTicks && hasStickyHandles) { - spacingStyles = css({ - '.ss__facet-slider__slider': { - margin: `${handlesMinusBarHalf}px 0 ${custom.slider.ticks + valuesPlusSpacing}px 0`, - '.ss__facet-slider__handles': { - button: { - '.ss__facet-slider__handle': { - '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { - top: 'auto', - bottom: `-${custom.slider.ticks + custom.spacing.x2}px`, - }, - }, - }, + if (hasTicks && hasStickyHandles) { + spacingStyles = css({ + '.ss__facet-slider__slider': { + margin: `${valuesTop ? handlesPlusSticky : handlesSizeHalf}px auto ${valuesTop ? ticksSpacing : ticksPlusSticky}px auto`, + '.ss__facet-slider__handles button .ss__facet-slider__handle': { + '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { + top: valuesTop ? `auto` : `${handlesSizeHalf + ticksPlusSticky - custom.slider.bar}px`, + bottom: valuesTop ? `${handlesSpacing}px` : ``, }, }, - }); - } else if (hasTicks && !hasStickyHandles) { - spacingStyles = css({ - '.ss__facet-slider__slider': { - padding: `${handlesMinusBarHalf}px 0 ${custom.slider.bar + custom.slider.ticks}px 0`, - }, - '.ss__facet-slider__labels': { - margin: `${custom.spacing.x2}px 0 0 0`, - }, - }); - } else if (!hasTicks && hasStickyHandles) { - spacingStyles = css({ - '.ss__facet-slider__slider': { - padding: `${handlesMinusBarHalf}px 0 ${custom.slider.bar + handlesMinusBarHalf + valuesPlusSpacing}px 0`, - '.ss__facet-slider__handles': { - button: { - '.ss__facet-slider__handle': { - '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { - top: 'auto', - bottom: `-${valuesPlusSpacing}px`, - }, - }, - }, + }, + }); + } else if (hasTicks && !hasStickyHandles) { + spacingStyles = css({ + '.ss__facet-slider__slider': { + margin: `${handlesSizeHalf}px auto ${ticksSpacing}px auto`, + }, + '.ss__facet-slider__labels': { + order: valuesTop ? -1 : '', + margin: `${valuesTop ? 0 : custom.spacing.x2}px 0 ${valuesTop ? custom.spacing.x2 : 0}px 0`, + }, + }); + } else if (!hasTicks && hasStickyHandles) { + spacingStyles = css({ + '.ss__facet-slider__slider': { + margin: `${valuesTop ? handlesPlusSticky : handlesSizeHalf}px auto ${valuesTop ? handlesSizeHalf : handlesPlusSticky}px auto`, + '.ss__facet-slider__handles button .ss__facet-slider__handle': { + '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { + top: valuesTop ? 'auto' : `${handlesSpacing}px`, + bottom: valuesTop ? `${handlesSpacing}px` : ``, }, }, - }); - } else { - // don't do anything -- this is the default - } + }, + }); + } else { + spacingStyles = css({ + '.ss__facet-slider__slider': { + margin: `${handlesSizeHalf}px auto`, + }, + '.ss__facet-slider__labels': { + order: valuesTop ? -1 : '', + margin: `${valuesTop ? 0 : custom.spacing.x2}px 0 ${valuesTop ? custom.spacing.x2 : 0}px 0`, + }, + }); } - // spacingStyles = css({ - // '.ss__facet-slider__slider': { - // '&, .ss__facet-slider__handles': {}, - // '.ss__facet-slider__tick': { - // '.ss__facet-slider__tick__label': {}, - // }, - // '.ss__facet-slider__segment': {}, - // '.ss__facet-slider__rail': {}, - // '.ss__facet-slider__handles': { - // button: { - // '.ss__facet-slider__handle': { - // '&:after': {}, - // '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': {}, - // }, - // }, - // }, - // }, - // '.ss__facet-slider__labels': { - // '.ss__facet-slider__label': {}, - // }, - // }); - return css([sharedStyles, spacingStyles]); }; @@ -242,8 +183,6 @@ export const facetSlider: ThemeComponent<'facetSlider', FacetSliderProps> = { default: { props: { themeStyleScript: facetSliderStyleScript, - showTicks: true, - stickyHandleLabel: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 21d1fd1d3..6c36a6248 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -59,9 +59,9 @@ export const custom: { handles: 20, // handle size values: 14, // values size bar: 6, // bar size - ticks: 20, // size of ticks - valuesPosition: 'topz', // position of slider values - valuesAlign: 'sidesz', // alignment of slider values + ticks: 17, // size of ticks + valuesPosition: 'top', // position of slider values (top or bottom) + valuesAlign: 'sides', // alignment of slider values (sides or center) }, spacing: { x1: 5, From 49c9b801a2193b103cb71080600871d681c4adfb Mon Sep 17 00:00:00 2001 From: adria Date: Sun, 25 May 2025 07:01:33 -0600 Subject: [PATCH 015/118] filter summary, update icon sizes, move facet slider variables --- .../src/themes/pike/components/atoms/icon.ts | 2 +- .../pike/components/molecules/checkbox.ts | 2 +- .../pike/components/molecules/facetSlider.ts | 50 ++++++++++++------- .../pike/components/molecules/filter.ts | 33 +++++++++++- .../themes/pike/components/molecules/radio.ts | 5 +- .../pike/components/molecules/select.ts | 6 +-- .../themes/pike/components/organisms/facet.ts | 13 ++--- .../components/organisms/filterSummary.ts | 19 ++++++- .../pike/components/organisms/sidebar.ts | 23 ++++++++- .../components/src/themes/pike/custom.ts | 16 ++---- 10 files changed, 119 insertions(+), 50 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts index 1fefcc025..262db874f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/icon.ts @@ -19,7 +19,7 @@ export const icon: ThemeComponent<'icon', IconProps> = { default: { props: { themeStyleScript: iconStyleScript, - size: custom.sizes.icons, + size: `${custom.sizes.icon16}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index 0cd8a1faf..25db263b8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -65,7 +65,7 @@ export const checkbox: ThemeComponent<'checkbox', CheckboxProps> = { props: { themeStyleScript: checkboxStyleScript, icon: custom.icons.check, - size: '14px', + size: `${custom.sizes.icon14}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index 3593f7a27..7a6c825bf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -6,11 +6,21 @@ import Color from 'color'; // CSS in JS style script for the FacetSlider component const facetSliderStyleScript = (props: FacetSliderProps) => { + // slider options + const slider = { + handles: 20, // handle size + values: 14, // values size + bar: 6, // bar size + ticks: 17, // size of ticks + valuesPosition: 'top', // position of slider values (top or bottom) + valuesAlign: 'sides', // alignment of slider values (sides or center) + }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const fontColor = props?.valueTextColor || variables?.colors?.text; - const valuesTop = custom.slider.valuesPosition == 'top' ? true : false; - const valuesSides = custom.slider.valuesAlign == 'sides' ? true : false; + const valuesTop = slider.valuesPosition == 'top' ? true : false; + const valuesSides = slider.valuesAlign == 'sides' ? true : false; const hasTicks = props?.showTicks ? true : false; const hasStickyHandles = props?.stickyHandleLabel ? true : false; const handleColor = new Color(props?.handleColor || variables?.colors?.primary); @@ -19,8 +29,8 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { // values font styles const valuesStyles = css({ - fontSize: `${custom.slider.values}px`, - lineHeight: `${custom.slider.values}px`, + fontSize: `${slider.values}px`, + lineHeight: `${slider.values}px`, color: fontColor, }); @@ -43,7 +53,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { display: 'block', top: 0, width: '100%', - height: `${custom.slider.bar}px`, + height: `${slider.bar}px`, '.ss__facet-slider__segment, .ss__facet-slider__rail, .ss__facet-slider__handles': { height: '100%', }, @@ -52,11 +62,11 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { transform: 'translate(-50%, 0)', }, '&:before': { - top: `${custom.slider.ticks / 2}px`, + top: `${slider.ticks / 2}px`, backgroundColor: custom.colors.gray03, }, '.ss__facet-slider__tick__label': { - top: `${custom.slider.ticks}px`, + top: `${slider.ticks}px`, color: props?.tickTextColor || variables?.colors?.text, lineHeight: 1, }, @@ -64,7 +74,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { '.ss__facet-slider__segment': { backgroundColor: props?.trackColor || custom.colors.gray01, border: `1px solid ${props?.trackColor || custom.colors.gray02}`, - borderRadius: `${custom.slider.bar}px`, + borderRadius: `${slider.bar}px`, }, '.ss__facet-slider__rail': { backgroundColor: props?.railColor || variables?.colors?.secondary, @@ -72,18 +82,18 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }, '.ss__facet-slider__handles': { position: 'relative', - margin: `0 ${custom.slider.handles / 2 - 2}px`, + margin: `0 ${slider.handles / 2 - 2}px`, button: { '.ss__facet-slider__handle': { transform: 'none', - width: `${custom.slider.handles}px`, - height: `${custom.slider.handles}px`, - lineHeight: `${custom.slider.handles}px`, + width: `${slider.handles}px`, + height: `${slider.handles}px`, + lineHeight: `${slider.handles}px`, backgroundColor: handleColor.hex(), border: `1px solid ${handleColor.hex()}`, '&:after': { - width: `${custom.slider.handles / 4}px`, - height: `${custom.slider.handles / 4}px`, + width: `${slider.handles / 4}px`, + height: `${slider.handles / 4}px`, backgroundColor: handleInnerColor.hex(), border: `1px solid ${handleInnerColor.hex()}`, }, @@ -118,10 +128,10 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }); // spacing and size calculations - const handlesSizeHalf = (custom.slider.handles - custom.slider.bar) / 2; - const handlesSpacing = custom.slider.handles + custom.spacing.x2; - const ticksSpacing = custom.slider.ticks + custom.spacing.x1; - const stickySpacing = custom.slider.values + custom.spacing.x2; + const handlesSizeHalf = (slider.handles - slider.bar) / 2; + const handlesSpacing = slider.handles + custom.spacing.x2; + const ticksSpacing = slider.ticks + custom.spacing.x1; + const stickySpacing = slider.values + custom.spacing.x2; const handlesPlusSticky = handlesSizeHalf + stickySpacing; const ticksPlusSticky = ticksSpacing + stickySpacing; @@ -135,7 +145,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { margin: `${valuesTop ? handlesPlusSticky : handlesSizeHalf}px auto ${valuesTop ? ticksSpacing : ticksPlusSticky}px auto`, '.ss__facet-slider__handles button .ss__facet-slider__handle': { '.ss__facet-slider__handle__label.ss__facet-slider__handle__label--sticky': { - top: valuesTop ? `auto` : `${handlesSizeHalf + ticksPlusSticky - custom.slider.bar}px`, + top: valuesTop ? `auto` : `${handlesSizeHalf + ticksPlusSticky - slider.bar}px`, bottom: valuesTop ? `${handlesSpacing}px` : ``, }, }, @@ -183,6 +193,8 @@ export const facetSlider: ThemeComponent<'facetSlider', FacetSliderProps> = { default: { props: { themeStyleScript: facetSliderStyleScript, + stickyHandleLabel: true, + showTicks: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts index e2a7e660a..74dc7b027 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts @@ -1,13 +1,38 @@ import { css } from '@emotion/react'; import type { FilterProps } from '../../../../components/Molecules/Filter'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Filter component const filterStyleScript = (props: FilterProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + display: 'block', + padding: 0, + '.ss__filter__button': { + position: 'relative', + height: 'auto', + lineHeight: 1.5, + padding: `${custom.spacing.x1}px ${custom.spacing.x2}px`, + paddingLeft: `${custom.spacing.x2 + custom.spacing.x2 + 10}px`, + border: `1px solid ${custom.colors.gray02}`, + fontWeight: 'normal', + color: variables?.colors?.text, + '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { + backgroundColor: custom.colors.gray01, + }, + '.ss__icon': { + position: 'absolute', + top: '10px', + left: `${custom.spacing.x2}px`, + }, + '.ss__filter__label': { + fontWeight: custom.fonts.weight01, + }, + }, + }); }; // Filter component props @@ -15,6 +40,12 @@ export const filter: ThemeComponent<'filter', FilterProps> = { default: { props: { themeStyleScript: filterStyleScript, + icon: custom.icons.close, + }, + components: { + '*filter icon': { + size: `${custom.sizes.icon10}px`, + }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index cb7b974e2..0a157261b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -30,6 +30,7 @@ const radioStyleScript = (props: RadioProps) => { display: 'none', }, '&.ss__radio--active': { + backgroundColor: custom.colors.white, borderColor: custom.colors.gray03, '.ss__icon': { display: 'block', @@ -63,12 +64,12 @@ export const radio: ThemeComponent<'radio', RadioProps> = { default: { props: { themeStyleScript: radioStyleScript, - size: '14px', + size: `${custom.sizes.icon14}px`, }, components: { '*radio icon': { icon: 'square', - size: '8px', + size: `${custom.sizes.icon10 - 2}px`, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 5155c713c..a0c899802 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -79,13 +79,13 @@ const selectStyleScript = (props: SelectProps) => { flexFlow: 'row nowrap', alignItems: 'center', gap: `${custom.spacing.x1}px`, - padding: `0 ${custom.spacing.x4 + custom.sizes.icons}px 0 ${custom.spacing.x2}px`, + padding: `0 ${custom.spacing.x4 + custom.sizes.icon}px 0 ${custom.spacing.x2}px`, height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, backgroundImage: `url(data:image/svg+xml;base64,${btoa(nativeIcon)})`, backgroundPosition: `right ${custom.spacing.x2}px center`, backgroundRepeat: 'no-repeat', - backgroundSize: `${custom.sizes.icons}px ${custom.sizes.icons}px`, + backgroundSize: `${custom.sizes.icon}px ${custom.sizes.icon}px`, '.ss__select__label': { fontWeight: custom.fonts.weight01, }, @@ -121,7 +121,7 @@ export const select: ThemeComponent<'select', SelectProps> = { native: false, }, '*select dropdown icon': { - size: '12px', + size: `${custom.sizes.icon12}px`, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index fd8d836c2..de7774992 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -9,7 +9,6 @@ const facetStyleScript = (props: FacetProps) => { const variables = props?.theme?.variables; return css({ - margin: ` 0 0 ${custom.spacing.x6}px 0`, '&.ss__facet--collapsed': { '.ss__facet__header': { '.ss__icon': { @@ -24,18 +23,14 @@ const facetStyleScript = (props: FacetProps) => { }, }, '.ss__facet__header': { - margin: ` 0 0 ${custom.spacing.x4}px 0`, - padding: ` 0 0 ${custom.spacing.x2}px 0`, gap: `${custom.spacing.x2}px`, - borderBottomColor: variables?.colors?.primary, fontSize: '16px', fontWeight: custom.fonts.weight02, - color: variables?.colors?.secondary, '.ss__icon': { transition: 'transform ease .5s', transform: 'rotate(180deg)', - width: '12px', - height: '12px', + width: `${custom.sizes.icon12}px`, + height: `${custom.sizes.icon12}px`, fill: variables?.colors?.primary, stroke: variables?.colors?.primary, }, @@ -59,8 +54,8 @@ const facetStyleScript = (props: FacetProps) => { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, '.ss__icon': { - width: '10px', - height: '10px', + width: `${custom.sizes.icon10}px`, + height: `${custom.sizes.icon10}px`, }, }, }); diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts index 33e54572e..ac8af6e92 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts @@ -8,7 +8,23 @@ const filterSummaryStyleScript = (props: FilterSummaryProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + display: 'flex', + flexFlow: 'row wrap', + alignItems: 'center', + '.ss__filter-summary__title': { + padding: `0 ${custom.spacing.x1}px 0 0`, + fontSize: '14px', + fontWeight: custom.fonts.weight02, + '&:after': { + content: '":"', + }, + }, + '.ss__filter-summary__filters': { + gap: `${custom.spacing.x1}px`, + margin: 0, + }, + }); }; // FilterSummary component props @@ -18,6 +34,7 @@ export const filterSummary: ThemeComponent<'filterSummary', FilterSummaryProps> themeStyleScript: filterSummaryStyleScript, clearAllIcon: custom.icons.close, filterIcon: custom.icons.close, + hideTitle: false, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts index d951c0b63..a74883c1b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts @@ -1,13 +1,34 @@ import { css } from '@emotion/react'; import type { SidebarProps } from '../../../../components/Organisms/Sidebar'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Sidebar component const sidebarStyleScript = (props: SidebarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__filter-summary .ss__filter-summary__title, .ss__facets .ss__facet .ss__facet__header': { + margin: ` 0 0 ${custom.spacing.x4}px 0`, + padding: ` 0 0 ${custom.spacing.x2}px 0`, + borderBottom: `2px solid ${variables?.colors?.primary}`, + fontSize: '16px', + fontWeight: custom.fonts.weight02, + color: variables?.colors?.secondary, + }, + '.ss__filter-summary, .ss__facet': { + margin: ` 0 0 ${custom.spacing.x6}px 0`, + }, + '.ss__filter-summary': { + display: 'block', + '.ss__filter-summary__title': { + '&:after': { + display: 'none', + }, + }, + }, + }); }; // Sidebar component props diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 6c36a6248..3dd03f60a 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -14,9 +14,6 @@ export const custom: { sizes: { [size: string]: any; }; - slider: { - [slider: string]: any; - }; spacing: { [size: string]: number; }; @@ -53,15 +50,10 @@ export const custom: { }, sizes: { height: 33, // refers to height for button and dropdown sizes - icons: 16, - }, - slider: { - handles: 20, // handle size - values: 14, // values size - bar: 6, // bar size - ticks: 17, // size of ticks - valuesPosition: 'top', // position of slider values (top or bottom) - valuesAlign: 'sides', // alignment of slider values (sides or center) + icon10: 10, + icon12: 12, + icon14: 14, + icon16: 16, }, spacing: { x1: 5, From 91f9309ada8ee419d1230a3cf3c53f485a3e4d4b Mon Sep 17 00:00:00 2001 From: adria Date: Sun, 25 May 2025 08:59:19 -0600 Subject: [PATCH 016/118] working on pagination --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../pike/components/atoms/paginationInfo.ts | 20 ++++++++++ .../pike/components/molecules/calloutBadge.ts | 20 ++++++++++ .../pike/components/molecules/facetSlider.ts | 2 - .../pike/components/molecules/pagination.ts | 39 ++++++++++++++++++- 5 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index b2b442881..2c75c0a96 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -6,7 +6,7 @@ import { SnapTemplatesConfig } from '@searchspring/snap-preact'; let config: SnapTemplatesConfig = { config: { - siteId: '8uyt2m', + siteId: '8uyt2m', // prvb79 language: 'en', currency: 'usd', platform: 'other', diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts new file mode 100644 index 000000000..dd0819fa0 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { PaginationInfoProps } from '../../../../components/Atoms/PaginationInfo'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Pagination component +const paginationInfoStyleScript = ({ theme }: PaginationInfoProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({}); +}; + +// PaginationInfo component props +export const paginationInfo: ThemeComponent<'paginationInfo', PaginationInfoProps> = { + default: { + props: { + themeStyleScript: paginationInfoStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts new file mode 100644 index 000000000..25a20adb8 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { CalloutBadgeProps } from '../../../../components/Molecules/CalloutBadge'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Search component +const calloutBadgeStyleScript = (props: CalloutBadgeProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + + return css({}); +}; + +// CalloutBadge component props +export const calloutBadge: ThemeComponent<'calloutBadge', CalloutBadgeProps> = { + default: { + props: { + themeStyleScript: calloutBadgeStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index 7a6c825bf..5718a1325 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -193,8 +193,6 @@ export const facetSlider: ThemeComponent<'facetSlider', FacetSliderProps> = { default: { props: { themeStyleScript: facetSliderStyleScript, - stickyHandleLabel: true, - showTicks: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts index 9eb1ef1ed..c8115ffa0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts @@ -1,13 +1,39 @@ import { css } from '@emotion/react'; import type { PaginationProps } from '../../../../components/Molecules/Pagination'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Pagination component const paginationStyleScript = (props: PaginationProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + nav: { + display: 'flex', + flexFlow: 'row wrap', + alignItems: 'center', + justifyContent: 'center', + '.ss__pagination__page, span': { + padding: `0 ${custom.spacing.x2}px`, + fontSize: '16px', + color: variables?.colors?.text, + }, + '.ss__pagination__page': {}, + }, + [`@media (min-width: ${variables?.breakpoints?.mobile}px)`]: { + nav: { + '.ss__pagination__page, span': { + padding: `0 ${custom.spacing.x1}px`, + fontSize: '14px', + }, + '.ss__icon': { + width: `${custom.sizes.icon12}px`, + height: `${custom.sizes.icon12}px`, + }, + }, + }, + }); }; // Pagination component props @@ -16,5 +42,16 @@ export const pagination: ThemeComponent<'pagination', PaginationProps> = { props: { themeStyleScript: paginationStyleScript, }, + components: { + '*pagination icon': { + size: `${custom.sizes.icon14}px`, + }, + '*pagination icon.prev': { + icon: custom.icons.arrowLeft, + }, + '*pagination icon.next': { + icon: custom.icons.arrowRight, + }, + }, }, }; From e22e741c33427596403d9d9659b78af58ad9d670 Mon Sep 17 00:00:00 2001 From: adria Date: Tue, 27 May 2025 16:13:04 -0600 Subject: [PATCH 017/118] fix a few things --- .../components/src/themes/pike/components/atoms/index.ts | 5 +++++ .../components/src/themes/pike/components/molecules/index.ts | 5 +++++ .../src/themes/pike/components/templates/searchSnapnco.ts | 3 +++ .../src/themes/pike/components/templates/searchSnappy.ts | 3 +++ 4 files changed, 16 insertions(+) diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts index 51f2a3a64..9be008bbd 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts @@ -7,6 +7,7 @@ import { dropdown } from './dropdown'; import { icon } from './icon'; import { image } from './image'; import { loadingBar } from './loadingBar'; +import { paginationInfo } from './paginationInfo'; import { price } from './price'; import { searchHeader } from './searchHeader'; import { skeleton } from './skeleton'; @@ -18,6 +19,7 @@ export const atoms: ThemeResponsiveComplete = { ...transformThemeComponent('icon', icon.default), ...transformThemeComponent('image', image.default), ...transformThemeComponent('loadingBar', loadingBar.default), + ...transformThemeComponent('paginationInfo', paginationInfo.default), ...transformThemeComponent('price', price.default), ...transformThemeComponent('searchHeader', searchHeader.default), ...transformThemeComponent('skeleton', skeleton.default), @@ -28,6 +30,7 @@ export const atoms: ThemeResponsiveComplete = { ...transformThemeComponent('icon', icon.mobile), ...transformThemeComponent('image', image.mobile), ...transformThemeComponent('loadingBar', loadingBar.mobile), + ...transformThemeComponent('paginationInfo', paginationInfo.mobile), ...transformThemeComponent('price', price.mobile), ...transformThemeComponent('searchHeader', searchHeader.mobile), ...transformThemeComponent('skeleton', skeleton.mobile), @@ -37,6 +40,7 @@ export const atoms: ThemeResponsiveComplete = { ...transformThemeComponent('icon', icon.tablet), ...transformThemeComponent('image', image.tablet), ...transformThemeComponent('loadingBar', loadingBar.tablet), + ...transformThemeComponent('paginationInfo', paginationInfo.tablet), ...transformThemeComponent('price', price.tablet), ...transformThemeComponent('searchHeader', searchHeader.tablet), ...transformThemeComponent('skeleton', skeleton.tablet), @@ -47,6 +51,7 @@ export const atoms: ThemeResponsiveComplete = { ...transformThemeComponent('icon', icon.desktop), ...transformThemeComponent('image', image.desktop), ...transformThemeComponent('loadingBar', loadingBar.desktop), + ...transformThemeComponent('paginationInfo', paginationInfo.desktop), ...transformThemeComponent('price', price.desktop), ...transformThemeComponent('searchHeader', searchHeader.desktop), ...transformThemeComponent('skeleton', skeleton.desktop), diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts index 3dc603f4f..479475aca 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts @@ -2,6 +2,7 @@ import { transformThemeComponent } from '../../../utils/transformThemeComponent' import { ThemeResponsiveComplete } from '../../../../providers'; // MOLECULES Imports +import { calloutBadge } from './calloutBadge'; import { carousel } from './carousel'; import { checkbox } from './checkbox'; import { errorHandler } from './errorHandler'; @@ -32,6 +33,7 @@ import { terms } from './terms'; export const molecules: ThemeResponsiveComplete = { default: { + ...transformThemeComponent('calloutBadge', calloutBadge.default), ...transformThemeComponent('carousel', carousel.default), ...transformThemeComponent('checkbox', checkbox.default), ...transformThemeComponent('errorHandler', errorHandler.default), @@ -61,6 +63,7 @@ export const molecules: ThemeResponsiveComplete = { ...transformThemeComponent('terms', terms.default), }, mobile: { + ...transformThemeComponent('calloutBadge', calloutBadge.mobile), ...transformThemeComponent('carousel', carousel.mobile), ...transformThemeComponent('checkbox', checkbox.mobile), ...transformThemeComponent('errorHandler', errorHandler.mobile), @@ -90,6 +93,7 @@ export const molecules: ThemeResponsiveComplete = { ...transformThemeComponent('terms', terms.mobile), }, tablet: { + ...transformThemeComponent('calloutBadge', calloutBadge.tablet), ...transformThemeComponent('carousel', carousel.tablet), ...transformThemeComponent('checkbox', checkbox.tablet), ...transformThemeComponent('errorHandler', errorHandler.tablet), @@ -119,6 +123,7 @@ export const molecules: ThemeResponsiveComplete = { ...transformThemeComponent('terms', terms.tablet), }, desktop: { + ...transformThemeComponent('calloutBadge', calloutBadge.desktop), ...transformThemeComponent('carousel', carousel.desktop), ...transformThemeComponent('checkbox', checkbox.desktop), ...transformThemeComponent('errorHandler', errorHandler.desktop), diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts index 10e5eb8fe..efc2c6e38 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts @@ -24,6 +24,9 @@ export const searchSnapnco: ThemeComponent<'searchSnapnco', SearchSnapncoProps> '*searchSnapnco button.sidebar-toggle': { icon: custom.icons.filter, }, + '*searchSnapnco filterSummary': { + hideTitle: false, + }, }, }, mobile: searchSnapncoThemeComponentProps.mobile, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts index fc2585211..f0e8045f4 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts @@ -24,6 +24,9 @@ export const searchSnappy: ThemeComponent<'searchSnappy', SearchSnappyProps> = { '*searchSnappy button.sidebar-toggle': { icon: custom.icons.filter, }, + '*searchSnappy filterSummary': { + hideTitle: false, + }, }, }, mobile: searchSnappyThemeComponentProps.mobile, From 80326a9c350c611bebb4bd13484986783dbd9cda Mon Sep 17 00:00:00 2001 From: adria Date: Fri, 30 May 2025 16:18:54 -0600 Subject: [PATCH 018/118] Update for templates --- .../themes/pike/components/molecules/facetGridOptions.ts | 2 +- .../pike/components/molecules/facetHierarchyOptions.ts | 2 +- .../themes/pike/components/molecules/facetListOptions.ts | 8 ++++---- .../pike/components/molecules/facetPaletteOptions.ts | 8 ++++---- .../src/themes/pike/components/molecules/terms.ts | 2 +- .../src/themes/pike/components/organisms/termsList.ts | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index a28b2747a..38ddb004d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -66,7 +66,7 @@ export const facetGridOptions: ThemeComponent<'facetGridOptions', FacetGridOptio default: { facetGridOptions: { themeStyleScript: facetGridOptionsStyleScript, - disableStyles: true, + //disableStyles: true, gridSize: '52px', gapSize: `${custom.spacing.x1}px`, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index aa6273429..830dd6afe 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -55,7 +55,7 @@ export const facetHierarchyOptions: ThemeComponent<'facetHierarchyOptions', Face default: { facetHierarchyOptions: { themeStyleScript: facetHierarchyOptionsStyleScript, - disableStyles: true, + //disableStyles: true, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 1c09daf8c..d7290dbf8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -45,10 +45,10 @@ export const facetListOptions: ThemeComponent<'facetListOptions', FacetListOptio default: { facetListOptions: { themeStyleScript: facetListOptionsStyleScript, - disableStyles: true, - }, - 'facetListOptions checkbox': { - disableStyles: false, + //disableStyles: true, }, + // 'facetListOptions checkbox': { + // disableStyles: false, + // }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 5d98f39b0..b3b1029d2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -175,7 +175,7 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal default: { facetPaletteOptions: { themeStyleScript: facetPaletteStyleScript, - disableStyles: true, + //disableStyles: true, hideIcon: true, gridSize: '52px', gapSize: `${custom.spacing.x1}px`, @@ -212,8 +212,8 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal }, }, }, - 'facetPaletteOptions checkbox': { - disableStyles: false, - }, + // 'facetPaletteOptions checkbox': { + // disableStyles: false, + // }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts index 02e9dbd51..e4f6d9fab 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts @@ -14,7 +14,7 @@ const termsStyleScript = (props: TermsProps) => { export const terms: ThemeComponent<'terms', TermsProps> = { default: { terms: { - styleScript: termsStyleScript, + themeStyleScript: termsStyleScript, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts index d8ecd1e21..26f38e55a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts @@ -14,7 +14,7 @@ const termsListStyleScript = (props: TermsListProps) => { export const termsList: ThemeComponent<'termsList', TermsListProps> = { default: { termsList: { - styleScript: termsListStyleScript, + themeStyleScript: termsListStyleScript, }, }, }; From 56a1b65cc095250808df670e35c1c52711f7519e Mon Sep 17 00:00:00 2001 From: adria Date: Tue, 3 Jun 2025 12:37:07 -0600 Subject: [PATCH 019/118] clean up styles and configurations with latest updates --- .../pike/components/atoms/searchHeader.ts | 17 +++++--------- .../pike/components/molecules/checkbox.ts | 4 ++-- .../molecules/facetHierarchyOptions.ts | 21 ++++++++++-------- .../components/molecules/facetListOptions.ts | 11 +++++----- .../components/molecules/layoutSelector.ts | 3 ++- .../themes/pike/components/molecules/radio.ts | 4 ++-- .../pike/components/molecules/radioList.ts | 6 ++--- .../pike/components/molecules/select.ts | 22 +++++++++++-------- .../themes/pike/components/organisms/facet.ts | 11 ++++++---- 9 files changed, 52 insertions(+), 47 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts index 39cb3ac7e..b6888e8ae 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts @@ -9,21 +9,21 @@ const searchHeaderStyleScript = (props: SearchHeaderProps) => { const variables = props?.theme?.variables; return css({ - h3: { + em: { + fontStyle: 'normal', + }, + '.ss__search-header__title': { margin: 0, fontWeight: custom.fonts.weight02, color: variables?.colors?.secondary, }, - h5: { + '.ss__search-header__subtitle': { margin: `${custom.spacing.x2}px 0 0 0`, fontSize: '16px', fontWeight: 400, color: variables?.colors?.text, }, - em: { - fontStyle: 'normal', - }, - '.ss__query': { + '.ss__search-header__results-query': { color: variables?.colors?.primary, }, }); @@ -34,11 +34,6 @@ export const searchHeader: ThemeComponent<'searchHeader', SearchHeaderProps> = { default: { searchHeader: { themeStyleScript: searchHeaderStyleScript, - titleText: (data) => { - const search = data?.search; - const query = search?.query?.string ? ` for "${search.query.string}"` : ``; - return search?.matchType == 'expanded' ? `We couldn't find an exact match${query}, but here's something similar:` : `Search Results${query}`; - }, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index b6ac0a43b..8db3f0c7f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -48,8 +48,8 @@ const checkboxStyleScript = (props: CheckboxProps) => { const nativeStyles = css([ sharedStyles, { - width: `${custom.sizes.icon}px`, - height: `${custom.sizes.icon}px`, + width: `${custom.sizes.icon16}px`, + height: `${custom.sizes.icon16}px`, border: `1px solid ${custom.colors.gray02}`, cursor: 'pointer', }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index 830dd6afe..bc4ac676a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -12,6 +12,7 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => '.ss__facet-hierarchy-options__option': { display: 'block', margin: `0 0 ${custom.spacing.x1}px 0`, + padding: 0, color: variables?.colors?.text, '&:last-child': { marginBottom: 0, @@ -21,6 +22,7 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => '.ss__facet-hierarchy-options__option__value__count': { position: 'relative', top: '-1px', + margin: 0, padding: `0 ${custom.spacing.x1}px`, opacity: 0.805, fontSize: '0.6rem', @@ -29,21 +31,19 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => }, '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--return': { '&:before': { - content: '"\\0000ab"', - display: 'inline-block', - height: '24px', + display: 'none', + }, + '.ss__icon': { position: 'relative', top: '1px', - padding: `0 ${custom.spacing.x1}px 0 0`, - fontSize: '24px', - color: 'inherit', - lineHeight: '24px', + margin: `0 ${custom.spacing.x1}px 0 0`, + padding: 0, }, }, '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, - '& ~ .ss__facet-hierarchy-options__option': { + '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { paddingLeft: `${custom.spacing.x6}px`, }, }, @@ -55,7 +55,10 @@ export const facetHierarchyOptions: ThemeComponent<'facetHierarchyOptions', Face default: { facetHierarchyOptions: { themeStyleScript: facetHierarchyOptionsStyleScript, - //disableStyles: true, + returnIcon: custom.icons.arrowLeft, + }, + 'facetHierarchyOptions icon': { + size: `${custom.sizes.icon12}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index d7290dbf8..d9116abfb 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -18,15 +18,17 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { '&:last-child': { marginBottom: 0, }, - '.ss__checkbox': { + '.ss__checkbox, .ss__radio': { position: 'absolute', - top: '1px', + top: '1.5px', left: 0, }, '.ss__facet-list-options__option__value': { + margin: 0, '.ss__facet-list-options__option__value__count': { position: 'relative', top: '-1px', + margin: 0, padding: `0 ${custom.spacing.x1}px`, opacity: 0.805, fontSize: '0.6rem', @@ -45,10 +47,7 @@ export const facetListOptions: ThemeComponent<'facetListOptions', FacetListOptio default: { facetListOptions: { themeStyleScript: facetListOptionsStyleScript, - //disableStyles: true, + respectSingleSelect: true, }, - // 'facetListOptions checkbox': { - // disableStyles: false, - // }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts index 6db077a51..e4f495709 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -61,11 +61,12 @@ export const layoutSelector: ThemeComponent<'layoutSelector', LayoutSelectorProp type: 'list', }, 'layoutSelector select': { - hideSelection: true, + hideSelection: false, separator: '', }, 'layoutSelector list': { hideTitleText: true, + hideOptionLabels: true, }, 'layoutSelector radioList': { hideTitleText: true, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index 9f0304e59..5cac39bb6 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -47,8 +47,8 @@ const radioStyleScript = (props: RadioProps) => { { lineHeight: 0, '.ss__radio__input': { - width: `${custom.sizes.icon}px`, - height: `${custom.sizes.icon}px`, + width: `${custom.sizes.icon16}px`, + height: `${custom.sizes.icon16}px`, border: `1px solid ${custom.colors.gray02}`, cursor: 'pointer', }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts index d49b46573..2319f667d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -22,12 +22,12 @@ const radioListStyleScript = (props: RadioListProps) => { '.ss__radio-list__options-wrapper': { '.ss__radio-list__option': { gap: `${custom.spacing.x2}px`, - '.ss__radio-list__option__icon, .ss__radio-list__option__label': { - padding: 0, - }, '&:last-child': { marginBottom: 0, }, + '.ss__radio-list__option__icon, .ss__radio-list__option__label': { + padding: 0, + }, }, '.ss__radio-list__option--selected': { fontWeight: custom.fonts.weight01, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 2e66c865b..dbe611e65 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -7,9 +7,6 @@ import { custom } from '../../custom'; const selectStyleScript = (props: SelectProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const nativeIcon = ``; // shared styles for select menus const sharedStyles = css({ @@ -22,12 +19,17 @@ const selectStyleScript = (props: SelectProps) => { const defaultStyles = css([ { '.ss__dropdown': { + width: '100%', '.ss__dropdown__button .ss__button, .ss__dropdown__content .ss__select__select': { ...sharedStyles, }, '.ss__dropdown__button': { '.ss__button': { + display: 'flex', padding: `0 ${custom.spacing.x2}px`, + '.ss__select__selection__icon': { + margin: 0, + }, '.ss__select__selection': { paddingRight: `${custom.spacing.x1}px`, fontWeight: 'normal', @@ -41,7 +43,9 @@ const selectStyleScript = (props: SelectProps) => { marginTop: `${custom.spacing.x2}px`, '.ss__select__select': { padding: `${custom.spacing.x2}px`, + margin: 0, '.ss__select__select__option': { + gap: `${custom.spacing.x2}px`, padding: 0, margin: `0 0 ${custom.spacing.x1}px 0`, color: 'inherit', @@ -79,17 +83,14 @@ const selectStyleScript = (props: SelectProps) => { flexFlow: 'row nowrap', alignItems: 'center', gap: `${custom.spacing.x1}px`, - padding: `0 ${custom.spacing.x4 + custom.sizes.icon}px 0 ${custom.spacing.x2}px`, + padding: `0 ${custom.spacing.x2}px`, height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, - backgroundImage: `url(data:image/svg+xml;base64,${btoa(nativeIcon)})`, - backgroundPosition: `right ${custom.spacing.x2}px center`, - backgroundRepeat: 'no-repeat', - backgroundSize: `${custom.sizes.icon}px ${custom.sizes.icon}px`, '.ss__select__label': { fontWeight: custom.fonts.weight01, }, '.ss__select__select': { + paddingRight: `${custom.spacing.x1}px`, backgroundColor: 'transparent', border: 'none', appearance: 'none', @@ -116,10 +117,13 @@ export const select: ThemeComponent<'select', SelectProps> = { iconOpen: custom.icons.arrowDown, iconClose: custom.icons.arrowDown, }, + 'select icon.open': { + size: `${custom.sizes.icon12}px`, + }, 'select dropdown button': { native: false, }, - 'select dropdown icon': { + 'select dropdown button icon': { size: `${custom.sizes.icon12}px`, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index 903e49049..350bd349b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -16,10 +16,12 @@ const facetStyleScript = (props: FacetProps) => { }, }, }, - '&.ss__facet--slider': { + '&.ss__facet--showing-all': { '.ss__facet__options': { - maxHeight: 'none', - overflow: 'visible', + maxHeight: `490px`, + overflowY: 'auto', + overflowX: 'hidden', + paddingRight: `${custom.spacing.x2}px`, }, }, '.ss__facet__header': { @@ -37,7 +39,8 @@ const facetStyleScript = (props: FacetProps) => { }, '.ss__facet__options': { marginTop: 0, - maxHeight: `490px`, + maxHeight: 'none', + overflow: 'visible', '&::-webkit-scrollbar': { width: '8px', height: '8px', From 81788003e6e0cfd4768be7a3ccf61f67d3fe3e75 Mon Sep 17 00:00:00 2001 From: adria Date: Tue, 3 Jun 2025 18:09:41 -0600 Subject: [PATCH 020/118] update styles, add filter summary flag for list styles, change sidebar and mobile sidebar --- .../snap-preact-demo/templates/src/index.ts | 6 +- .../pike/components/atoms/paginationInfo.ts | 6 +- .../src/themes/pike/components/atoms/price.ts | 11 +--- .../components/molecules/facetGridOptions.ts | 10 +++- .../molecules/facetPaletteOptions.ts | 56 ++++++++++++++----- .../pike/components/molecules/filter.ts | 18 +++--- .../pike/components/molecules/result.ts | 28 +++++++++- .../components/organisms/filterSummary.ts | 47 ++++++++++++++++ .../components/organisms/mobileSidebar.ts | 29 +++++++++- .../pike/components/organisms/sidebar.ts | 25 ++++++--- .../components/src/themes/pike/custom.ts | 1 + 11 files changed, 188 insertions(+), 49 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index ba4b7d312..5a67cc0be 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -12,9 +12,9 @@ let config: SnapTemplatesConfig = { platform: 'other', }, components: { - result: { - CustomResult: async () => (await import('./components/Result')).CustomResult, - }, + // result: { + // CustomResult: async () => (await import('./components/Result')).CustomResult, + // }, }, theme: { extends: 'pike', diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts index 20d64d1bc..c94bcd29c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts @@ -1,13 +1,17 @@ import { css } from '@emotion/react'; import type { PaginationInfoProps } from '../../../../components/Atoms/PaginationInfo'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Pagination component const paginationInfoStyleScript = ({ theme }: PaginationInfoProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = theme?.variables; - return css({}); + return css({ + fontWeight: custom.fonts.weight02, + color: variables?.colors?.text, + }); }; // PaginationInfo component props diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts index a8c1b2c89..67310d786 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts @@ -1,7 +1,6 @@ import { css } from '@emotion/react'; import type { PriceProps } from '../../../../components/Atoms/Price'; import { ThemeComponent } from '../../../../providers'; -import { custom } from '../../custom'; // CSS in JS style script for the Price component const priceStyleScript = (props: PriceProps) => { @@ -9,13 +8,9 @@ const priceStyleScript = (props: PriceProps) => { const variables = props?.theme?.variables; return css({ - color: props?.name == 'price' ? variables?.colors?.primary : variables?.colors?.text, - '&:not(.ss__price--strike)': { - fontWeight: custom.fonts.weight01, - }, - '&.ss__price--strike': { - color: variables?.colors?.text, - opacity: 0.805, + color: variables?.colors?.text, + 'span, &.ss__price, &.ss__price--strike': { + color: 'inherit', }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 38ddb004d..05c7c71ea 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -12,15 +12,18 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { const fontColor = activeColor.isDark() || activeColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); return css({ - display: 'grid', gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', + '&:before': { + display: 'none', + }, '.ss__facet-grid-options__option': { position: 'relative', height: 0, paddingBottom: '100%', color: variables?.colors?.text, + border: 0, '&, &:before, .ss__facet-grid-options__option__value': { display: 'block', }, @@ -47,7 +50,9 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { maxHeight: `calc(100% - ${custom.spacing.x2}px)`, overflow: 'hidden', textAlign: 'center', - fontSize: '0.75rem', + '&, &.ss__facet-grid-options__option__value--smaller': { + fontSize: '0.75rem', + }, }, }, '.ss__facet-grid-options__option.ss__facet-grid-options__option--filtered': { @@ -66,7 +71,6 @@ export const facetGridOptions: ThemeComponent<'facetGridOptions', FacetGridOptio default: { facetGridOptions: { themeStyleScript: facetGridOptionsStyleScript, - //disableStyles: true, gridSize: '52px', gapSize: `${custom.spacing.x1}px`, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index b3b1029d2..191b2cd58 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -9,6 +9,11 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { const variables = props?.theme?.variables; const hasCheckbox = !props?.hideCheckbox ? true : false; + // set details for radius + const radius = 0; + const radiusUnit = 'px'; + const paletteRadius = radius ? `${radius}${radiusUnit}` : `0`; + // light colors selector const palettePrefix = '.ss__facet-palette-options__option__palette'; const lightColors = `${palettePrefix}--white, ${palettePrefix}--ivory, ${palettePrefix}--clear, ${palettePrefix}--transparent`; @@ -23,7 +28,15 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { // shared palette styles const sharedStyles = css({ '.ss__facet-palette-options__option': { + display: 'block', color: variables?.colors?.text, + '&, &.ss__facet-palette-options__option--filtered': { + '.ss__facet-palette-options__option__wrapper': { + border: 0, + borderRadius: 0, + padding: 0, + }, + }, '.ss__facet-palette-options__option__wrapper': { '&:before, .ss__facet-palette-options__option__palette, .ss__facet-palette-options__option__palette:before': { position: 'absolute', @@ -32,6 +45,7 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { left: 0, right: 0, margin: 'auto', + borderRadius: paletteRadius, }, '&:before, .ss__facet-palette-options__option__palette:before': { content: '""', @@ -45,6 +59,8 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { }, '.ss__facet-palette-options__option__palette': { zIndex: 1, + border: 0, + padding: 0, '&:before': { border: `1px solid ${custom.colors.white}`, }, @@ -77,13 +93,14 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { visibility: 'visible', }, '&:before': { - opacity: 0.4, + opacity: 0.3, }, '.ss__facet-palette-options__option__palette': { '&:before': { opacity: 1, margin: '1px', borderWidth: props?.layout == 'list' ? `${hasCheckbox ? 2 : 3}px` : `5px`, + borderRadius: radius ? `calc(${paletteRadius} - 1px)` : '', }, '&:not([style]):before': { borderColor: custom.colors.gray01, @@ -98,24 +115,25 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { }, }); - // default styles - const defaultStyles = css([ + // grid styles + const gridStyles = css([ sharedStyles, { - display: 'grid', gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', '.ss__facet-palette-options__option': { textAlign: 'center', + '&, &.ss__facet-palette-options__option--filtered': { + '.ss__facet-palette-options__option__wrapper': { + position: 'relative', + height: 0, + padding: '0 0 100% 0', + }, + }, '.ss__checkbox': { display: 'none', }, - '.ss__facet-palette-options__option__wrapper': { - position: 'relative', - height: 0, - paddingBottom: '100%', - }, '.ss__facet-palette-options__option__value, .ss__facet-palette-options__option__value__count': { display: 'block', lineHeight: '0.85rem', @@ -141,8 +159,10 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { const listStyles = css([ sharedStyles, { - '.ss__facet-palette-options__option': { + '&.ss__facet-palette-options--list': { display: 'block', + }, + '.ss__facet-palette-options__option': { position: 'relative', margin: `0 0 ${custom.spacing.x1}px 0`, padding: `${hasCheckbox ? 0 : custom.spacing.x1 + 'px'} 0 0 ${listPadding}px`, @@ -163,11 +183,21 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { height: `${listSize}px`, lineHeight: `${listSize}px`, }, + '.ss__facet-palette-options__option__value, .ss__facet-palette-options__option__value__count': { + display: 'inline', + overflow: 'visible', + textOverflow: 'unset', + textAlign: 'left', + whiteSpace: 'unset', + }, + '.ss__facet-palette-options__option__value__count': { + margin: 0, + }, }, }, ]); - return props?.layout == 'list' ? listStyles : defaultStyles; + return props?.layout == 'list' ? listStyles : gridStyles; }; // FacetPaletteOptions component props @@ -175,7 +205,6 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal default: { facetPaletteOptions: { themeStyleScript: facetPaletteStyleScript, - //disableStyles: true, hideIcon: true, gridSize: '52px', gapSize: `${custom.spacing.x1}px`, @@ -212,8 +241,5 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal }, }, }, - // 'facetPaletteOptions checkbox': { - // disableStyles: false, - // }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts index eacc3b0f9..877209cc4 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts @@ -16,20 +16,22 @@ const filterStyleScript = (props: FilterProps) => { height: 'auto', lineHeight: 1.5, padding: `${custom.spacing.x1}px ${custom.spacing.x2}px`, - paddingLeft: `${custom.spacing.x2 + custom.spacing.x2 + 10}px`, + paddingLeft: `${custom.spacing.x2 + custom.spacing.x1 + 10}px`, border: `1px solid ${custom.colors.gray02}`, fontWeight: 'normal', color: variables?.colors?.text, '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { backgroundColor: custom.colors.gray01, }, - '.ss__icon': { - position: 'absolute', - top: '10px', - left: `${custom.spacing.x2}px`, - }, - '.ss__filter__label': { - fontWeight: custom.fonts.weight01, + '.ss__button__content': { + '.ss__icon': { + position: 'absolute', + top: '10px', + left: `${custom.spacing.x2}px`, + }, + '.ss__filter__label': { + fontWeight: custom.fonts.weight01, + }, }, }, }); diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts index 0f89c1d10..5969c5e64 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts @@ -1,13 +1,39 @@ import { css } from '@emotion/react'; import type { ResultProps } from '../../../../components/Molecules/Result'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Result component const resultStyleScript = (props: ResultProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '&.ss__result--sale': { + '.ss__result__details': { + '.ss__result__details__pricing': { + '.ss__result__price:not(.ss__price--strike)': { + color: variables?.colors?.primary, + }, + }, + }, + }, + '.ss__result__details': { + '.ss__result__details__pricing': { + fontSize: '16px', + '.ss__result__price': { + fontSize: 'inherit', + '&:not(.ss__price--strike)': { + fontWeight: custom.fonts.weight01, + }, + }, + '.ss__price--strike': { + fontSize: '0.875rem', + opacity: 0.805, + }, + }, + }, + }); }; // Result component props diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts index ec0756d64..e2294ddef 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts @@ -12,10 +12,57 @@ const filterSummaryStyleScript = (props: FilterSummaryProps) => { display: 'flex', flexFlow: 'row wrap', alignItems: 'center', + '&.ss__filter-summary--list': { + '&, .ss__filter-summary__filters': { + display: 'block', + }, + '.ss__filter-summary__title:after': { + display: 'none', + }, + '.ss__filter-summary__filters': { + '.ss__filter': { + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-child': { + marginBottom: 0, + }, + '.ss__filter__button': { + padding: `0 0 0 ${16 + custom.spacing.x2}px`, + border: 0, + '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { + backgroundColor: 'transparent', + }, + '.ss__button__content': { + '&:before, .ss__icon': { + top: '1px', + left: 0, + }, + '&:before': { + content: '""', + display: 'block', + position: 'absolute', + + width: `${custom.sizes.icon14}px`, + height: `${custom.sizes.icon14}px`, + backgroundColor: custom.colors.white, + border: `1px solid ${custom.colors.gray03}`, + }, + '.ss__icon': { + margin: '4px', + width: `${custom.sizes.icon08}px`, + height: `${custom.sizes.icon08}px`, + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, + }, + }, + }, + }, + }, '.ss__filter-summary__title': { padding: `0 ${custom.spacing.x1}px 0 0`, fontSize: '14px', fontWeight: custom.fonts.weight02, + color: variables?.colors?.text, '&:after': { content: '":"', }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index be9c21a09..6d3e089d6 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -3,12 +3,36 @@ import type { MobileSidebarProps } from '../../../../components/Organisms/Mobile import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; +// Flag to determine if filter summary should be list style in sidebar +const enableSummaryList = true; + // CSS in JS style script for the MobileSidebar component const mobileSidebarStyleScript = (props: MobileSidebarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__layout': { + gap: `${custom.spacing.x6}px`, + '& > *': { + flex: `1 1 100%`, + }, + }, + '.ss__filter-summary .ss__filter-summary__title, .ss__facets .ss__facet .ss__facet__header': { + margin: ` 0 0 ${custom.spacing.x4}px 0`, + padding: ` 0 0 ${custom.spacing.x2}px 0`, + borderBottom: `2px solid ${variables?.colors?.primary}`, + fontSize: '16px', + fontWeight: custom.fonts.weight02, + color: variables?.colors?.secondary, + }, + '.ss__facets .ss__facet': { + margin: `0 0 ${custom.spacing.x6}px 0`, + '&:last-child': { + marginBottom: 0, + }, + }, + }); }; // MobileSidebar component props @@ -21,5 +45,8 @@ export const mobileSidebar: ThemeComponent<'mobileSidebar', MobileSidebarProps> 'mobileSidebar button.close': { icon: custom.icons.close, }, + 'mobileSidebar filterSummary': { + className: enableSummaryList ? 'ss__filter-summary--list' : '', + }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts index 53ea30152..8ec278271 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts @@ -3,12 +3,21 @@ import type { SidebarProps } from '../../../../components/Organisms/Sidebar'; import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; +// Flag to determine if filter summary should be list style in sidebar +const enableSummaryList = true; + // CSS in JS style script for the Sidebar component const sidebarStyleScript = (props: SidebarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; return css({ + '.ss__layout': { + gap: `${custom.spacing.x6}px`, + '& > *': { + flex: `1 1 100%`, + }, + }, '.ss__filter-summary .ss__filter-summary__title, .ss__facets .ss__facet .ss__facet__header': { margin: ` 0 0 ${custom.spacing.x4}px 0`, padding: ` 0 0 ${custom.spacing.x2}px 0`, @@ -17,15 +26,10 @@ const sidebarStyleScript = (props: SidebarProps) => { fontWeight: custom.fonts.weight02, color: variables?.colors?.secondary, }, - '.ss__filter-summary, .ss__facet': { - margin: ` 0 0 ${custom.spacing.x6}px 0`, - }, - '.ss__filter-summary': { - display: 'block', - '.ss__filter-summary__title': { - '&:after': { - display: 'none', - }, + '.ss__facets .ss__facet': { + margin: `0 0 ${custom.spacing.x6}px 0`, + '&:last-child': { + marginBottom: 0, }, }, }); @@ -37,5 +41,8 @@ export const sidebar: ThemeComponent<'sidebar', SidebarProps> = { sidebar: { themeStyleScript: sidebarStyleScript, }, + 'sidebar filterSummary': { + className: enableSummaryList ? 'ss__filter-summary--list' : '', + }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 3dd03f60a..7516d4cda 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -50,6 +50,7 @@ export const custom: { }, sizes: { height: 33, // refers to height for button and dropdown sizes + icon08: 8, icon10: 10, icon12: 12, icon14: 14, From a5fc6869434c187071f6f1f9c538feebb0bd5f9e Mon Sep 17 00:00:00 2001 From: adria Date: Sat, 26 Jul 2025 15:55:17 -0600 Subject: [PATCH 021/118] pagination and badges --- .../snap-preact-demo/templates/src/index.ts | 4 +-- .../pike/components/molecules/calloutBadge.ts | 21 ++++++++++--- .../pike/components/molecules/overlayBadge.ts | 24 +++++++++++--- .../pike/components/molecules/pagination.ts | 31 +++++++++++++++---- .../components/src/themes/pike/custom.ts | 10 +++--- 5 files changed, 67 insertions(+), 23 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 5a67cc0be..69b7481fb 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -6,7 +6,7 @@ import type { SnapTemplatesConfig } from '@searchspring/snap-preact'; let config: SnapTemplatesConfig = { config: { - siteId: '8uyt2m', // prvb79 + siteId: 'prvb79', // prvb79 // 8uyt2m language: 'en', currency: 'usd', platform: 'other', @@ -20,7 +20,7 @@ let config: SnapTemplatesConfig = { extends: 'pike', variables: { breakpoints: { - mobile: 768, + mobile: 767, tablet: 1024, desktop: 1280, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts index ea13fa74c..1db4dbf1b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts @@ -1,13 +1,23 @@ import { css } from '@emotion/react'; import type { CalloutBadgeProps } from '../../../../components/Molecules/CalloutBadge'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component -const calloutBadgeStyleScript = (props: CalloutBadgeProps) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = props?.theme?.variables; - - return css({}); +const calloutBadgeStyleScript = () => { + return css({ + gap: `${custom.spacing.x2}px`, + '& > div': { + padding: `${custom.spacing.x1}px ${custom.spacing.x2}px`, + lineHeight: 1, + span: { + fontSize: '12px', + }, + }, + '.ss__badge-text': { + padding: `0`, + }, + }); }; // CalloutBadge component props @@ -15,6 +25,7 @@ export const calloutBadge: ThemeComponent<'calloutBadge', CalloutBadgeProps> = { default: { calloutBadge: { themeStyleScript: calloutBadgeStyleScript, + limit: 3, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts index aafc8bb97..162607f93 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts @@ -1,13 +1,26 @@ import { css } from '@emotion/react'; import type { OverlayBadgeProps } from '../../../../components/Molecules/OverlayBadge'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component -const overlayBadgeStyleScript = (props: OverlayBadgeProps) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = props?.theme?.variables; - - return css({}); +const overlayBadgeStyleScript = () => { + return css({ + '.ss__overlay-badge__grid-wrapper': { + gap: `${custom.spacing.x1}px`, + bottom: 'auto', + '.ss__overlay-badge__grid-wrapper__slot': { + gap: 0, + '& > div': { + padding: `${custom.spacing.x1}px ${custom.spacing.x2}px`, + lineHeight: 1, + span: { + fontSize: '12px', + }, + }, + }, + }, + }); }; // OverlayBadge component props @@ -15,6 +28,7 @@ export const overlayBadge: ThemeComponent<'overlayBadge', OverlayBadgeProps> = { default: { overlayBadge: { themeStyleScript: overlayBadgeStyleScript, + limit: 3, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts index 592af110b..27edd3616 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const paginationStyleScript = (props: PaginationProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const desktopBp = variables?.breakpoints?.mobile || 767; return css({ nav: { @@ -14,22 +15,35 @@ const paginationStyleScript = (props: PaginationProps) => { flexFlow: 'row wrap', alignItems: 'center', justifyContent: 'center', + lineHeight: 1, '.ss__pagination__page, span': { padding: `0 ${custom.spacing.x2}px`, fontSize: '16px', color: variables?.colors?.text, }, - '.ss__pagination__page': {}, + '.ss__pagination__page': { + minWidth: '1px', + minHeight: '1px', + }, + '.ss__pagination__page--active': { + color: variables?.colors?.primary, + }, + '.ss__pagination__page--previous, .ss__pagination__page--next': { + lineHeight: `${custom.sizes.icon14}px`, + '.ss__icon': { + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, + }, }, - [`@media (min-width: ${variables?.breakpoints?.mobile}px)`]: { + [`@media (min-width: ${desktopBp + 1}px)`]: { nav: { '.ss__pagination__page, span': { padding: `0 ${custom.spacing.x1}px`, fontSize: '14px', }, - '.ss__icon': { - width: `${custom.sizes.icon12}px`, - height: `${custom.sizes.icon12}px`, + '.ss__pagination__page--previous, .ss__pagination__page--next': { + lineHeight: `${custom.sizes.icon12}px`, }, }, }, @@ -43,7 +57,7 @@ export const pagination: ThemeComponent<'pagination', PaginationProps> = { themeStyleScript: paginationStyleScript, }, 'pagination icon': { - size: `${custom.sizes.icon14}px`, + size: `${custom.sizes.icon12}px`, }, 'pagination icon.prev': { icon: custom.icons.arrowLeft, @@ -52,4 +66,9 @@ export const pagination: ThemeComponent<'pagination', PaginationProps> = { icon: custom.icons.arrowRight, }, }, + mobile: { + 'pagination icon': { + size: `${custom.sizes.icon14}px`, + }, + }, }; diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 7516d4cda..5270a5eef 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -3,19 +3,19 @@ import Color from 'color'; export const custom: { colors: { - [color: string]: string; + [key: string]: string; }; fonts: { - [font: string]: any; + [key: string]: any; }; icons: { - [icon: string]: IconType; + [key: string]: IconType; }; sizes: { - [size: string]: any; + [key: string]: number; }; spacing: { - [size: string]: number; + [key: string]: number; }; } = { colors: { From 03ee1d48e63c9e3de077d8bd0d79d140e777d718 Mon Sep 17 00:00:00 2001 From: adria Date: Thu, 31 Jul 2025 12:04:54 -0600 Subject: [PATCH 022/118] update result --- .../snap-preact-demo/templates/src/index.ts | 17 +++++++--- .../pike/components/molecules/rating.ts | 32 ++++++++++++++++++- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 69b7481fb..d0f7f3bad 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -12,12 +12,13 @@ let config: SnapTemplatesConfig = { platform: 'other', }, components: { - // result: { - // CustomResult: async () => (await import('./components/Result')).CustomResult, - // }, + result: { + CustomResult: async () => (await import('./components/Result')).CustomResult, + }, }, theme: { extends: 'pike', + //resultComponent: 'CustomResult', variables: { breakpoints: { mobile: 767, @@ -31,7 +32,13 @@ let config: SnapTemplatesConfig = { // }, }, style: globalStyles, - overrides: {}, + // overrides: { + // default: { + // 'toolbar.bottom': { + // layout: ['loadMore'] + // } + // } + // }, }, recommendation: { email: { @@ -55,7 +62,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchSnappy', + component: 'SearchSnapnco', }, ], }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts index 7903fec6c..3927b8362 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts @@ -1,13 +1,41 @@ import { css } from '@emotion/react'; import type { RatingProps } from '../../../../components/Molecules/Rating'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Rating component const ratingStyleScript = (props: RatingProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + flexWrap: 'wrap', + gap: `${custom.spacing.x1}px`, + lineHeight: 1, + '.ss__rating__icons': { + '.ss__rating__stars': { + margin: '0 -1px', + '.ss__rating__stars__star': { + margin: '0 1px', + }, + }, + '.ss__rating__stars--empty': { + '.ss__rating__stars__star .ss__icon': { + fill: custom.colors.gray03, + stroke: custom.colors.gray03, + }, + }, + '.ss__rating__stars--full': { + '.ss__rating__stars__star .ss__icon': { + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, + }, + }, + '.ss__rating__count, .ss__rating__text': { + color: variables?.colors?.text, + }, + }); }; // Rating component props @@ -15,6 +43,8 @@ export const rating: ThemeComponent<'rating', RatingProps> = { default: { rating: { themeStyleScript: ratingStyleScript, + emptyIcon: 'star', + fullIcon: 'star', }, }, }; From d4d30555b721ed6e150b83ee1b57abbbe32c21bd Mon Sep 17 00:00:00 2001 From: adria Date: Thu, 31 Jul 2025 17:22:38 -0600 Subject: [PATCH 023/118] remove breadcrumb test --- .../templates/src/components/Result.tsx | 5 ++- .../snap-preact-demo/templates/src/index.ts | 24 ++++++++---- .../pike/components/atoms/breadcrumbs.ts | 38 +++++++++++++++++++ .../themes/pike/components/atoms/button.ts | 17 ++++++--- .../themes/pike/components/atoms/dropdown.ts | 20 ---------- .../src/themes/pike/components/atoms/image.ts | 26 ++++++++++++- .../src/themes/pike/components/atoms/index.ts | 15 +++++--- .../themes/pike/components/atoms/overlay.ts | 23 +++++++++++ .../themes/pike/components/atoms/skeleton.ts | 11 +----- .../pike/components/molecules/loadMore.ts | 36 +++++++++++++++++- .../pike/components/molecules/searchInput.ts | 3 +- .../pike/components/molecules/slideout.ts | 2 +- 12 files changed, 166 insertions(+), 54 deletions(-) create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts delete mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts diff --git a/packages/snap-preact-demo/templates/src/components/Result.tsx b/packages/snap-preact-demo/templates/src/components/Result.tsx index 7501e36d7..2cc0a924e 100644 --- a/packages/snap-preact-demo/templates/src/components/Result.tsx +++ b/packages/snap-preact-demo/templates/src/components/Result.tsx @@ -1,5 +1,5 @@ import { h, Fragment } from 'preact'; -import { Price, Image, OverlayBadge, CalloutBadge } from '@searchspring/snap-preact/components'; +import { Price, Image, OverlayBadge, CalloutBadge, Rating } from '@searchspring/snap-preact/components'; export const CustomResult = (props) => { const { result, controller } = props; @@ -14,6 +14,7 @@ export const CustomResult = (props) => { + diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index d0f7f3bad..5396160a4 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -32,13 +32,16 @@ let config: SnapTemplatesConfig = { // }, }, style: globalStyles, - // overrides: { - // default: { - // 'toolbar.bottom': { - // layout: ['loadMore'] - // } - // } - // }, + overrides: { + default: { + // 'toolbar.top': { + // layout: ['breadcrumbs'] + // } + // 'toolbar.bottom': { + // layout: ['loadMore'] + // } + }, + }, }, recommendation: { email: { @@ -62,9 +65,14 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchSnapnco', + component: 'SearchSnappy', }, ], + settings: { + infinite: { + backfill: 5, + }, + }, }, autocomplete: { targets: [ diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts new file mode 100644 index 000000000..b75a87cda --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts @@ -0,0 +1,38 @@ +import { css } from '@emotion/react'; +import type { BreadcrumbsProps } from '../../../../components/Atoms/Breadcrumbs'; +import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; + +// CSS in JS style script for the Breadcrumbs component +const breadcrumbsStyleScript = (props: BreadcrumbsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + + return css({ + '.ss__breadcrumbs__crumbs': { + margin: `0 -${custom.spacing.x1}px`, + '&, .ss__breadcrumbs__crumbs__crumb': { + listStyle: 'none', + }, + '&, a': { + color: variables?.colors?.text, + }, + '.ss__breadcrumbs__crumbs__crumb': { + padding: `0 ${custom.spacing.x1}px`, + '&:last-child': { + color: variables?.colors?.primary, + fontWeight: custom?.fonts?.weight01, + }, + }, + }, + }); +}; + +// Breadcrumbs component props +export const breadcrumbs: ThemeComponent<'breadcrumbs', BreadcrumbsProps> = { + default: { + breadcrumbs: { + themeStyleScript: breadcrumbsStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index 8ff38d620..a1bc6cc13 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -11,9 +11,12 @@ const buttonStyleScript = (props: ButtonProps) => { const buttonColor = new Color(props?.backgroundColor || variables?.colors?.primary); const fontColor = buttonColor.isDark() || buttonColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); + // disabled button selectors + const disabledSelectors = '&.ss__button--disabled, &.ss__load-more__button.ss__load-more__button--disabled'; + // shared button styles const disabledStyles = css({ - '&.ss__button--disabled': { + [`${disabledSelectors}`]: { opacity: 0.65, '&, & *': { cursor: 'not-allowed', @@ -25,7 +28,6 @@ const buttonStyleScript = (props: ButtonProps) => { const defaultStyles = css([ { padding: `0 ${custom.spacing.x4}px`, - borderColor: buttonColor.hex(), color: fontColor.hex(), fontWeight: custom.fonts.weight01, textAlign: 'center', @@ -34,7 +36,8 @@ const buttonStyleScript = (props: ButtonProps) => { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { + [`&, &:hover, &:not(.ss__button--disabled):hover, ${disabledSelectors}`]: { + borderColor: buttonColor.hex(), backgroundColor: buttonColor.hex(), }, }, @@ -45,10 +48,12 @@ const buttonStyleScript = (props: ButtonProps) => { const nativeStyles = css([ { cursor: 'pointer', - border: `1px solid ${custom.colors.gray02}`, - '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { - color: variables?.colors?.text, + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + [`&, &:hover, &:not(.ss__button--disabled):hover, ${disabledSelectors}`]: { + border: `1px solid ${custom.colors.gray02}`, backgroundColor: custom.colors.white, + fontSize: '14px', + color: variables?.colors?.text, }, }, disabledStyles, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts deleted file mode 100644 index 225b2e351..000000000 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { css } from '@emotion/react'; -import type { DropdownProps } from '../../../../components/Atoms/Dropdown'; -import { ThemeComponent } from '../../../../providers'; - -// CSS in JS style script for the Dropdown component -const dropdownStyleScript = (props: DropdownProps) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = props?.theme?.variables; - - return css({}); -}; - -// Dropdown component props -export const dropdown: ThemeComponent<'dropdown', DropdownProps> = { - default: { - dropdown: { - themeStyleScript: dropdownStyleScript, - }, - }, -}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts index 2bbf65c6e..a48a80c0d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts @@ -7,7 +7,31 @@ const imageStyleScript = (props: ImageProps & { visibility: React.CSSProperties[ // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + position: 'relative', + lineHeight: 0, + height: 0, + padding: '0 0 150% 0', + overflow: 'hidden', + '&, img': { + display: 'block', + }, + img: { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + margin: 'auto', + width: '100%', + height: '100%', + maxWidth: '100%', + maxHeight: '100%', + border: 0, + objectFit: 'contain', + objectPosition: 'center 0', + }, + }); }; // Image component props diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts index fea74a51e..033879b8e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts @@ -1,11 +1,12 @@ import { ThemeResponsiveComplete } from '../../../../providers'; // ATOMS Imports +import { breadcrumbs } from './breadcrumbs'; import { button } from './button'; -import { dropdown } from './dropdown'; import { icon } from './icon'; import { image } from './image'; import { loadingBar } from './loadingBar'; +import { overlay } from './overlay'; import { paginationInfo } from './paginationInfo'; import { price } from './price'; import { searchHeader } from './searchHeader'; @@ -13,43 +14,47 @@ import { skeleton } from './skeleton'; export const atoms: ThemeResponsiveComplete = { default: { + ...breadcrumbs.default, ...button.default, - ...dropdown.default, ...icon.default, ...image.default, ...loadingBar.default, + ...overlay.default, ...price.default, ...searchHeader.default, ...skeleton.default, ...paginationInfo.default, }, mobile: { + ...breadcrumbs.mobile, ...button.mobile, - ...dropdown.mobile, ...icon.mobile, ...image.mobile, ...loadingBar.mobile, + ...overlay.mobile, ...price.mobile, ...searchHeader.mobile, ...skeleton.mobile, ...paginationInfo.mobile, }, tablet: { - ...dropdown.tablet, + ...breadcrumbs.tablet, ...icon.tablet, ...image.tablet, ...loadingBar.tablet, + ...overlay.tablet, ...price.tablet, ...searchHeader.tablet, ...skeleton.tablet, ...paginationInfo.tablet, }, desktop: { + ...breadcrumbs.desktop, ...button.desktop, - ...dropdown.desktop, ...icon.desktop, ...image.desktop, ...loadingBar.desktop, + ...overlay.desktop, ...price.desktop, ...searchHeader.desktop, ...skeleton.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts new file mode 100644 index 000000000..dc9b3203f --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts @@ -0,0 +1,23 @@ +import { css } from '@emotion/react'; +import type { OverlayProps } from '../../../../components/Atoms/Overlay'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Overlay component +const overlayStyleScript = (props: OverlayProps) => { + const backgroundColor = props?.color || 'rgba(0, 0, 0, 0.60)'; + + return css({ + '&, &.ss__overlay--active': { + background: backgroundColor, + }, + }); +}; + +// Overlay component props +export const overlay: ThemeComponent<'overlay', OverlayProps> = { + default: { + overlay: { + themeStyleScript: overlayStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts index ff912c9b4..62b806141 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/skeleton.ts @@ -1,22 +1,13 @@ -import { css } from '@emotion/react'; import type { SkeletonProps } from '../../../../components/Atoms/Skeleton'; import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; -// CSS in JS style script for the Skeleton component -const skeletonStyleScript = (props: SkeletonProps) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = props?.theme?.variables; - - return css({}); -}; - // Skeleton component props export const skeleton: ThemeComponent<'skeleton', SkeletonProps> = { default: { skeleton: { - themeStyleScript: skeletonStyleScript, backgroundColor: custom.colors.gray02, + animatedColor: custom.colors.gray01, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts index 04d0f7c22..75abbdb3b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/loadMore.ts @@ -1,13 +1,47 @@ import { css } from '@emotion/react'; import type { LoadMoreProps } from '../../../../components/Molecules/LoadMore'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the LoadMore component const loadMoreStyleScript = (props: LoadMoreProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const indicatorColor = new Color(props?.backgroundColor || custom.colors.gray01); + const indicatorBorderColor = new Color(props?.backgroundColor || custom.colors.gray02); + const barColor = new Color(props?.color || variables?.colors?.primary); - return css({}); + return css({ + '&.ss__load-more': { + '&, .ss__load-more__progress': { + gap: `${custom.spacing.x2}px`, + }, + '& > .ss__load-more__icon': { + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, + '.ss__button': { + '.ss__button__content': { + display: 'flex', + alignItems: 'center', + }, + }, + '.ss__load-more__progress': { + '.ss__load-more__progress__indicator': { + backgroundColor: indicatorColor.hex(), + border: `1px solid ${indicatorBorderColor}`, + '.ss__load-more__progress__indicator__bar': { + backgroundColor: barColor.hex(), + margin: '-1px', + }, + }, + '.ss__load-more__progress__text': { + color: variables?.colors?.text, + }, + }, + }, + }); }; // LoadMore component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index 21c3adb82..8db016a31 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -10,7 +10,7 @@ const searchInputStyleScript = (props: SearchInputProps) => { return css({ '&.ss__search-input': { - margin: `0 0 ${custom.spacing.x4}px`, + margin: `0 0 ${custom.spacing.x2}px`, border: `1px solid ${custom.colors.gray02}`, backgroundColor: `${custom.colors.gray01}`, '.ss__icon': { @@ -20,6 +20,7 @@ const searchInputStyleScript = (props: SearchInputProps) => { padding: `0 ${custom.spacing.x2}px`, backgroundColor: `inherit`, color: variables?.colors?.text, + minHeight: '1px', height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, '&::-webkit-input-placeholder': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts index 86942ab08..4e2cf3a48 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts @@ -3,7 +3,7 @@ import type { SlideoutProps } from '../../../../components/Molecules/Slideout'; import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Slideout component -const slideoutStyleScript = (props: SlideoutProps & { isActive: boolean }) => { +const slideoutStyleScript = (props: SlideoutProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; From 102cebbdd7f63351cbb36ba9009ef5fba5fb4842 Mon Sep 17 00:00:00 2001 From: adria Date: Fri, 1 Aug 2025 17:32:05 -0600 Subject: [PATCH 024/118] remove lint errors --- .../snap-preact-demo/templates/src/index.ts | 10 ++-- .../pike/components/atoms/breadcrumbs.ts | 3 -- .../themes/pike/components/atoms/button.ts | 1 - .../themes/pike/components/atoms/overlay.ts | 2 +- .../src/themes/pike/components/atoms/price.ts | 5 +- .../pike/components/atoms/searchHeader.ts | 3 +- .../pike/components/molecules/calloutBadge.ts | 2 +- .../pike/components/molecules/carousel.ts | 53 ++++++++++++++++--- .../pike/components/molecules/checkbox.ts | 5 +- .../components/molecules/facetGridOptions.ts | 3 +- .../molecules/facetHierarchyOptions.ts | 6 +-- .../components/molecules/facetListOptions.ts | 6 +-- .../molecules/facetPaletteOptions.ts | 8 +-- .../pike/components/molecules/facetSlider.ts | 4 +- .../pike/components/molecules/filter.ts | 4 +- .../themes/pike/components/molecules/list.ts | 2 +- .../pike/components/molecules/overlayBadge.ts | 2 +- .../pike/components/molecules/pagination.ts | 5 +- .../themes/pike/components/molecules/radio.ts | 3 +- .../pike/components/molecules/radioList.ts | 2 +- .../pike/components/molecules/rating.ts | 9 +++- .../pike/components/molecules/result.ts | 34 ++++++++++-- .../pike/components/molecules/searchInput.ts | 30 +++++++---- .../pike/components/molecules/select.ts | 17 ++++++ .../pike/components/molecules/slideout.ts | 1 + .../themes/pike/components/organisms/facet.ts | 2 +- .../components/organisms/filterSummary.ts | 5 +- .../components/organisms/mobileSidebar.ts | 2 +- .../pike/components/organisms/sidebar.ts | 2 +- .../templates/autocompleteTemplate.ts | 33 +++++++++++- .../components/templates/recommendation.ts | 33 +++++++++++- .../templates/recommendationBundle.ts | 32 ++++++++++- .../templates/recommendationBundleEasyAdd.ts | 33 +++++++++++- .../templates/recommendationBundleVertical.ts | 33 +++++++++++- .../templates/recommendationGrid.ts | 33 +++++++++++- .../pike/components/templates/search.ts | 32 ++++++++++- .../pike/components/templates/searchBoca.ts | 32 ++++++++++- .../components/templates/searchHorizontal.ts | 32 ++++++++++- .../components/templates/searchSnapnco.ts | 32 ++++++++++- .../pike/components/templates/searchSnappy.ts | 32 ++++++++++- .../components/src/themes/pike/custom.ts | 21 +++++++- 41 files changed, 527 insertions(+), 82 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 5396160a4..7f821a29a 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -68,11 +68,11 @@ let config: SnapTemplatesConfig = { component: 'SearchSnappy', }, ], - settings: { - infinite: { - backfill: 5, - }, - }, + // settings: { + // infinite: { + // backfill: 5, + // }, + // }, }, autocomplete: { targets: [ diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts index b75a87cda..fc013941c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts @@ -14,9 +14,6 @@ const breadcrumbsStyleScript = (props: BreadcrumbsProps) => { '&, .ss__breadcrumbs__crumbs__crumb': { listStyle: 'none', }, - '&, a': { - color: variables?.colors?.text, - }, '.ss__breadcrumbs__crumbs__crumb': { padding: `0 ${custom.spacing.x1}px`, '&:last-child': { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index a1bc6cc13..303c41453 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -52,7 +52,6 @@ const buttonStyleScript = (props: ButtonProps) => { [`&, &:hover, &:not(.ss__button--disabled):hover, ${disabledSelectors}`]: { border: `1px solid ${custom.colors.gray02}`, backgroundColor: custom.colors.white, - fontSize: '14px', color: variables?.colors?.text, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts index dc9b3203f..cf8ada961 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts @@ -4,7 +4,7 @@ import { ThemeComponent } from '../../../../providers'; // CSS in JS style script for the Overlay component const overlayStyleScript = (props: OverlayProps) => { - const backgroundColor = props?.color || 'rgba(0, 0, 0, 0.60)'; + const backgroundColor = props?.color || 'rgba(0, 0, 0, 0.80)'; return css({ '&, &.ss__overlay--active': { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts index 67310d786..a7ab2ab41 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts @@ -8,9 +8,8 @@ const priceStyleScript = (props: PriceProps) => { const variables = props?.theme?.variables; return css({ - color: variables?.colors?.text, - 'span, &.ss__price, &.ss__price--strike': { - color: 'inherit', + '&, span, &.ss__price, &.ss__price--strike': { + color: variables?.colors?.text, }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts index b6888e8ae..a5c13abf3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/searchHeader.ts @@ -14,12 +14,13 @@ const searchHeaderStyleScript = (props: SearchHeaderProps) => { }, '.ss__search-header__title': { margin: 0, + fontSize: custom.utils.convertPxToEm(22), fontWeight: custom.fonts.weight02, color: variables?.colors?.secondary, }, '.ss__search-header__subtitle': { margin: `${custom.spacing.x2}px 0 0 0`, - fontSize: '16px', + fontSize: custom.utils.convertPxToEm(16), fontWeight: 400, color: variables?.colors?.text, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts index 1db4dbf1b..367766c73 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/calloutBadge.ts @@ -11,7 +11,7 @@ const calloutBadgeStyleScript = () => { padding: `${custom.spacing.x1}px ${custom.spacing.x2}px`, lineHeight: 1, span: { - fontSize: '12px', + fontSize: custom.utils.convertPxToEm(12), }, }, '.ss__badge-text': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts index deeaa841a..c2e14533a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts @@ -8,7 +8,52 @@ const carouselStyleScript = (props: CarouselProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '&, .ss__carousel__navigation': { + '.ss__carousel__prev-wrapper .swiper-button-disabled, .ss__carousel__next-wrapper .swiper-button-disabled, .ss__carousel__prev-wrapper--hidden div, .ss__carousel__next-wrapper--hidden div': + { + cursor: 'not-allowed', + opacity: 0.5, + }, + }, + '.swiper-container': { + margin: '0 auto', + '&:has(.swiper-pagination)': { + paddingBottom: `${custom.spacing.x5}px`, + }, + '& > .swiper-wrapper': { + '& > .swiper-slide': { + '& > *, .ss__result': { + padding: 0, + margin: 0, + width: 'auto', + height: '100%', + }, + }, + }, + '& > .swiper-pagination': { + position: 'absolute', + bottom: 0, + left: 0, + right: 0, + margin: 'auto', + '.swiper-pagination-bullet': { + margin: `0 ${custom.spacing.x1 / 2}px`, + width: '12px', + height: '12px', + minWidth: '1px', + flex: '0 1 auto', + backgroundColor: custom.colors.gray01, + border: `1px solid ${custom.colors.gray02}`, + opacity: 1, + }, + '.swiper-pagination-bullet-active': { + backgroundColor: variables?.colors?.primary, + borderColor: variables?.colors?.primary, + }, + }, + }, + }); }; // Carousel component props @@ -17,11 +62,5 @@ export const carousel: ThemeComponent<'carousel', CarouselProps> = { carousel: { themeStyleScript: carouselStyleScript, }, - 'carousel icon.prev': { - icon: custom.icons.arrowLeft, - }, - 'carousel icon.next': { - icon: custom.icons.arrowRight, - }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index 8db3f0c7f..11aef4690 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const checkboxStyleScript = (props: CheckboxProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); // shared checkbox styles const sharedStyles = css({ @@ -34,7 +35,7 @@ const checkboxStyleScript = (props: CheckboxProps) => { }, '&.ss__checkbox--active': { backgroundColor: custom.colors.white, - borderColor: custom.colors.gray03, + borderColor: darkGray, '.ss__icon': { fill: variables?.colors?.primary, stroke: variables?.colors?.primary, @@ -65,7 +66,7 @@ export const checkbox: ThemeComponent<'checkbox', CheckboxProps> = { checkbox: { themeStyleScript: checkboxStyleScript, icon: custom.icons.check, - size: `${custom.sizes.icon14}px`, + size: `${custom.sizes.icon16}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 05c7c71ea..b9bc2c867 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -22,7 +22,6 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { position: 'relative', height: 0, paddingBottom: '100%', - color: variables?.colors?.text, border: 0, '&, &:before, .ss__facet-grid-options__option__value': { display: 'block', @@ -51,7 +50,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { overflow: 'hidden', textAlign: 'center', '&, &.ss__facet-grid-options__option__value--smaller': { - fontSize: '0.75rem', + fontSize: custom.utils.convertPxToEm(12), }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index bc4ac676a..ef0e8c6cd 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -7,13 +7,13 @@ import { custom } from '../../custom'; const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); return css({ '.ss__facet-hierarchy-options__option': { display: 'block', margin: `0 0 ${custom.spacing.x1}px 0`, padding: 0, - color: variables?.colors?.text, '&:last-child': { marginBottom: 0, }, @@ -24,8 +24,8 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => top: '-1px', margin: 0, padding: `0 ${custom.spacing.x1}px`, - opacity: 0.805, - fontSize: '0.6rem', + fontSize: custom.utils.convertPxToEm(10), + color: lightGray, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index d9116abfb..63002fe76 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); return css({ '.ss__facet-list-options__option': { @@ -14,7 +15,6 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { position: 'relative', margin: `0 0 ${custom.spacing.x1}px 0`, padding: props?.hideCheckbox ? `` : `0 0 0 ${16 + custom.spacing.x2}px`, - color: variables?.colors?.text, '&:last-child': { marginBottom: 0, }, @@ -30,8 +30,8 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { top: '-1px', margin: 0, padding: `0 ${custom.spacing.x1}px`, - opacity: 0.805, - fontSize: '0.6rem', + fontSize: custom.utils.convertPxToEm(10), + color: lightGray, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 191b2cd58..498f80c9c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); const hasCheckbox = !props?.hideCheckbox ? true : false; // set details for radius @@ -29,7 +30,6 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { const sharedStyles = css({ '.ss__facet-palette-options__option': { display: 'block', - color: variables?.colors?.text, '&, &.ss__facet-palette-options__option--filtered': { '.ss__facet-palette-options__option__wrapper': { border: 0, @@ -81,8 +81,8 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { position: 'relative', top: props?.layout == 'list' ? '-1px' : '', padding: props?.layout == 'list' ? `0 ${custom.spacing.x1}px` : ``, - opacity: 0.805, - fontSize: '0.6rem', + fontSize: custom.utils.convertPxToEm(10), + color: lightGray, }, }, '.ss__facet-palette-options__option.ss__facet-palette-options__option--filtered': { @@ -141,7 +141,7 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { whiteSpace: 'nowrap', }, '.ss__facet-palette-options__option__value': { - fontSize: '0.75rem', + fontSize: custom.utils.convertPxToEm(12), overflow: 'hidden', margin: `${custom.spacing.x1}px 0 0 0`, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index 3cbaea157..2e40da781 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -19,6 +19,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const fontColor = props?.valueTextColor || variables?.colors?.text; + const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); const valuesTop = slider.valuesPosition == 'top' ? true : false; const valuesSides = slider.valuesAlign == 'sides' ? true : false; const hasTicks = props?.showTicks ? true : false; @@ -29,7 +30,6 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { // values font styles const valuesStyles = css({ - fontSize: `${slider.values}px`, lineHeight: `${slider.values}px`, color: fontColor, }); @@ -63,7 +63,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { }, '&:before': { top: `${slider.ticks / 2}px`, - backgroundColor: custom.colors.gray03, + backgroundColor: darkGray, }, '.ss__facet-slider__tick__label': { top: `${slider.ticks}px`, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts index 877209cc4..0ca52e78a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts @@ -17,11 +17,11 @@ const filterStyleScript = (props: FilterProps) => { lineHeight: 1.5, padding: `${custom.spacing.x1}px ${custom.spacing.x2}px`, paddingLeft: `${custom.spacing.x2 + custom.spacing.x1 + 10}px`, - border: `1px solid ${custom.colors.gray02}`, fontWeight: 'normal', - color: variables?.colors?.text, + color: 'inherit', '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { backgroundColor: custom.colors.gray01, + border: `1px solid ${custom.colors.gray02}`, }, '.ss__button__content': { '.ss__icon': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts index 69bfb3761..2b0531420 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -17,7 +17,7 @@ const listStyleScript = (props: ListProps) => { }, '.ss__list__title': { display: 'block', - fontSize: '14px', + fontSize: custom.utils.convertPxToEm(14), fontWeight: custom.fonts.weight02, lineHeight: 1, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts index 162607f93..4f5fce065 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/overlayBadge.ts @@ -15,7 +15,7 @@ const overlayBadgeStyleScript = () => { padding: `${custom.spacing.x1}px ${custom.spacing.x2}px`, lineHeight: 1, span: { - fontSize: '12px', + fontSize: custom.utils.convertPxToEm(12), }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts index 27edd3616..a0da884b2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts @@ -18,8 +18,7 @@ const paginationStyleScript = (props: PaginationProps) => { lineHeight: 1, '.ss__pagination__page, span': { padding: `0 ${custom.spacing.x2}px`, - fontSize: '16px', - color: variables?.colors?.text, + fontSize: custom.utils.convertPxToEm(16), }, '.ss__pagination__page': { minWidth: '1px', @@ -40,7 +39,7 @@ const paginationStyleScript = (props: PaginationProps) => { nav: { '.ss__pagination__page, span': { padding: `0 ${custom.spacing.x1}px`, - fontSize: '14px', + fontSize: custom.utils.convertPxToEm(14), }, '.ss__pagination__page--previous, .ss__pagination__page--next': { lineHeight: `${custom.sizes.icon12}px`, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index 5cac39bb6..86cc18a2c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const radioStyleScript = (props: RadioProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); // shared radio styles const disabledStyles = css({ @@ -31,7 +32,7 @@ const radioStyleScript = (props: RadioProps) => { }, '&.ss__radio--active': { backgroundColor: custom.colors.white, - borderColor: custom.colors.gray03, + borderColor: darkGray, '.ss__icon': { display: 'block', fill: variables?.colors?.primary, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts index 2319f667d..23a8375f7 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -15,7 +15,7 @@ const radioListStyleScript = (props: RadioListProps) => { }, '.ss__radio-list__title': { display: 'block', - fontSize: '14px', + fontSize: custom.utils.convertPxToEm(14), fontWeight: custom.fonts.weight02, lineHeight: 1, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts index 3927b8362..4e934f1db 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/rating.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const ratingStyleScript = (props: RatingProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); return css({ flexWrap: 'wrap', @@ -21,8 +22,8 @@ const ratingStyleScript = (props: RatingProps) => { }, '.ss__rating__stars--empty': { '.ss__rating__stars__star .ss__icon': { - fill: custom.colors.gray03, - stroke: custom.colors.gray03, + fill: darkGray, + stroke: darkGray, }, }, '.ss__rating__stars--full': { @@ -33,6 +34,7 @@ const ratingStyleScript = (props: RatingProps) => { }, }, '.ss__rating__count, .ss__rating__text': { + fontSize: custom.utils.convertPxToEm(12), color: variables?.colors?.text, }, }); @@ -46,5 +48,8 @@ export const rating: ThemeComponent<'rating', RatingProps> = { emptyIcon: 'star', fullIcon: 'star', }, + 'rating icon': { + size: `${custom.sizes.icon14}px`, + }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts index 5969c5e64..f43b50da4 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts @@ -7,29 +7,51 @@ import { custom } from '../../custom'; const resultStyleScript = (props: ResultProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); return css({ '&.ss__result--sale': { '.ss__result__details': { '.ss__result__details__pricing': { '.ss__result__price:not(.ss__price--strike)': { - color: variables?.colors?.primary, + '&, span': { + color: variables?.colors?.primary, + }, }, }, }, }, + '&.ss__result--grid': { + display: 'block', + height: 'auto', + width: 'auto', + }, + '.ss__result__image-wrapper': { + margin: `0 0 ${custom.spacing.x2}px 0`, + }, '.ss__result__details': { + padding: 0, + display: 'flex', + flexFlow: 'column nowrap', + gap: `${custom.spacing.x2}px`, + '& > *, .ss__result__details__title': { + margin: 0, + }, + '.ss__result__details__title': { + order: -1, + }, '.ss__result__details__pricing': { - fontSize: '16px', '.ss__result__price': { - fontSize: 'inherit', + fontSize: custom.utils.convertPxToEm(16), '&:not(.ss__price--strike)': { fontWeight: custom.fonts.weight01, }, }, '.ss__price--strike': { - fontSize: '0.875rem', - opacity: 0.805, + fontSize: custom.utils.convertPxToEm(14), + '&, span': { + color: lightGray, + }, }, }, }, @@ -41,6 +63,8 @@ export const result: ThemeComponent<'result', ResultProps> = { default: { result: { themeStyleScript: resultStyleScript, + hideRating: false, + hideAddToCartButton: false, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index 8db016a31..1ac1503b5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -7,30 +7,35 @@ import { custom } from '../../custom'; const searchInputStyleScript = (props: SearchInputProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const desktopBp = variables?.breakpoints?.mobile || 767; return css({ '&.ss__search-input': { margin: `0 0 ${custom.spacing.x2}px`, border: `1px solid ${custom.colors.gray02}`, backgroundColor: `${custom.colors.gray01}`, + '.ss__icon, .ss__search-input__input': { + minWidth: '1px', + padding: 0, + }, '.ss__icon': { - padding: `0 0 0 ${custom.spacing.x2}px`, + flex: '0 1 auto', + margin: `0 0 0 ${custom.spacing.x2}px`, }, '.ss__search-input__input': { - padding: `0 ${custom.spacing.x2}px`, + flex: '1 1 0%', + margin: `0 ${custom.spacing.x2}px`, backgroundColor: `inherit`, - color: variables?.colors?.text, minHeight: '1px', height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, - '&::-webkit-input-placeholder': { - opacity: 0.5, - }, - '&::-ms-input-placeholder': { - opacity: 0.5, - }, - '&::placeholder': { - opacity: 0.5, + fontSize: custom.utils.convertPxToEm(16), + }, + }, + [`@media (min-width: ${desktopBp + 1}px)`]: { + '&.ss__search-input': { + '.ss__search-input__input': { + fontSize: custom.utils.convertPxToEm(14), }, }, }, @@ -43,5 +48,8 @@ export const searchInput: ThemeComponent<'searchInput', SearchInputProps> = { searchInput: { themeStyleScript: searchInputStyleScript, }, + 'searchInput icon': { + size: `${custom.sizes.icon14}px`, + }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index dbe611e65..b02b4cfd9 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const selectStyleScript = (props: SelectProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const desktopBp = variables?.breakpoints?.mobile || 767; // shared styles for select menus const sharedStyles = css({ @@ -86,6 +87,9 @@ const selectStyleScript = (props: SelectProps) => { padding: `0 ${custom.spacing.x2}px`, height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, + '.ss__select__label, .ss__select__select': { + fontSize: custom.utils.convertPxToEm(16), + }, '.ss__select__label': { fontWeight: custom.fonts.weight01, }, @@ -103,6 +107,19 @@ const selectStyleScript = (props: SelectProps) => { display: 'none', }, }, + '.ss__select__dropdown__button__icon': { + width: `${custom.sizes.icon14}px`, + height: `${custom.sizes.icon14}px`, + }, + [`@media (min-width: ${desktopBp + 1}px)`]: { + '.ss__select__label, .ss__select__select': { + fontSize: custom.utils.convertPxToEm(14), + }, + '.ss__select__dropdown__button__icon': { + width: `${custom.sizes.icon12}px`, + height: `${custom.sizes.icon12}px`, + }, + }, }, ]); diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts index 4e2cf3a48..35781133c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts @@ -15,6 +15,7 @@ export const slideout: ThemeComponent<'slideout', SlideoutProps> = { default: { slideout: { themeStyleScript: slideoutStyleScript, + overlayColor: '', }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index 350bd349b..4e6b9b0f3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -26,7 +26,7 @@ const facetStyleScript = (props: FacetProps) => { }, '.ss__facet__header': { gap: `${custom.spacing.x2}px`, - fontSize: '16px', + fontSize: custom.utils.convertPxToEm(16), fontWeight: custom.fonts.weight02, '.ss__icon': { transition: 'transform ease .5s', diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts index e2294ddef..dd5867fcd 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const filterSummaryStyleScript = (props: FilterSummaryProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); return css({ display: 'flex', @@ -44,7 +45,7 @@ const filterSummaryStyleScript = (props: FilterSummaryProps) => { width: `${custom.sizes.icon14}px`, height: `${custom.sizes.icon14}px`, backgroundColor: custom.colors.white, - border: `1px solid ${custom.colors.gray03}`, + border: `1px solid ${darkGray}`, }, '.ss__icon': { margin: '4px', @@ -60,7 +61,7 @@ const filterSummaryStyleScript = (props: FilterSummaryProps) => { }, '.ss__filter-summary__title': { padding: `0 ${custom.spacing.x1}px 0 0`, - fontSize: '14px', + fontSize: custom.utils.convertPxToEm(14), fontWeight: custom.fonts.weight02, color: variables?.colors?.text, '&:after': { diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index 6d3e089d6..5b3b9e060 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -22,7 +22,7 @@ const mobileSidebarStyleScript = (props: MobileSidebarProps) => { margin: ` 0 0 ${custom.spacing.x4}px 0`, padding: ` 0 0 ${custom.spacing.x2}px 0`, borderBottom: `2px solid ${variables?.colors?.primary}`, - fontSize: '16px', + fontSize: custom.utils.convertPxToEm(16), fontWeight: custom.fonts.weight02, color: variables?.colors?.secondary, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts index 8ec278271..a49dca53e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts @@ -22,7 +22,7 @@ const sidebarStyleScript = (props: SidebarProps) => { margin: ` 0 0 ${custom.spacing.x4}px 0`, padding: ` 0 0 ${custom.spacing.x2}px 0`, borderBottom: `2px solid ${variables?.colors?.primary}`, - fontSize: '16px', + fontSize: custom.utils.convertPxToEm(16), fontWeight: custom.fonts.weight02, color: variables?.colors?.secondary, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts index 2c1e10086..b13f55fdf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts @@ -2,13 +2,44 @@ import { css } from '@emotion/react'; import type { AutocompleteTemplateProps } from '../../../../components/Templates/AutocompleteTemplate'; import { autocompleteThemeComponentProps } from '../../../themeComponents/autocompleteTemplate'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // AutocompleteTemplate component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts index c7c23594a..ed3658a1d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts @@ -2,13 +2,44 @@ import { css } from '@emotion/react'; import type { RecommendationProps } from '../../../../components/Templates/Recommendation'; import { recommendationThemeComponentProps } from '../../../themeComponents/recommendation'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Recommendation component const recommendationStyleScript = (props: RecommendationProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // Recommendation component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts index f2afc2857..76eed549c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts @@ -8,8 +8,38 @@ import { custom } from '../../custom'; const recommendationBundleStyleScript = (props: RecommendationBundleProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // RecommendationBundle component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts index 81b46f881..65fdb5a16 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts @@ -2,13 +2,44 @@ import { css } from '@emotion/react'; import type { RecommendationBundleEasyAddProps } from '../../../../components/Templates/RecommendationBundleEasyAdd'; import { recommendationBundleEasyAddThemeComponentProps } from '../../../themeComponents/recommendationBundleEasyAdd'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the RecommendationBundle component const recommendationBundleEasyAddStyleScript = (props: RecommendationBundleEasyAddProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // RecommendationBundleEasyAdd component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts index 3e4d6763f..c851cb225 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts @@ -2,13 +2,44 @@ import { css } from '@emotion/react'; import type { RecommendationBundleVerticalProps } from '../../../../components/Templates/RecommendationBundleVertical'; import { recommendationBundleVerticalThemeComponentProps } from '../../../themeComponents/recommendationBundleVertical'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the RecommendationBundle component const recommendationBundleVerticalStyleScript = (props: RecommendationBundleVerticalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // RecommendationBundleVertical component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts index c2a0c9fde..0c6ee9263 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts @@ -2,13 +2,44 @@ import { css } from '@emotion/react'; import type { RecommendationGridProps } from '../../../../components/Templates/RecommendationGrid'; import { recommendationGridThemeComponentProps } from '../../../themeComponents/recommendationGrid'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the RecommendationBundle component const recommendationGridStyleScript = (props: RecommendationGridProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // RecommendationGrid component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts index ae8f8e229..4f575d082 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts @@ -8,8 +8,38 @@ import { custom } from '../../custom'; const searchStyleScript = (props: SearchProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // Search component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts index 04f511db4..37d50a0f5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts @@ -8,8 +8,38 @@ import { custom } from '../../custom'; const searchBocaStyleScript = (props: SearchBocaProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; export const searchBoca: ThemeComponent<'searchBoca', SearchBocaProps> = { diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts index b82402eb4..34308712b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts @@ -8,8 +8,38 @@ import { custom } from '../../custom'; const searchHorizontalStyleScript = (props: SearchHorizontalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; export const searchHorizontal: ThemeComponent<'searchHorizontal', SearchHorizontalProps> = { diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts index 182de3d4d..f504d741f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts @@ -8,8 +8,38 @@ import { custom } from '../../custom'; const searchSnapncoStyleScript = (props: SearchSnapncoProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // Search component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts index 8378080ea..e957ecd2f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts @@ -8,8 +8,38 @@ import { custom } from '../../custom'; const searchSnappyStyleScript = (props: SearchSnappyProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + return css({ + fontSize: `${custom.sizes.font}px`, + '*, *:after, *:before': { + boxSizing: 'border-box', + }, + 'p, ul li, a, input, input[type], textarea, button, div': { + fontSize: custom.utils.convertPxToEm(14), + }, + 'p, ul li, a, input, input[type], textarea, select': { + color: variables?.colors?.text, + }, + 'input, input[type], textarea': { + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, + }, + a: { + textDecoration: 'none', + '&:hover': { + color: darkGray, + }, + }, + }); }; // Search component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 5270a5eef..0a1cd2eed 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -17,13 +17,17 @@ export const custom: { spacing: { [key: string]: number; }; + utils: { + convertPxToEm: (value: number) => string; + lightenColor: (color: string | undefined, amount: number) => string; + darkenColor: (color: string | undefined, amount: number) => string; + }; } = { colors: { white: '#ffffff', black: '#000000', gray01: '#f8f8f8', // lighter gray: bg color under terms, dropdown, checkboxes gray02: '#ebebeb', // light gray: borders for autocomplete, dropdown, checkboxes - gray03: `${new Color('#ebebeb').darken(0.055).hex().toLowerCase()}`, // dark gray: active border for checkboxes, palette, etc. brown: '#845329', // for color palette purple: '#7c368e', // for color palette rainbow: @@ -49,6 +53,7 @@ export const custom: { sort: 'sort', }, sizes: { + font: 16, // base font size height: 33, // refers to height for button and dropdown sizes icon08: 8, icon10: 10, @@ -64,4 +69,18 @@ export const custom: { x5: 25, x6: 30, }, + utils: { + convertPxToEm: (value: number) => { + // translates px to rem + return `${value / custom.sizes.font}rem`; + }, + lightenColor: (color: string | undefined, amount: number) => { + const lightColor = new Color(color || '#515151').lighten(amount).hex().toLowerCase(); + return lightColor; + }, + darkenColor: (color: string | undefined, amount: number) => { + const darkColor = new Color(color || '#515151').darken(amount).hex().toLowerCase(); + return darkColor; + }, + }, }; From 3ee032d0aca168751153178527b9c228c46dc061 Mon Sep 17 00:00:00 2001 From: adria Date: Sat, 2 Aug 2025 08:49:58 -0600 Subject: [PATCH 025/118] finish result, revert some changes --- .../snap-preact-demo/templates/src/index.ts | 10 +-- .../pike/components/atoms/breadcrumbs.ts | 3 + .../themes/pike/components/atoms/button.ts | 1 + .../pike/components/atoms/paginationInfo.ts | 2 +- .../src/themes/pike/components/atoms/price.ts | 4 ++ .../pike/components/molecules/carousel.ts | 66 +++++++++++++++++-- .../pike/components/molecules/checkbox.ts | 4 +- .../components/molecules/facetGridOptions.ts | 1 + .../molecules/facetHierarchyOptions.ts | 1 + .../components/molecules/facetListOptions.ts | 1 + .../molecules/facetPaletteOptions.ts | 1 + .../pike/components/molecules/facetSlider.ts | 1 + .../pike/components/molecules/filter.ts | 2 +- .../pike/components/molecules/pagination.ts | 1 + .../themes/pike/components/molecules/radio.ts | 1 - .../pike/components/molecules/result.ts | 63 +++++++++++++++--- .../pike/components/molecules/searchInput.ts | 11 ++++ .../pike/components/molecules/select.ts | 4 +- .../components/organisms/filterSummary.ts | 3 +- .../templates/autocompleteTemplate.ts | 33 +--------- .../components/templates/recommendation.ts | 33 +--------- .../templates/recommendationBundle.ts | 32 +-------- .../templates/recommendationBundleEasyAdd.ts | 33 +--------- .../templates/recommendationBundleVertical.ts | 33 +--------- .../templates/recommendationGrid.ts | 33 +--------- .../pike/components/templates/search.ts | 32 +-------- .../pike/components/templates/searchBoca.ts | 32 +-------- .../components/templates/searchHorizontal.ts | 32 +-------- .../components/templates/searchSnapnco.ts | 32 +-------- .../pike/components/templates/searchSnappy.ts | 32 +-------- .../components/src/themes/pike/custom.ts | 2 + 31 files changed, 165 insertions(+), 374 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 7f821a29a..5396160a4 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -68,11 +68,11 @@ let config: SnapTemplatesConfig = { component: 'SearchSnappy', }, ], - // settings: { - // infinite: { - // backfill: 5, - // }, - // }, + settings: { + infinite: { + backfill: 5, + }, + }, }, autocomplete: { targets: [ diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts index fc013941c..b75a87cda 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts @@ -14,6 +14,9 @@ const breadcrumbsStyleScript = (props: BreadcrumbsProps) => { '&, .ss__breadcrumbs__crumbs__crumb': { listStyle: 'none', }, + '&, a': { + color: variables?.colors?.text, + }, '.ss__breadcrumbs__crumbs__crumb': { padding: `0 ${custom.spacing.x1}px`, '&:last-child': { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index 303c41453..c54099aef 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -52,6 +52,7 @@ const buttonStyleScript = (props: ButtonProps) => { [`&, &:hover, &:not(.ss__button--disabled):hover, ${disabledSelectors}`]: { border: `1px solid ${custom.colors.gray02}`, backgroundColor: custom.colors.white, + fontSize: custom.utils.convertPxToEm(14), color: variables?.colors?.text, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts index c94bcd29c..c55c2a8f2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/paginationInfo.ts @@ -10,7 +10,7 @@ const paginationInfoStyleScript = ({ theme }: PaginationInfoProps) => { return css({ fontWeight: custom.fonts.weight02, - color: variables?.colors?.text, + color: variables?.colors?.secondary, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts index a7ab2ab41..fb263f1a1 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/price.ts @@ -1,6 +1,7 @@ import { css } from '@emotion/react'; import type { PriceProps } from '../../../../components/Atoms/Price'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Price component const priceStyleScript = (props: PriceProps) => { @@ -11,6 +12,9 @@ const priceStyleScript = (props: PriceProps) => { '&, span, &.ss__price, &.ss__price--strike': { color: variables?.colors?.text, }, + '& ~ .ss__result__price': { + paddingLeft: `${custom.spacing.x1 / 2}px`, + }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts index c2e14533a..19102c068 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts @@ -8,13 +8,49 @@ const carouselStyleScript = (props: CarouselProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + // shared button styles + const disabledStyles = css({ + opacity: 0.65, + '&, & *': { + cursor: 'not-allowed', + }, + }); + return css({ - '&, .ss__carousel__navigation': { - '.ss__carousel__prev-wrapper .swiper-button-disabled, .ss__carousel__next-wrapper .swiper-button-disabled, .ss__carousel__prev-wrapper--hidden div, .ss__carousel__next-wrapper--hidden div': - { - cursor: 'not-allowed', - opacity: 0.5, - }, + position: 'relative', + '.ss__carousel__prev-wrapper--hidden > div, .ss__carousel__next-wrapper--hidden > div': { + ...disabledStyles, + }, + '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { + width: '24px', + height: '24px', + display: 'block', + position: 'absolute', + top: 0, + bottom: '33.33%', + zIndex: 2, + margin: 'auto', + '& > div': { + display: 'flex', + flexFlow: 'column nowrap', + alignItems: 'center', + justifyContent: 'center', + padding: 0, + width: '100%', + height: '100%', + lineHeight: 1, + backgroundColor: variables?.colors?.primary, + color: custom.colors.white, + }, + '.swiper-button-disabled': { + ...disabledStyles, + }, + }, + '.ss__carousel__prev-wrapper': { + left: 0, + }, + '.ss__carousel__next-wrapper': { + right: 0, }, '.swiper-container': { margin: '0 auto', @@ -53,6 +89,16 @@ const carouselStyleScript = (props: CarouselProps) => { }, }, }, + '.swiper-grid-column': { + '& > .swiper-wrapper': { + flexFlow: 'row wrap', + '& > .swiper-slide': { + height: 'auto !important', + marginTop: '0 !important', + marginBottom: `${custom.spacing.x4}px`, + }, + }, + }, }); }; @@ -62,5 +108,13 @@ export const carousel: ThemeComponent<'carousel', CarouselProps> = { carousel: { themeStyleScript: carouselStyleScript, }, + 'carousel icon.prev': { + icon: custom.icons.arrowLeft, + size: '8px', + }, + 'carousel icon.next': { + icon: custom.icons.arrowRight, + size: '8px', + }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index 11aef4690..ddb02287b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -29,12 +29,14 @@ const checkboxStyleScript = (props: CheckboxProps) => { { backgroundColor: custom.colors.gray01, border: `1px solid ${custom.colors.gray02}`, + '&, *': { + boxSizing: 'border-box', + }, '.ss__icon': { width: '8px', height: '8px', }, '&.ss__checkbox--active': { - backgroundColor: custom.colors.white, borderColor: darkGray, '.ss__icon': { fill: variables?.colors?.primary, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index b9bc2c867..30489cec0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -23,6 +23,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { height: 0, paddingBottom: '100%', border: 0, + color: variables?.colors?.text, '&, &:before, .ss__facet-grid-options__option__value': { display: 'block', }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index ef0e8c6cd..68cd9e1b3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -14,6 +14,7 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => display: 'block', margin: `0 0 ${custom.spacing.x1}px 0`, padding: 0, + color: variables?.colors?.text, '&:last-child': { marginBottom: 0, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 63002fe76..450509100 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -15,6 +15,7 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { position: 'relative', margin: `0 0 ${custom.spacing.x1}px 0`, padding: props?.hideCheckbox ? `` : `0 0 0 ${16 + custom.spacing.x2}px`, + color: variables?.colors?.text, '&:last-child': { marginBottom: 0, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 498f80c9c..91c40d87e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -30,6 +30,7 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { const sharedStyles = css({ '.ss__facet-palette-options__option': { display: 'block', + color: variables?.colors?.text, '&, &.ss__facet-palette-options__option--filtered': { '.ss__facet-palette-options__option__wrapper': { border: 0, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts index 2e40da781..2788c98fa 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetSlider.ts @@ -30,6 +30,7 @@ const facetSliderStyleScript = (props: FacetSliderProps) => { // values font styles const valuesStyles = css({ + fontSize: custom.utils.convertPxToEm(slider.values), lineHeight: `${slider.values}px`, color: fontColor, }); diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts index 0ca52e78a..eb5e96f21 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/filter.ts @@ -18,7 +18,7 @@ const filterStyleScript = (props: FilterProps) => { padding: `${custom.spacing.x1}px ${custom.spacing.x2}px`, paddingLeft: `${custom.spacing.x2 + custom.spacing.x1 + 10}px`, fontWeight: 'normal', - color: 'inherit', + color: variables?.colors?.text, '&, &:hover, &:not(.ss__button--disabled):hover, &.ss__button--disabled': { backgroundColor: custom.colors.gray01, border: `1px solid ${custom.colors.gray02}`, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts index a0da884b2..85d0443a3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts @@ -19,6 +19,7 @@ const paginationStyleScript = (props: PaginationProps) => { '.ss__pagination__page, span': { padding: `0 ${custom.spacing.x2}px`, fontSize: custom.utils.convertPxToEm(16), + color: variables?.colors?.text, }, '.ss__pagination__page': { minWidth: '1px', diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index 86cc18a2c..94518739b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -31,7 +31,6 @@ const radioStyleScript = (props: RadioProps) => { display: 'none', }, '&.ss__radio--active': { - backgroundColor: custom.colors.white, borderColor: darkGray, '.ss__icon': { display: 'block', diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts index f43b50da4..baa9755fe 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts @@ -10,6 +10,11 @@ const resultStyleScript = (props: ResultProps) => { const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); return css({ + '&.ss__result': { + display: 'block', + height: 'auto', + width: 'auto', + }, '&.ss__result--sale': { '.ss__result__details': { '.ss__result__details__pricing': { @@ -21,10 +26,17 @@ const resultStyleScript = (props: ResultProps) => { }, }, }, - '&.ss__result--grid': { - display: 'block', - height: 'auto', - width: 'auto', + '&.ss__result--list': { + '.ss__result__details': { + textAlign: 'center', + margin: 0, + '.ss__result__details__title': { + a: { + fontSize: custom.utils.convertPxToEm(18), + fontWeight: custom.fonts.weight02, + }, + }, + }, }, '.ss__result__image-wrapper': { margin: `0 0 ${custom.spacing.x2}px 0`, @@ -32,13 +44,20 @@ const resultStyleScript = (props: ResultProps) => { '.ss__result__details': { padding: 0, display: 'flex', - flexFlow: 'column nowrap', + flexFlow: 'row wrap', gap: `${custom.spacing.x2}px`, - '& > *, .ss__result__details__title': { + '& > *, .ss__result__details__title, .ss__result__details__title, .ss__result__details__pricing': { margin: 0, }, + '& > *': { + minWidth: '1px', + flex: '1 1 100%', + }, '.ss__result__details__title': { - order: -1, + order: -2, + a: { + color: variables?.colors?.text, + }, }, '.ss__result__details__pricing': { '.ss__result__price': { @@ -55,6 +74,34 @@ const resultStyleScript = (props: ResultProps) => { }, }, }, + '@media (min-width: 541px)': { + '&.ss__result--list': { + display: 'flex', + flexFlow: 'row wrap', + alignItems: 'center', + '.ss__result__image-wrapper, .ss__result__details': { + minWidth: '1px', + }, + '.ss__result__image-wrapper': { + flex: '0 0 33.33%', + margin: `0 ${custom.spacing.x4}px 0 0`, + }, + '.ss__result__details': { + flex: '1 1 0%', + textAlign: 'left', + '.ss__callout-badge, .ss__result__rating-wrapper': { + justifyContent: 'flex-start', + }, + '.ss__result__details__title': { + flex: '1 1 0%', + }, + '.ss__result__details__pricing': { + flex: '0 1 auto', + order: -1, + }, + }, + }, + }, }); }; @@ -63,8 +110,6 @@ export const result: ThemeComponent<'result', ResultProps> = { default: { result: { themeStyleScript: resultStyleScript, - hideRating: false, - hideAddToCartButton: false, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index 1ac1503b5..a7cae9edf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const searchInputStyleScript = (props: SearchInputProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); const desktopBp = variables?.breakpoints?.mobile || 767; return css({ @@ -30,6 +31,16 @@ const searchInputStyleScript = (props: SearchInputProps) => { height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, fontSize: custom.utils.convertPxToEm(16), + color: variables?.colors?.text, + '&::-webkit-input-placeholder': { + color: lightGray, + }, + '&::-ms-input-placeholder': { + color: lightGray, + }, + '&::placeholder': { + color: lightGray, + }, }, }, [`@media (min-width: ${desktopBp + 1}px)`]: { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index b02b4cfd9..2297f98f2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -88,7 +88,7 @@ const selectStyleScript = (props: SelectProps) => { height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, '.ss__select__label, .ss__select__select': { - fontSize: custom.utils.convertPxToEm(16), + fontSize: '16px', }, '.ss__select__label': { fontWeight: custom.fonts.weight01, @@ -113,7 +113,7 @@ const selectStyleScript = (props: SelectProps) => { }, [`@media (min-width: ${desktopBp + 1}px)`]: { '.ss__select__label, .ss__select__select': { - fontSize: custom.utils.convertPxToEm(14), + fontSize: '14px', }, '.ss__select__dropdown__button__icon': { width: `${custom.sizes.icon12}px`, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts index dd5867fcd..c715375d5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts @@ -41,10 +41,9 @@ const filterSummaryStyleScript = (props: FilterSummaryProps) => { content: '""', display: 'block', position: 'absolute', - width: `${custom.sizes.icon14}px`, height: `${custom.sizes.icon14}px`, - backgroundColor: custom.colors.white, + backgroundColor: custom.colors.gray01, border: `1px solid ${darkGray}`, }, '.ss__icon': { diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts index b13f55fdf..2c1e10086 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts @@ -2,44 +2,13 @@ import { css } from '@emotion/react'; import type { AutocompleteTemplateProps } from '../../../../components/Templates/AutocompleteTemplate'; import { autocompleteThemeComponentProps } from '../../../themeComponents/autocompleteTemplate'; import { ThemeComponent } from '../../../../providers'; -import { custom } from '../../custom'; // CSS in JS style script for the Search component const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // AutocompleteTemplate component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts index ed3658a1d..c7c23594a 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts @@ -2,44 +2,13 @@ import { css } from '@emotion/react'; import type { RecommendationProps } from '../../../../components/Templates/Recommendation'; import { recommendationThemeComponentProps } from '../../../themeComponents/recommendation'; import { ThemeComponent } from '../../../../providers'; -import { custom } from '../../custom'; // CSS in JS style script for the Recommendation component const recommendationStyleScript = (props: RecommendationProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // Recommendation component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts index 76eed549c..f2afc2857 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts @@ -8,38 +8,8 @@ import { custom } from '../../custom'; const recommendationBundleStyleScript = (props: RecommendationBundleProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // RecommendationBundle component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts index 65fdb5a16..81b46f881 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleEasyAdd.ts @@ -2,44 +2,13 @@ import { css } from '@emotion/react'; import type { RecommendationBundleEasyAddProps } from '../../../../components/Templates/RecommendationBundleEasyAdd'; import { recommendationBundleEasyAddThemeComponentProps } from '../../../themeComponents/recommendationBundleEasyAdd'; import { ThemeComponent } from '../../../../providers'; -import { custom } from '../../custom'; // CSS in JS style script for the RecommendationBundle component const recommendationBundleEasyAddStyleScript = (props: RecommendationBundleEasyAddProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // RecommendationBundleEasyAdd component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts index c851cb225..3e4d6763f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundleVertical.ts @@ -2,44 +2,13 @@ import { css } from '@emotion/react'; import type { RecommendationBundleVerticalProps } from '../../../../components/Templates/RecommendationBundleVertical'; import { recommendationBundleVerticalThemeComponentProps } from '../../../themeComponents/recommendationBundleVertical'; import { ThemeComponent } from '../../../../providers'; -import { custom } from '../../custom'; // CSS in JS style script for the RecommendationBundle component const recommendationBundleVerticalStyleScript = (props: RecommendationBundleVerticalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // RecommendationBundleVertical component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts index 0c6ee9263..c2a0c9fde 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts @@ -2,44 +2,13 @@ import { css } from '@emotion/react'; import type { RecommendationGridProps } from '../../../../components/Templates/RecommendationGrid'; import { recommendationGridThemeComponentProps } from '../../../themeComponents/recommendationGrid'; import { ThemeComponent } from '../../../../providers'; -import { custom } from '../../custom'; // CSS in JS style script for the RecommendationBundle component const recommendationGridStyleScript = (props: RecommendationGridProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // RecommendationGrid component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts index 4f575d082..ae8f8e229 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/search.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/search.ts @@ -8,38 +8,8 @@ import { custom } from '../../custom'; const searchStyleScript = (props: SearchProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // Search component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts index 37d50a0f5..04f511db4 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchBoca.ts @@ -8,38 +8,8 @@ import { custom } from '../../custom'; const searchBocaStyleScript = (props: SearchBocaProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; export const searchBoca: ThemeComponent<'searchBoca', SearchBocaProps> = { diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts index 34308712b..b82402eb4 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts @@ -8,38 +8,8 @@ import { custom } from '../../custom'; const searchHorizontalStyleScript = (props: SearchHorizontalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; export const searchHorizontal: ThemeComponent<'searchHorizontal', SearchHorizontalProps> = { diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts index f504d741f..182de3d4d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnapnco.ts @@ -8,38 +8,8 @@ import { custom } from '../../custom'; const searchSnapncoStyleScript = (props: SearchSnapncoProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // Search component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts index e957ecd2f..8378080ea 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchSnappy.ts @@ -8,38 +8,8 @@ import { custom } from '../../custom'; const searchSnappyStyleScript = (props: SearchSnappyProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const darkGray = custom.utils.darkenColor(variables?.colors?.text, 0.45); - const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ - fontSize: `${custom.sizes.font}px`, - '*, *:after, *:before': { - boxSizing: 'border-box', - }, - 'p, ul li, a, input, input[type], textarea, button, div': { - fontSize: custom.utils.convertPxToEm(14), - }, - 'p, ul li, a, input, input[type], textarea, select': { - color: variables?.colors?.text, - }, - 'input, input[type], textarea': { - '&::-webkit-input-placeholder': { - color: lightGray, - }, - '&::-ms-input-placeholder': { - color: lightGray, - }, - '&::placeholder': { - color: lightGray, - }, - }, - a: { - textDecoration: 'none', - '&:hover': { - color: darkGray, - }, - }, - }); + return css({}); }; // Search component props come from Template export diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 0a1cd2eed..46a76cf03 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -75,10 +75,12 @@ export const custom: { return `${value / custom.sizes.font}rem`; }, lightenColor: (color: string | undefined, amount: number) => { + // lighten a color const lightColor = new Color(color || '#515151').lighten(amount).hex().toLowerCase(); return lightColor; }, darkenColor: (color: string | undefined, amount: number) => { + // darken a color const darkColor = new Color(color || '#515151').darken(amount).hex().toLowerCase(); return darkColor; }, From c313e4bbfc5b9a1eecf4fa8f7b12a9de8840598d Mon Sep 17 00:00:00 2001 From: adria Date: Sat, 2 Aug 2025 10:14:57 -0600 Subject: [PATCH 026/118] working on slideout, mobile sidebar --- .../snap-preact-demo/templates/src/index.ts | 10 +- .../themes/pike/components/atoms/dropdown.ts | 24 ++++ .../src/themes/pike/components/atoms/index.ts | 6 + .../pike/components/molecules/slideout.ts | 5 +- .../components/organisms/mobileSidebar.ts | 112 +++++++++++++++--- 5 files changed, 133 insertions(+), 24 deletions(-) create mode 100644 packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 5396160a4..7f821a29a 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -68,11 +68,11 @@ let config: SnapTemplatesConfig = { component: 'SearchSnappy', }, ], - settings: { - infinite: { - backfill: 5, - }, - }, + // settings: { + // infinite: { + // backfill: 5, + // }, + // }, }, autocomplete: { targets: [ diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts new file mode 100644 index 000000000..5156f7634 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts @@ -0,0 +1,24 @@ +import { css } from '@emotion/react'; +import type { DropdownProps } from '../../../../components/Atoms/Dropdown'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Dropdown component +const dropdownStyleScript = ({ theme }: DropdownProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = theme?.variables; + + return css({ + '.ss__dropdown__content': { + minWidth: '1px', + }, + }); +}; + +// Dropdown component props +export const dropdown: ThemeComponent<'dropdown', DropdownProps> = { + default: { + dropdown: { + themeStyleScript: dropdownStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts index 033879b8e..0c9983f79 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/index.ts @@ -3,6 +3,7 @@ import { ThemeResponsiveComplete } from '../../../../providers'; // ATOMS Imports import { breadcrumbs } from './breadcrumbs'; import { button } from './button'; +import { dropdown } from './dropdown'; import { icon } from './icon'; import { image } from './image'; import { loadingBar } from './loadingBar'; @@ -16,6 +17,7 @@ export const atoms: ThemeResponsiveComplete = { default: { ...breadcrumbs.default, ...button.default, + ...dropdown.default, ...icon.default, ...image.default, ...loadingBar.default, @@ -28,6 +30,7 @@ export const atoms: ThemeResponsiveComplete = { mobile: { ...breadcrumbs.mobile, ...button.mobile, + ...dropdown.mobile, ...icon.mobile, ...image.mobile, ...loadingBar.mobile, @@ -39,6 +42,8 @@ export const atoms: ThemeResponsiveComplete = { }, tablet: { ...breadcrumbs.tablet, + ...button.tablet, + ...dropdown.tablet, ...icon.tablet, ...image.tablet, ...loadingBar.tablet, @@ -51,6 +56,7 @@ export const atoms: ThemeResponsiveComplete = { desktop: { ...breadcrumbs.desktop, ...button.desktop, + ...dropdown.desktop, ...icon.desktop, ...image.desktop, ...loadingBar.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts index 35781133c..b477ea5af 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts @@ -7,7 +7,10 @@ const slideoutStyleScript = (props: SlideoutProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + overflow: 'hidden', + padding: 0, + }); }; // Slideout component props diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index 5b3b9e060..925ce2584 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -10,26 +10,102 @@ const enableSummaryList = true; const mobileSidebarStyleScript = (props: MobileSidebarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const headerHeight = 60; + const footerHeight = 75; return css({ - '.ss__layout': { - gap: `${custom.spacing.x6}px`, - '& > *': { - flex: `1 1 100%`, - }, - }, - '.ss__filter-summary .ss__filter-summary__title, .ss__facets .ss__facet .ss__facet__header': { - margin: ` 0 0 ${custom.spacing.x4}px 0`, - padding: ` 0 0 ${custom.spacing.x2}px 0`, - borderBottom: `2px solid ${variables?.colors?.primary}`, - fontSize: custom.utils.convertPxToEm(16), - fontWeight: custom.fonts.weight02, - color: variables?.colors?.secondary, - }, - '.ss__facets .ss__facet': { - margin: `0 0 ${custom.spacing.x6}px 0`, - '&:last-child': { - marginBottom: 0, + background: '', + '.ss__mobile-sidebar__slideout': { + '.ss__mobile-sidebar__content': { + height: '100%', + '.ss__mobile-sidebar__header, .ss__mobile-sidebar__footer': { + padding: `0 ${custom.spacing.x4}px`, + gap: `${custom.spacing.x2}px`, + alignItems: 'center', + }, + '.ss__mobile-sidebar__header': { + height: `${headerHeight}px`, + backgroundColor: variables?.colors?.primary, + color: custom.colors.white, + '.ss__mobile-sidebar__header__title': { + margin: 0, + fontSize: custom.utils.convertPxToEm(18), + }, + '.ss__mobile-sidebar__header__close-button': { + padding: 0, + width: '16px', + height: '16px', + lineHeight: '16px', + '.ss__icon': { + width: '100%', + height: '100%', + lineHeight: 1, + }, + }, + }, + '.ss__mobile-sidebar__inner': { + height: `calc(100% - ${headerHeight + footerHeight}px)`, + overflowY: 'auto', + overflowX: 'hidden', + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + '.ss__layout': { + '&, & > *': { + display: 'block', + }, + '& > *': { + borderBottom: `1px solid ${custom.colors.gray02}`, + padding: `${custom.spacing.x4}px`, + '&:last-child': { + borderBottom: 0, + }, + }, + }, + '.ss__filter-summary, .ss__facets': { + padding: 0, + }, + '.ss__filter-summary .ss__filter-summary__title, .ss__facets .ss__facet .ss__facet__header': { + margin: 0, + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + backgroundColor: custom.colors.gray01, + border: 0, + fontSize: custom.utils.convertPxToEm(14), + fontWeight: custom.fonts.weight02, + color: variables?.colors?.text, + }, + '.ss__filter-summary .ss__filter-summary__filters, .ss__facets .ss__facet .ss__dropdown__content': { + padding: `${custom.spacing.x4}px`, + }, + '.ss__facets .ss__facet': { + margin: 0, + width: 'auto', + '&.ss__facet--collapsed': { + borderBottom: `1px solid ${custom.colors.gray02}`, + }, + '.ss__facet__header': { + '.ss__icon': { + fill: 'currentColor', + stroke: 'currentColor', + }, + }, + }, + }, + '.ss__mobile-sidebar__footer': { + height: `${footerHeight}px`, + backgroundColor: custom.colors.white, + borderTop: `1px solid ${custom.colors.gray02}`, + '.ss__button': { + flex: `1 1 0%`, + }, + }, }, }, }); From 572242b011d68661101b91aafe0460c7969219fa Mon Sep 17 00:00:00 2001 From: adria Date: Sat, 2 Aug 2025 21:23:21 -0600 Subject: [PATCH 027/118] sidebar changes mostly --- .../snap-preact-demo/templates/src/index.ts | 4 +- .../themes/pike/components/atoms/dropdown.ts | 3 ++ .../pike/components/molecules/errorHandler.ts | 23 ++++++++- .../pike/components/molecules/searchInput.ts | 10 +--- .../pike/components/molecules/select.ts | 49 ++++++++++--------- .../pike/components/molecules/slideout.ts | 5 +- .../themes/pike/components/molecules/terms.ts | 36 +++++++++++++- .../pike/components/organisms/facets.ts | 25 ++++++++++ .../themes/pike/components/organisms/index.ts | 5 ++ .../components/organisms/mobileSidebar.ts | 31 +++++++----- .../pike/components/organisms/sidebar.ts | 43 ++++++++++------ 11 files changed, 167 insertions(+), 67 deletions(-) create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/facets.ts diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 7f821a29a..d737de1d3 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -6,7 +6,7 @@ import type { SnapTemplatesConfig } from '@searchspring/snap-preact'; let config: SnapTemplatesConfig = { config: { - siteId: 'prvb79', // prvb79 // 8uyt2m + siteId: '8uyt2m', // prvb79 // 8uyt2m language: 'en', currency: 'usd', platform: 'other', @@ -65,7 +65,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchSnappy', + component: 'SearchHorizontal', }, ], // settings: { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts index 5156f7634..f257e17c3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts @@ -8,8 +8,11 @@ const dropdownStyleScript = ({ theme }: DropdownProps) => { const variables = theme?.variables; return css({ + width: 'auto', '.ss__dropdown__content': { minWidth: '1px', + left: 0, + right: 0, }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts index e8a7746a6..69432601e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/errorHandler.ts @@ -1,13 +1,31 @@ import { css } from '@emotion/react'; import type { ErrorHandlerProps } from '../../../../components/Molecules/ErrorHandler'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the ErrorHandler component const errorHandlerStyleScript = (props: ErrorHandlerProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__error-handler__message': { + display: 'block', + flex: `1 1 0%`, + color: variables?.colors?.text, + '.ss__icon': { + position: 'relative', + top: '2px', + }, + }, + '.ss__error-handler__button': { + gap: 0, + flex: `0 1 auto`, + padding: `0 ${custom.spacing.x1}px`, + height: '25px', + lineHeight: '25px', + }, + }); }; // ErrorHandler component props @@ -16,5 +34,8 @@ export const errorHandler: ThemeComponent<'errorHandler', ErrorHandlerProps> = { errorHandler: { themeStyleScript: errorHandlerStyleScript, }, + 'errorHandler icon': { + size: `${custom.sizes.icon14}px`, + }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index a7cae9edf..cf821b0c3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -8,7 +8,6 @@ const searchInputStyleScript = (props: SearchInputProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - const desktopBp = variables?.breakpoints?.mobile || 767; return css({ '&.ss__search-input': { @@ -30,7 +29,7 @@ const searchInputStyleScript = (props: SearchInputProps) => { minHeight: '1px', height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, - fontSize: custom.utils.convertPxToEm(16), + fontSize: custom.utils.convertPxToEm(14), color: variables?.colors?.text, '&::-webkit-input-placeholder': { color: lightGray, @@ -43,13 +42,6 @@ const searchInputStyleScript = (props: SearchInputProps) => { }, }, }, - [`@media (min-width: ${desktopBp + 1}px)`]: { - '&.ss__search-input': { - '.ss__search-input__input': { - fontSize: custom.utils.convertPxToEm(14), - }, - }, - }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 2297f98f2..0396043c7 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -7,7 +7,6 @@ import { custom } from '../../custom'; const selectStyleScript = (props: SelectProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const desktopBp = variables?.breakpoints?.mobile || 767; // shared styles for select menus const sharedStyles = css({ @@ -19,8 +18,8 @@ const selectStyleScript = (props: SelectProps) => { // default styles const defaultStyles = css([ { + display: 'block', '.ss__dropdown': { - width: '100%', '.ss__dropdown__button .ss__button, .ss__dropdown__content .ss__select__select': { ...sharedStyles, }, @@ -28,15 +27,23 @@ const selectStyleScript = (props: SelectProps) => { '.ss__button': { display: 'flex', padding: `0 ${custom.spacing.x2}px`, - '.ss__select__selection__icon': { - margin: 0, - }, - '.ss__select__selection': { - paddingRight: `${custom.spacing.x1}px`, - fontWeight: 'normal', - }, - '.ss__select__dropdown__button__icon': { - transition: 'transform ease .5s', + textAlign: 'left', + '.ss__button__content': { + '& > *': { + minWidth: '1px', + flex: '0 1 auto', + }, + '.ss__select__selection__icon': { + margin: 0, + }, + '.ss__select__selection': { + flex: '1 1 0%', + paddingRight: `${custom.spacing.x1}px`, + fontWeight: 'normal', + }, + '.ss__select__dropdown__button__icon': { + transition: 'transform ease .5s', + }, }, }, }, @@ -87,13 +94,18 @@ const selectStyleScript = (props: SelectProps) => { padding: `0 ${custom.spacing.x2}px`, height: `${custom.sizes.height}px`, lineHeight: `${custom.sizes.height}px`, + '& > *': { + minWidth: '1px', + flex: '0 1 auto', + }, '.ss__select__label, .ss__select__select': { - fontSize: '16px', + fontSize: custom.utils.convertPxToEm(14), }, '.ss__select__label': { fontWeight: custom.fonts.weight01, }, '.ss__select__select': { + flex: '1 1 0%', paddingRight: `${custom.spacing.x1}px`, backgroundColor: 'transparent', border: 'none', @@ -108,17 +120,8 @@ const selectStyleScript = (props: SelectProps) => { }, }, '.ss__select__dropdown__button__icon': { - width: `${custom.sizes.icon14}px`, - height: `${custom.sizes.icon14}px`, - }, - [`@media (min-width: ${desktopBp + 1}px)`]: { - '.ss__select__label, .ss__select__select': { - fontSize: '14px', - }, - '.ss__select__dropdown__button__icon': { - width: `${custom.sizes.icon12}px`, - height: `${custom.sizes.icon12}px`, - }, + width: `${custom.sizes.icon12}px`, + height: `${custom.sizes.icon12}px`, }, }, ]); diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts index b477ea5af..35781133c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/slideout.ts @@ -7,10 +7,7 @@ const slideoutStyleScript = (props: SlideoutProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({ - overflow: 'hidden', - padding: 0, - }); + return css({}); }; // Slideout component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts index e4f6d9fab..e64faeda5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/terms.ts @@ -1,13 +1,47 @@ import { css } from '@emotion/react'; import type { TermsProps } from '../../../../components/Molecules/Terms'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Terms component const termsStyleScript = (props: TermsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '.ss__terms__title': { + padding: 0, + h5: { + margin: `0 0 ${custom.spacing.x4}px 0`, + fontWeight: custom.fonts.weight02, + fontSize: custom.utils.convertPxToEm(14), + textTransform: custom.fonts.transform ? custom.fonts.transform : 'none', + color: variables?.colors?.secondary, + }, + }, + '.ss__terms__options': { + margin: `0 -${custom.spacing.x2}px -${custom.spacing.x1}px -${custom.spacing.x2}px`, + flexFlow: 'row wrap', + justifyContent: 'flex-start', + '&, .ss__terms__option': { + listStyle: 'none', + padding: 0, + }, + '.ss__terms__option': { + flex: '0 1 auto', + a: { + padding: `0 ${custom.spacing.x2}px ${custom.spacing.x1}px ${custom.spacing.x2}px`, + fontSize: custom.utils.convertPxToEm(12), + color: variables?.colors?.text, + }, + }, + '.ss__terms__option--active': { + a: { + color: variables?.colors?.primary, + }, + }, + }, + }); }; // Terms component props diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facets.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facets.ts new file mode 100644 index 000000000..ff933eb03 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facets.ts @@ -0,0 +1,25 @@ +import { css } from '@emotion/react'; +import type { FacetsProps } from '../../../../components/Organisms/Facets'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Facets component +const facetsStyleScript = (props: FacetsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + + return css({ + '&.ss__facets': { + display: 'block', + width: 'auto', + }, + }); +}; + +// Facets component props +export const facets: ThemeComponent<'facets', FacetsProps> = { + default: { + facets: { + themeStyleScript: facetsStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts index 257776ad6..3660cd8f7 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts @@ -2,6 +2,7 @@ import { ThemeResponsiveComplete } from '../../../../providers'; // ORGANISMS Imports import { facet } from './facet'; +import { facets } from './facets'; import { facetsHorizontal } from './facetsHorizontal'; import { filterSummary } from './filterSummary'; import { mobileSidebar } from './mobileSidebar'; @@ -13,6 +14,7 @@ import { toolbar } from './toolbar'; export const organisms: ThemeResponsiveComplete = { default: { ...facet.default, + ...facets.default, ...facetsHorizontal.default, ...filterSummary.default, ...mobileSidebar.default, @@ -23,6 +25,7 @@ export const organisms: ThemeResponsiveComplete = { }, mobile: { ...facet.mobile, + ...facets.mobile, ...facetsHorizontal.mobile, ...filterSummary.mobile, ...mobileSidebar.mobile, @@ -33,6 +36,7 @@ export const organisms: ThemeResponsiveComplete = { }, tablet: { ...facet.tablet, + ...facets.tablet, ...facetsHorizontal.tablet, ...filterSummary.tablet, ...mobileSidebar.tablet, @@ -43,6 +47,7 @@ export const organisms: ThemeResponsiveComplete = { }, desktop: { ...facet.desktop, + ...facets.desktop, ...facetsHorizontal.desktop, ...filterSummary.desktop, ...mobileSidebar.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index 925ce2584..f1804cada 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -14,8 +14,10 @@ const mobileSidebarStyleScript = (props: MobileSidebarProps) => { const footerHeight = 75; return css({ - background: '', '.ss__mobile-sidebar__slideout': { + overflowY: 'hidden', + padding: 0, + width: '100%', '.ss__mobile-sidebar__content': { height: '100%', '.ss__mobile-sidebar__header, .ss__mobile-sidebar__footer': { @@ -43,6 +45,14 @@ const mobileSidebarStyleScript = (props: MobileSidebarProps) => { }, }, }, + '.ss__mobile-sidebar__footer': { + height: `${footerHeight}px`, + backgroundColor: custom.colors.white, + borderTop: `1px solid ${custom.colors.gray02}`, + '.ss__button': { + flex: `1 1 0%`, + }, + }, '.ss__mobile-sidebar__inner': { height: `calc(100% - ${headerHeight + footerHeight}px)`, overflowY: 'auto', @@ -58,9 +68,8 @@ const mobileSidebarStyleScript = (props: MobileSidebarProps) => { backgroundColor: custom.colors.gray02, }, '.ss__layout': { - '&, & > *': { - display: 'block', - }, + overflow: 'hidden', + display: 'block', '& > *': { borderBottom: `1px solid ${custom.colors.gray02}`, padding: `${custom.spacing.x4}px`, @@ -69,6 +78,12 @@ const mobileSidebarStyleScript = (props: MobileSidebarProps) => { }, }, }, + '.ss__select--native': { + padding: `0 ${custom.spacing.x4}px`, + borderTop: 0, + height: '40px', + lineHeight: '40px', + }, '.ss__filter-summary, .ss__facets': { padding: 0, }, @@ -98,14 +113,6 @@ const mobileSidebarStyleScript = (props: MobileSidebarProps) => { }, }, }, - '.ss__mobile-sidebar__footer': { - height: `${footerHeight}px`, - backgroundColor: custom.colors.white, - borderTop: `1px solid ${custom.colors.gray02}`, - '.ss__button': { - flex: `1 1 0%`, - }, - }, }, }, }); diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts index a49dca53e..dbaa4bc09 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts @@ -12,24 +12,37 @@ const sidebarStyleScript = (props: SidebarProps) => { const variables = props?.theme?.variables; return css({ - '.ss__layout': { - gap: `${custom.spacing.x6}px`, - '& > *': { - flex: `1 1 100%`, - }, - }, - '.ss__filter-summary .ss__filter-summary__title, .ss__facets .ss__facet .ss__facet__header': { - margin: ` 0 0 ${custom.spacing.x4}px 0`, - padding: ` 0 0 ${custom.spacing.x2}px 0`, - borderBottom: `2px solid ${variables?.colors?.primary}`, - fontSize: custom.utils.convertPxToEm(16), + '.ss__sidebar__title': { + margin: `0 0 ${custom.spacing.x6}px 0`, + fontSize: custom.utils.convertPxToEm(20), fontWeight: custom.fonts.weight02, color: variables?.colors?.secondary, }, - '.ss__facets .ss__facet': { - margin: `0 0 ${custom.spacing.x6}px 0`, - '&:last-child': { - marginBottom: 0, + '.ss__sidebar__inner': { + '.ss__layout': { + '&, .ss__layout__row': { + display: 'block', + }, + '.ss__layout__row': { + minWidth: '1px', + '& > div:only-child': { + width: 'auto', + }, + }, + }, + '.ss__layout .ss__layout__row, .ss__facets .ss__facet': { + margin: `0 0 ${custom.spacing.x6}px 0`, + '&:last-child': { + marginBottom: 0, + }, + }, + '.ss__filter-summary .ss__filter-summary__title, .ss__facets .ss__facet .ss__facet__header': { + margin: ` 0 0 ${custom.spacing.x4}px 0`, + padding: ` 0 0 ${custom.spacing.x2}px 0`, + borderBottom: `2px solid ${variables?.colors?.primary}`, + fontSize: custom.utils.convertPxToEm(16), + fontWeight: custom.fonts.weight02, + color: variables?.colors?.secondary, }, }, }); From c7b131c2d23fe425379a24f1a7c1c6a354658d69 Mon Sep 17 00:00:00 2001 From: adria Date: Sun, 3 Aug 2025 15:19:36 -0600 Subject: [PATCH 028/118] horizontal facets --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../themes/pike/components/atoms/dropdown.ts | 6 + .../components/molecules/facetGridOptions.ts | 34 ++- .../molecules/facetHierarchyOptions.ts | 45 +++- .../components/molecules/facetListOptions.ts | 51 +++- .../molecules/facetPaletteOptions.ts | 84 +++++-- .../pike/components/molecules/pagination.ts | 24 +- .../pike/components/molecules/result.ts | 49 ++-- .../pike/components/molecules/searchInput.ts | 27 ++- .../themes/pike/components/organisms/facet.ts | 3 + .../components/organisms/facetsHorizontal.ts | 220 ++++++++++++++++-- 11 files changed, 467 insertions(+), 78 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index d737de1d3..49ca25bb3 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -65,7 +65,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchHorizontal', + component: 'SearchSnappy', }, ], // settings: { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts index f257e17c3..56713d02d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts @@ -9,10 +9,16 @@ const dropdownStyleScript = ({ theme }: DropdownProps) => { return css({ width: 'auto', + '&.ss__dropdown--open': { + '.ss__dropdown__content': { + zIndex: 1, + }, + }, '.ss__dropdown__content': { minWidth: '1px', left: 0, right: 0, + zIndex: -1, }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 30489cec0..40b8a38ae 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -8,11 +8,12 @@ import Color from 'color'; const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const isHorizontal = props?.className?.includes('horizontal') ? true : false; const activeColor = new Color(variables?.colors?.primary); const fontColor = activeColor.isDark() || activeColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); - return css({ - gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, + // shared grid styles + const sharedStyles = css({ gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', '&:before': { @@ -36,7 +37,6 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { display: 'block', width: '100%', height: '100%', - backgroundColor: custom.colors.gray01, border: `1px solid ${custom.colors.gray02}`, boxSizing: 'border-box', }, @@ -64,6 +64,34 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { }, }, }); + + // default grid styles + const defaultStyles = css([ + sharedStyles, + { + gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, + '.ss__facet-grid-options__option': { + '&:before': { + backgroundColor: custom.colors.gray01, + }, + }, + }, + ]); + + // horizontal grid styles + const horizontalStyles = css([ + sharedStyles, + { + gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '62px'}, 1fr))`, + '.ss__facet-grid-options__option': { + '&:before': { + backgroundColor: custom.colors.white, + }, + }, + }, + ]); + + return isHorizontal ? horizontalStyles : defaultStyles; }; // FacetGridOptions component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index 68cd9e1b3..5afeea4be 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -7,9 +7,11 @@ import { custom } from '../../custom'; const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const isHorizontal = props?.className?.includes('horizontal') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ + // shared hierarchy styles + const sharedStyles = css({ '.ss__facet-hierarchy-options__option': { display: 'block', margin: `0 0 ${custom.spacing.x1}px 0`, @@ -44,11 +46,46 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, - '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { - paddingLeft: `${custom.spacing.x6}px`, - }, }, }); + + // default hierarchy styles + const defaultStyles = css([ + sharedStyles, + { + '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { + '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { + paddingLeft: `${custom.spacing.x6}px`, + }, + }, + }, + ]); + + // horizontal hierarchy styles + const horizontalStyles = css([ + sharedStyles, + { + display: 'flex', + flexFlow: 'row wrap', + margin: `0 -${custom.spacing.x2}px`, + '.ss__facet-hierarchy-options__option': { + flex: '0 1 auto', + minWidth: '1px', + padding: `0 ${custom.spacing.x2}px`, + boxSizing: 'border-box', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, + '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { + '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { + paddingLeft: `${custom.spacing.x2}px`, + }, + }, + }, + ]); + + return isHorizontal ? horizontalStyles : defaultStyles; }; // FacetHierarchyOptions component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 450509100..18f06b741 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -7,22 +7,20 @@ import { custom } from '../../custom'; const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const isHorizontal = props?.className?.includes('horizontal') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); + const checkboxSpacing = 16 + custom.spacing.x2; - return css({ + // shared list styles + const sharedStyles = css({ '.ss__facet-list-options__option': { display: 'block', position: 'relative', margin: `0 0 ${custom.spacing.x1}px 0`, - padding: props?.hideCheckbox ? `` : `0 0 0 ${16 + custom.spacing.x2}px`, color: variables?.colors?.text, - '&:last-child': { - marginBottom: 0, - }, '.ss__checkbox, .ss__radio': { position: 'absolute', top: '1.5px', - left: 0, }, '.ss__facet-list-options__option__value': { margin: 0, @@ -41,6 +39,47 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { color: variables?.colors?.primary, }, }); + + // default list styles + const defaultStyles = css([ + sharedStyles, + { + '.ss__facet-list-options__option': { + padding: props?.hideCheckbox ? `` : `0 0 0 ${checkboxSpacing}px`, + '&:last-child': { + marginBottom: 0, + }, + '.ss__checkbox, .ss__radio': { + left: 0, + }, + }, + }, + ]); + + // horizontal list styles + const horizontalStyles = css([ + sharedStyles, + { + display: 'flex', + flexFlow: 'row wrap', + margin: `0 -${custom.spacing.x2}px`, + '.ss__facet-list-options__option': { + flex: '0 1 auto', + minWidth: '1px', + padding: props?.hideCheckbox ? `0 ${custom.spacing.x2}px` : `0 ${custom.spacing.x2}px 0 ${checkboxSpacing + custom.spacing.x2}px`, + boxSizing: 'border-box', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + '.ss__checkbox, .ss__radio': { + left: `${custom.spacing.x2}px`, + backgroundColor: custom.colors.white, + }, + }, + }, + ]); + + return isHorizontal ? horizontalStyles : defaultStyles; }; // FacetListOptions component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 91c40d87e..97d438d6f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const isHorizontal = props?.className?.includes('horizontal') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); const hasCheckbox = !props?.hideCheckbox ? true : false; @@ -116,11 +117,21 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { }, }); - // grid styles + // default palette grid styles + const defaultGridStyles = css({ + gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, + }); + + // default palette grid horizontal styles + const defaultGridHorizontalStyles = css({ + gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '62px'}, 1fr))`, + }); + + // grid palette styles const gridStyles = css([ sharedStyles, + isHorizontal ? defaultGridHorizontalStyles : defaultGridStyles, { - gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', '.ss__facet-palette-options__option': { @@ -132,7 +143,7 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { padding: '0 0 100% 0', }, }, - '.ss__checkbox': { + '.ss__checkbox, .ss__radio': { display: 'none', }, '.ss__facet-palette-options__option__value, .ss__facet-palette-options__option__value__count': { @@ -153,33 +164,69 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { }, ]); - // list styles + // list variables const listSize = hasCheckbox ? 16 : 22; const listCheckboxSize = 16; const listPadding = hasCheckbox ? custom.spacing.x4 + listSize + listCheckboxSize : custom.spacing.x2 + listSize; + + // default palette list styles + const defaultListStyles = css({ + '&.ss__facet-palette-options--list': { + display: 'block', + }, + '.ss__facet-palette-options__option': { + padding: `${hasCheckbox ? 0 : '2px'} 0 0 ${listPadding}px`, + '&:last-child': { + marginBottom: 0, + }, + '.ss__checkbox, .ss__radio': { + left: 0, + }, + '.ss__facet-palette-options__option__wrapper': { + left: hasCheckbox ? `${listCheckboxSize + custom.spacing.x2}px` : 0, + }, + }, + }); + + // default palette list horizontal styles + const defaultListHorizontalStyles = css({ + '&.ss__facet-palette-options--list': { + display: 'flex', + flexFlow: 'row wrap', + margin: `0 -${custom.spacing.x2}px`, + }, + '.ss__facet-palette-options__option': { + flex: '0 1 auto', + minWidth: '1px', + padding: `${hasCheckbox ? 0 : '2px'} ${custom.spacing.x2}px 0 ${listPadding + custom.spacing.x2}px`, + boxSizing: 'border-box', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + '.ss__checkbox, .ss__radio': { + left: `${custom.spacing.x2}px`, + backgroundColor: custom.colors.white, + }, + '.ss__facet-palette-options__option__wrapper': { + left: hasCheckbox ? `${listCheckboxSize + custom.spacing.x4}px` : `${custom.spacing.x2}px`, + }, + }, + }); + + // list palette styles const listStyles = css([ sharedStyles, + isHorizontal ? defaultListHorizontalStyles : defaultListStyles, { - '&.ss__facet-palette-options--list': { - display: 'block', - }, '.ss__facet-palette-options__option': { position: 'relative', margin: `0 0 ${custom.spacing.x1}px 0`, - padding: `${hasCheckbox ? 0 : custom.spacing.x1 + 'px'} 0 0 ${listPadding}px`, - minHeight: hasCheckbox ? '' : `${listSize}px`, - '&:last-child': { - marginBottom: 0, - }, - '.ss__checkbox, .ss__facet-palette-options__option__wrapper': { + minHeight: hasCheckbox ? '' : `${listSize + 2}px`, + '.ss__checkbox, .ss__radio, .ss__facet-palette-options__option__wrapper': { position: 'absolute', - top: `${hasCheckbox ? '2' : '3.5'}px`, - }, - '.ss__checkbox': { - left: 0, + top: `${hasCheckbox ? 2 : 0.5}px`, }, '.ss__facet-palette-options__option__wrapper': { - left: hasCheckbox ? `${listCheckboxSize + custom.spacing.x2}px` : 0, width: `${listSize}px`, height: `${listSize}px`, lineHeight: `${listSize}px`, @@ -209,6 +256,7 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal hideIcon: true, gridSize: '52px', gapSize: `${custom.spacing.x1}px`, + layout: 'grid', colorMapping: { brown: { background: custom.colors.brown, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts index 85d0443a3..ccb97eedc 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/pagination.ts @@ -7,7 +7,7 @@ import { custom } from '../../custom'; const paginationStyleScript = (props: PaginationProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const desktopBp = variables?.breakpoints?.mobile || 767; + const mobileBp = variables?.breakpoints?.mobile || 767; return css({ nav: { @@ -17,8 +17,8 @@ const paginationStyleScript = (props: PaginationProps) => { justifyContent: 'center', lineHeight: 1, '.ss__pagination__page, span': { - padding: `0 ${custom.spacing.x2}px`, - fontSize: custom.utils.convertPxToEm(16), + padding: `0 ${custom.spacing.x1}px`, + fontSize: custom.utils.convertPxToEm(14), color: variables?.colors?.text, }, '.ss__pagination__page': { @@ -29,21 +29,23 @@ const paginationStyleScript = (props: PaginationProps) => { color: variables?.colors?.primary, }, '.ss__pagination__page--previous, .ss__pagination__page--next': { - lineHeight: `${custom.sizes.icon14}px`, + lineHeight: `${custom.sizes.icon12}px`, '.ss__icon': { + position: 'relative', + top: '0.5px', fill: variables?.colors?.primary, stroke: variables?.colors?.primary, }, }, }, - [`@media (min-width: ${desktopBp + 1}px)`]: { + [`@media (max-width: ${mobileBp}px)`]: { nav: { '.ss__pagination__page, span': { - padding: `0 ${custom.spacing.x1}px`, - fontSize: custom.utils.convertPxToEm(14), + padding: `0 ${custom.spacing.x2}px`, + fontSize: custom.utils.convertPxToEm(16), }, '.ss__pagination__page--previous, .ss__pagination__page--next': { - lineHeight: `${custom.sizes.icon12}px`, + lineHeight: `${custom.sizes.icon14}px`, }, }, }, @@ -57,7 +59,7 @@ export const pagination: ThemeComponent<'pagination', PaginationProps> = { themeStyleScript: paginationStyleScript, }, 'pagination icon': { - size: `${custom.sizes.icon12}px`, + size: `${custom.sizes.icon14}px`, }, 'pagination icon.prev': { icon: custom.icons.arrowLeft, @@ -66,9 +68,9 @@ export const pagination: ThemeComponent<'pagination', PaginationProps> = { icon: custom.icons.arrowRight, }, }, - mobile: { + desktop: { 'pagination icon': { - size: `${custom.sizes.icon14}px`, + size: `${custom.sizes.icon12}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts index baa9755fe..62c31e9e5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/result.ts @@ -27,15 +27,34 @@ const resultStyleScript = (props: ResultProps) => { }, }, '&.ss__result--list': { + display: 'flex', + flexFlow: 'row wrap', + alignItems: 'center', + '.ss__result__image-wrapper, .ss__result__details': { + minWidth: '1px', + }, + '.ss__result__image-wrapper': { + flex: '0 0 33.33%', + margin: `0 ${custom.spacing.x4}px 0 0`, + }, '.ss__result__details': { - textAlign: 'center', + flex: '1 1 0%', + textAlign: 'left', margin: 0, + '.ss__callout-badge, .ss__result__rating-wrapper': { + justifyContent: 'flex-start', + }, '.ss__result__details__title': { + flex: '1 1 0%', a: { fontSize: custom.utils.convertPxToEm(18), fontWeight: custom.fonts.weight02, }, }, + '.ss__result__details__pricing': { + flex: '0 1 auto', + order: -1, + }, }, }, '.ss__result__image-wrapper': { @@ -74,32 +93,24 @@ const resultStyleScript = (props: ResultProps) => { }, }, }, - '@media (min-width: 541px)': { + '@media (max-width: 540px)': { '&.ss__result--list': { - display: 'flex', - flexFlow: 'row wrap', - alignItems: 'center', - '.ss__result__image-wrapper, .ss__result__details': { - minWidth: '1px', - }, - '.ss__result__image-wrapper': { - flex: '0 0 33.33%', - margin: `0 ${custom.spacing.x4}px 0 0`, - }, + display: 'block', '.ss__result__details': { - flex: '1 1 0%', - textAlign: 'left', + textAlign: 'center', '.ss__callout-badge, .ss__result__rating-wrapper': { - justifyContent: 'flex-start', + justifyContent: 'center', }, - '.ss__result__details__title': { - flex: '1 1 0%', + '.ss__result__details__title, .ss__result__details__pricing': { + flex: '1 1 100%', }, '.ss__result__details__pricing': { - flex: '0 1 auto', - order: -1, + order: 0, }, }, + '.ss__result__image-wrapper': { + margin: `0 0 ${custom.spacing.x2}px 0`, + }, }, }, }); diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index cf821b0c3..3ef6eaaa3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -7,13 +7,14 @@ import { custom } from '../../custom'; const searchInputStyleScript = (props: SearchInputProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const isHorizontal = props?.className?.includes('horizontal') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({ + // shared search input styles + const sharedStyles = css({ '&.ss__search-input': { margin: `0 0 ${custom.spacing.x2}px`, border: `1px solid ${custom.colors.gray02}`, - backgroundColor: `${custom.colors.gray01}`, '.ss__icon, .ss__search-input__input': { minWidth: '1px', padding: 0, @@ -43,6 +44,28 @@ const searchInputStyleScript = (props: SearchInputProps) => { }, }, }); + + // default search input styles + const defaultStyles = css([ + sharedStyles, + { + '&.ss__search-input': { + backgroundColor: `${custom.colors.gray01}`, + }, + }, + ]); + + // horizontal search input styles + const horizontalStyles = css([ + sharedStyles, + { + '&.ss__search-input': { + backgroundColor: `${custom.colors.white}`, + }, + }, + ]); + + return isHorizontal ? horizontalStyles : defaultStyles; }; // SearchInput component props diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts index 4e6b9b0f3..b58394b9f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facet.ts @@ -57,6 +57,9 @@ const facetStyleScript = (props: FacetProps) => { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, '.ss__icon': { + position: 'relative', + top: '-0.5px', + marginRight: `${custom.spacing.x1}px`, width: `${custom.sizes.icon10}px`, height: `${custom.sizes.icon10}px`, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts index fa94bf0af..d6c6c7485 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts @@ -1,13 +1,182 @@ import { css } from '@emotion/react'; import type { FacetsHorizontalProps } from '../../../../components/Organisms/FacetsHorizontal'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Facets component const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const mobileBp = variables?.breakpoints?.mobile || 767; + const tabletBp = variables?.breakpoints?.tablet || 1024; + //const mobileBp = variables?.breakpoints?.mobile || 767; - return css({}); + return css({ + margin: 0, + '.ss__facets-horizontal__header': { + gap: 0, + margin: `0 -${custom.spacing.x1}px ${custom.spacing.x2}px -${custom.spacing.x1}px `, + position: 'relative', + '& > *': { + boxSizing: 'border-box', + minWidth: '1px', + width: `${100 / 6}%`, + flex: '0 1 auto', + padding: `0 ${custom.spacing.x1}px`, + }, + '& > *, & > .ss__dropdown, .ss__mobile-sidebar': { + margin: `0 0 ${custom.spacing.x2}px 0`, + }, + '& > .ss__dropdown': { + position: 'static', + '&.ss__dropdown--open': { + '.ss__dropdown__button': { + '.ss__dropdown__button__heading': { + '.ss__icon': { + transform: 'rotate(180deg)', + }, + }, + }, + '.ss__dropdown__content': { + width: 'auto', + minWidth: '1px', + maxHeight: 'none', + overflowY: 'visible', + padding: `${custom.spacing.x2}px`, + marginTop: `${custom.spacing.x2}px`, + left: `${custom.spacing.x1}px`, + right: `${custom.spacing.x1}px`, + }, + }, + '&.ss__facets-horizontal__header__dropdown--slider': { + '.ss__dropdown__content': { + '.ss__facet__options': { + maxHeight: `none`, + overflow: 'visible', + paddingRight: 0, + }, + }, + }, + '.ss__dropdown__button, .ss__dropdown__content': { + border: `1px solid ${custom.colors.gray02}`, + backgroundColor: custom.colors.gray01, + }, + '.ss__dropdown__button': { + display: 'block', + height: `${custom.sizes.height}px`, + lineHeight: `${custom.sizes.height}px`, + padding: `0 ${custom.spacing.x2}px`, + textAlign: 'left', + color: variables?.colors?.text, + '.ss__dropdown__button__heading': { + flexFlow: 'row nowrap', + justifyContent: 'flex-start', + gap: `${custom.spacing.x1}px`, + padding: 0, + '& > *': { + minWidth: '1px', + flex: '0 1 auto', + }, + span: { + flex: '1 1 0%', + paddingRight: `${custom.spacing.x1}px`, + fontWeight: custom.fonts.weight01, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + }, + '.ss__icon': { + transition: 'transform ease .5s', + }, + }, + }, + '.ss__dropdown__content': { + width: 'auto', + padding: `${custom.spacing.x2}px`, + '.ss__facet__options': { + maxHeight: `335px`, + overflowY: 'auto', + overflowX: 'hidden', + paddingRight: `${custom.spacing.x2}px`, + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + }, + '.ss__facet-hierarchy-options--horizontal, .ss__facet-list-options--horizontal, .ss__facet-palette-options--list.ss__facet-palette-options--horizontal': + { + '& > *': { + width: `${100 / 4}%`, + }, + }, + '.ss__facet-palette-options--list.ss__facet-palette-options--horizontal': { + '& > *': { + maxWidth: `24%`, + }, + }, + '.ss__facet__show-more-less': { + margin: `${custom.spacing.x2}px 0 0 0`, + fontWeight: custom.fonts.weight01, + textAlign: 'center', + color: variables?.colors?.primary, + '.ss__icon': { + position: 'relative', + top: '-0.5px', + marginRight: `${custom.spacing.x1}px`, + width: `${custom.sizes.icon10}px`, + height: `${custom.sizes.icon10}px`, + }, + }, + }, + }, + }, + [`@media (max-width: ${tabletBp}px)`]: { + '.ss__facets-horizontal__header': { + '& > *': { + width: `${100 / 4}%`, + }, + '& > .ss__dropdown .ss__dropdown__content': { + '.ss__facet-hierarchy-options--horizontal, .ss__facet-list-options--horizontal, .ss__facet-palette-options--list.ss__facet-palette-options--horizontal': + { + '& > *': { + width: `${100 / 3}%`, + }, + }, + '.ss__facet-palette-options--list.ss__facet-palette-options--horizontal': { + '& > *': { + maxWidth: `32%`, + }, + }, + }, + }, + }, + [`@media (max-width: ${mobileBp}px)`]: { + '.ss__facets-horizontal__header': { + '& > *': { + width: `${100 / 2}%`, + }, + '& > .ss__dropdown .ss__dropdown__content': { + '.ss__facet-hierarchy-options--horizontal, .ss__facet-list-options--horizontal, .ss__facet-palette-options--list.ss__facet-palette-options--horizontal': + { + '& > *': { + width: `${100 / 2}%`, + }, + }, + '.ss__facet-palette-options--list.ss__facet-palette-options--horizontal': { + '& > *': { + maxWidth: `48%`, + }, + }, + }, + }, + }, + }); }; // FacetsHorizontal component props @@ -15,22 +184,45 @@ export const facetsHorizontal: ThemeComponent<'facetsHorizontal', FacetsHorizont default: { facetsHorizontal: { themeStyleScript: facetsHorizontalStyleScript, - limit: 9, + iconExpand: custom.icons.arrowDown, + iconCollapse: custom.icons.arrowDown, }, - }, - mobile: { - facetsHorizontal: { - limit: 0, + 'facetsHorizontal dropdown button icon': { + size: `${custom.sizes.icon12}px`, }, - }, - tablet: { - facetsHorizontal: { - limit: 5, + 'facetsHorizontal facetGridOptions': { + className: 'ss__facet-grid-options--horizontal', + gridSize: '62px', }, - }, - desktop: { - facetsHorizontal: { - limit: 7, + 'facetsHorizontal mobileSidebar facetGridOptions': { + className: '', + gridSize: '52px', + }, + 'facetsHorizontal facetHierarchyOptions': { + className: 'ss__facet-hierarchy-options--horizontal', + }, + 'facetsHorizontal mobileSidebar facetHierarchyOptions': { + className: '', + }, + 'facetsHorizontal facetListOptions': { + className: 'ss__facet-list-options--horizontal', + }, + 'facetsHorizontal mobileSidebar facetListOptions': { + className: '', + }, + 'facetsHorizontal facetPaletteOptions': { + className: 'ss__facet-palette-options--horizontal', + gridSize: '62px', + }, + 'facetsHorizontal mobileSidebar facetPaletteOptions': { + className: '', + gridSize: '52px', + }, + 'facetsHorizontal searchInput': { + className: 'ss__search-input--horizontal', + }, + 'facetsHorizontal mobileSidebar searchInput': { + className: '', }, }, }; From 58182d7640a55f85bf1c00c3b2361bba305b12ab Mon Sep 17 00:00:00 2001 From: adria Date: Sun, 3 Aug 2025 17:54:34 -0600 Subject: [PATCH 029/118] update search result content --- .../components/molecules/facetGridOptions.ts | 3 +- .../molecules/facetPaletteOptions.ts | 12 +---- .../pike/components/organisms/autocomplete.ts | 20 +++++++++ .../components/organisms/facetsHorizontal.ts | 44 +++++++++---------- .../themes/pike/components/organisms/index.ts | 10 +++++ .../pike/components/organisms/noResults.ts | 33 +++++++++++++- .../pike/components/organisms/results.ts | 27 ++++++++++++ .../pike/components/organisms/termsList.ts | 2 +- .../pike/components/organisms/toolbar.ts | 36 ++++++++++++++- .../components/src/themes/pike/custom.ts | 2 + 10 files changed, 150 insertions(+), 39 deletions(-) create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/results.ts diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 40b8a38ae..c3221887c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -14,6 +14,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { // shared grid styles const sharedStyles = css({ + gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', '&:before': { @@ -69,7 +70,6 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { const defaultStyles = css([ sharedStyles, { - gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, '.ss__facet-grid-options__option': { '&:before': { backgroundColor: custom.colors.gray01, @@ -82,7 +82,6 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { const horizontalStyles = css([ sharedStyles, { - gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '62px'}, 1fr))`, '.ss__facet-grid-options__option': { '&:before': { backgroundColor: custom.colors.white, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 97d438d6f..96888446c 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -117,21 +117,11 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { }, }); - // default palette grid styles - const defaultGridStyles = css({ - gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, - }); - - // default palette grid horizontal styles - const defaultGridHorizontalStyles = css({ - gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '62px'}, 1fr))`, - }); - // grid palette styles const gridStyles = css([ sharedStyles, - isHorizontal ? defaultGridHorizontalStyles : defaultGridStyles, { + gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', '.ss__facet-palette-options__option': { diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts new file mode 100644 index 000000000..705075205 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { AutocompleteProps } from '../../../../components/Organisms/Autocomplete'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Autocomplete component +const autocompleteStyleScript = (props: AutocompleteProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + + return css({}); +}; + +// Autocomplete component props +export const autocomplete: ThemeComponent<'autocomplete', AutocompleteProps> = { + default: { + autocomplete: { + themeStyleScript: autocompleteStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts index d6c6c7485..70f520c04 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts @@ -9,7 +9,8 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { const variables = props?.theme?.variables; const mobileBp = variables?.breakpoints?.mobile || 767; const tabletBp = variables?.breakpoints?.tablet || 1024; - //const mobileBp = variables?.breakpoints?.mobile || 767; + const columnsPaletteSelector = `.ss__facet-palette-options--list.ss__facet-palette-options--horizontal`; + const columnsSelector = `.ss__facet-hierarchy-options--horizontal, .ss__facet-list-options--horizontal, ${columnsPaletteSelector}`; return css({ margin: 0, @@ -24,10 +25,10 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { flex: '0 1 auto', padding: `0 ${custom.spacing.x1}px`, }, - '& > *, & > .ss__dropdown, .ss__mobile-sidebar': { + '& > *, .ss__facets-horizontal__header__dropdown, .ss__mobile-sidebar': { margin: `0 0 ${custom.spacing.x2}px 0`, }, - '& > .ss__dropdown': { + '.ss__facets-horizontal__header__dropdown': { position: 'static', '&.ss__dropdown--open': { '.ss__dropdown__button': { @@ -109,13 +110,12 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { backgroundColor: custom.colors.gray02, }, }, - '.ss__facet-hierarchy-options--horizontal, .ss__facet-list-options--horizontal, .ss__facet-palette-options--list.ss__facet-palette-options--horizontal': - { - '& > *': { - width: `${100 / 4}%`, - }, + [columnsSelector]: { + '& > *': { + width: `${100 / 4}%`, }, - '.ss__facet-palette-options--list.ss__facet-palette-options--horizontal': { + }, + [columnsPaletteSelector]: { '& > *': { maxWidth: `24%`, }, @@ -141,14 +141,13 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { '& > *': { width: `${100 / 4}%`, }, - '& > .ss__dropdown .ss__dropdown__content': { - '.ss__facet-hierarchy-options--horizontal, .ss__facet-list-options--horizontal, .ss__facet-palette-options--list.ss__facet-palette-options--horizontal': - { - '& > *': { - width: `${100 / 3}%`, - }, + '.ss__facets-horizontal__header__dropdown .ss__dropdown__content': { + [columnsSelector]: { + '& > *': { + width: `${100 / 3}%`, }, - '.ss__facet-palette-options--list.ss__facet-palette-options--horizontal': { + }, + [columnsPaletteSelector]: { '& > *': { maxWidth: `32%`, }, @@ -161,14 +160,13 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { '& > *': { width: `${100 / 2}%`, }, - '& > .ss__dropdown .ss__dropdown__content': { - '.ss__facet-hierarchy-options--horizontal, .ss__facet-list-options--horizontal, .ss__facet-palette-options--list.ss__facet-palette-options--horizontal': - { - '& > *': { - width: `${100 / 2}%`, - }, + '.ss__facets-horizontal__header__dropdown .ss__dropdown__content': { + [columnsSelector]: { + '& > *': { + width: `${100 / 2}%`, }, - '.ss__facet-palette-options--list.ss__facet-palette-options--horizontal': { + }, + [columnsPaletteSelector]: { '& > *': { maxWidth: `48%`, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts index 3660cd8f7..2a6ce520d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts @@ -1,57 +1,67 @@ import { ThemeResponsiveComplete } from '../../../../providers'; // ORGANISMS Imports +import { autocomplete } from './autocomplete'; import { facet } from './facet'; import { facets } from './facets'; import { facetsHorizontal } from './facetsHorizontal'; import { filterSummary } from './filterSummary'; import { mobileSidebar } from './mobileSidebar'; import { noResults } from './noResults'; +import { results } from './results'; import { sidebar } from './sidebar'; import { termsList } from './termsList'; import { toolbar } from './toolbar'; export const organisms: ThemeResponsiveComplete = { default: { + ...autocomplete.default, ...facet.default, ...facets.default, ...facetsHorizontal.default, ...filterSummary.default, ...mobileSidebar.default, ...noResults.default, + ...results.default, ...sidebar.default, ...toolbar.default, ...termsList.default, }, mobile: { + ...autocomplete.mobile, ...facet.mobile, ...facets.mobile, ...facetsHorizontal.mobile, ...filterSummary.mobile, ...mobileSidebar.mobile, ...noResults.mobile, + ...results.mobile, ...sidebar.mobile, ...toolbar.mobile, ...termsList.mobile, }, tablet: { + ...autocomplete.tablet, ...facet.tablet, ...facets.tablet, ...facetsHorizontal.tablet, ...filterSummary.tablet, ...mobileSidebar.tablet, ...noResults.tablet, + ...results.tablet, ...sidebar.tablet, ...toolbar.tablet, ...termsList.tablet, }, desktop: { + ...autocomplete.desktop, ...facet.desktop, ...facets.desktop, ...facetsHorizontal.desktop, ...filterSummary.desktop, ...mobileSidebar.desktop, ...noResults.desktop, + ...results.desktop, ...sidebar.desktop, ...toolbar.desktop, ...termsList.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts index a8710742c..966521326 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts @@ -1,13 +1,44 @@ import { css } from '@emotion/react'; import type { NoResultsProps } from '../../../../components/Organisms/NoResults'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the NoResults component const noResultsStyleScript = (props: NoResultsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + 'h1, h2, h3, h4, h5, h6, ul': { + margin: `0 0 ${custom.spacing.x4}px 0`, + }, + 'h1, h2, h3, h4, h5, h6': { + fontSize: custom.utils.convertPxToEm(20), + fontWeight: custom.fonts.weight02, + color: variables?.colors?.secondary, + }, + 'ul li, p': { + color: variables?.colors?.text, + }, + a: { + color: variables?.colors?.primary, + '&:hover': { + color: variables?.colors?.secondary, + }, + }, + ul: { + padding: 0, + marginLeft: `${custom.spacing.x8}px`, + listStyle: 'none', + li: { + listStyle: 'disc', + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-child': { + marginBottom: 0, + }, + }, + }, + }); }; // NoResults component props diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts new file mode 100644 index 000000000..490511722 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts @@ -0,0 +1,27 @@ +import { css } from '@emotion/react'; +import type { ResultsProps } from '../../../../components/Organisms/Results'; +import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; + +// CSS in JS style script for the Results component +const resultsStyleScript = (props: ResultsProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + const mobileBp = variables?.breakpoints?.mobile || 767; + + return css({ + gap: `${custom.spacing.x6}px ${custom.spacing.x4}px`, + [`@media (max-width: ${mobileBp}px)`]: { + gap: `${custom.spacing.x6}px ${custom.spacing.x2}px`, + }, + }); +}; + +// Results component props +export const results: ThemeComponent<'results', ResultsProps> = { + default: { + results: { + themeStyleScript: resultsStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts index 26f38e55a..2b190254d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import type { TermsListProps } from '../../../../components/Organisms/TermsList'; import { ThemeComponent } from '../../../../providers'; -// CSS in JS style script for the Terms component +// CSS in JS style script for the TermsList component const termsListStyleScript = (props: TermsListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts index c7d256254..905b35876 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts @@ -1,13 +1,47 @@ import { css } from '@emotion/react'; import { ThemeComponent } from '../../../../providers'; import { ToolbarProps } from '../../../../components/Organisms/Toolbar'; +import { custom } from '../../custom'; // CSS in JS style script for the Toolbar component const toolbarStyleScript = (props: ToolbarProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const mobileBp = variables?.breakpoints?.mobile || 767; - return css({}); + // shared toolbar styles + const sharedStyles = css({ + '.ss__layout': { + gap: `${custom.spacing.x2}px`, + margin: 0, + }, + }); + + // default toolbar styles + const defaultStyles = css([ + sharedStyles, + { + margin: `0 0 ${custom.spacing.x4}px 0`, + '.ss__pagination-info': { + fontSize: custom.utils.convertPxToEm(16), + }, + [`@media (max-width: ${mobileBp}px)`]: { + '.ss__pagination-info': { + fontSize: custom.utils.convertPxToEm(18), + }, + }, + }, + ]); + + // bottom toolbar styles + const bottomStyles = css([ + sharedStyles, + { + margin: `${custom.spacing.x4}px 0 0 0`, + }, + ]); + + return props?.name == 'bottom' ? bottomStyles : defaultStyles; }; // Toolbar component props diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index 46a76cf03..c7f3d0bc2 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -68,6 +68,8 @@ export const custom: { x4: 20, x5: 25, x6: 30, + x7: 35, + x8: 40, }, utils: { convertPxToEm: (value: number) => { From e08ac4c789fe3e403e6e25946d6f2ce50cca2fd5 Mon Sep 17 00:00:00 2001 From: adria Date: Mon, 4 Aug 2025 09:53:39 -0600 Subject: [PATCH 030/118] changes for horizontal facets --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../themes/pike/components/atoms/dropdown.ts | 2 +- .../pike/components/molecules/checkbox.ts | 56 +++++++++++----- .../components/molecules/facetGridOptions.ts | 8 +-- .../molecules/facetHierarchyOptions.ts | 45 ++----------- .../components/molecules/facetListOptions.ts | 50 ++------------ .../molecules/facetPaletteOptions.ts | 59 ++++------------- .../themes/pike/components/molecules/radio.ts | 55 +++++++++++----- .../pike/components/molecules/searchInput.ts | 8 +-- .../components/organisms/facetsHorizontal.ts | 66 +++++++++---------- .../components/templates/searchHorizontal.ts | 4 +- 11 files changed, 141 insertions(+), 214 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 49ca25bb3..d737de1d3 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -65,7 +65,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchSnappy', + component: 'SearchHorizontal', }, ], // settings: { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts index 56713d02d..9c42708ae 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts @@ -11,7 +11,7 @@ const dropdownStyleScript = ({ theme }: DropdownProps) => { width: 'auto', '&.ss__dropdown--open': { '.ss__dropdown__content': { - zIndex: 1, + zIndex: 2, }, }, '.ss__dropdown__content': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts index ddb02287b..ad5ce244f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/checkbox.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const checkboxStyleScript = (props: CheckboxProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const isSecondary = props?.className?.includes('secondary') ? true : false; const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); // shared checkbox styles @@ -14,6 +15,23 @@ const checkboxStyleScript = (props: CheckboxProps) => { position: 'relative', top: '-1px', }); + const sharedDefaultStyles = css({ + border: `1px solid ${custom.colors.gray02}`, + '&, *': { + boxSizing: 'border-box', + }, + '.ss__icon': { + width: '8px', + height: '8px', + }, + '&.ss__checkbox--active': { + borderColor: darkGray, + '.ss__icon': { + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, + }, + }); const disabledStyles = css({ '&.ss__checkbox--disabled': { opacity: 0.65, @@ -23,31 +41,26 @@ const checkboxStyleScript = (props: CheckboxProps) => { }, }); - // default styles + // default checkbox styles const defaultStyles = css([ sharedStyles, + sharedDefaultStyles, { backgroundColor: custom.colors.gray01, - border: `1px solid ${custom.colors.gray02}`, - '&, *': { - boxSizing: 'border-box', - }, - '.ss__icon': { - width: '8px', - height: '8px', - }, - '&.ss__checkbox--active': { - borderColor: darkGray, - '.ss__icon': { - fill: variables?.colors?.primary, - stroke: variables?.colors?.primary, - }, - }, }, disabledStyles, ]); - // native styles + // secondary checkbox styles + const secondaryStyles = css([ + sharedStyles, + sharedDefaultStyles, + { + backgroundColor: custom.colors.white, + }, + ]); + + // native checkbox styles const nativeStyles = css([ sharedStyles, { @@ -59,7 +72,14 @@ const checkboxStyleScript = (props: CheckboxProps) => { disabledStyles, ]); - return props?.native ? nativeStyles : defaultStyles; + // return checkbox styles + if (props?.native) { + return nativeStyles; + } else if (isSecondary) { + return secondaryStyles; + } else { + return defaultStyles; + } }; // Checkbox component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index c3221887c..16b4f18dc 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -8,7 +8,7 @@ import Color from 'color'; const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const isHorizontal = props?.className?.includes('horizontal') ? true : false; + const isSecondary = props?.className?.includes('secondary') ? true : false; const activeColor = new Color(variables?.colors?.primary); const fontColor = activeColor.isDark() || activeColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); @@ -78,8 +78,8 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { }, ]); - // horizontal grid styles - const horizontalStyles = css([ + // secondary grid styles + const secondaryStyles = css([ sharedStyles, { '.ss__facet-grid-options__option': { @@ -90,7 +90,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { }, ]); - return isHorizontal ? horizontalStyles : defaultStyles; + return isSecondary ? secondaryStyles : defaultStyles; }; // FacetGridOptions component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index 5afeea4be..68cd9e1b3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -7,11 +7,9 @@ import { custom } from '../../custom'; const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const isHorizontal = props?.className?.includes('horizontal') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - // shared hierarchy styles - const sharedStyles = css({ + return css({ '.ss__facet-hierarchy-options__option': { display: 'block', margin: `0 0 ${custom.spacing.x1}px 0`, @@ -46,46 +44,11 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, - }, - }); - - // default hierarchy styles - const defaultStyles = css([ - sharedStyles, - { - '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { - '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { - paddingLeft: `${custom.spacing.x6}px`, - }, + '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { + paddingLeft: `${custom.spacing.x6}px`, }, }, - ]); - - // horizontal hierarchy styles - const horizontalStyles = css([ - sharedStyles, - { - display: 'flex', - flexFlow: 'row wrap', - margin: `0 -${custom.spacing.x2}px`, - '.ss__facet-hierarchy-options__option': { - flex: '0 1 auto', - minWidth: '1px', - padding: `0 ${custom.spacing.x2}px`, - boxSizing: 'border-box', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - }, - '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { - '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { - paddingLeft: `${custom.spacing.x2}px`, - }, - }, - }, - ]); - - return isHorizontal ? horizontalStyles : defaultStyles; + }); }; // FacetHierarchyOptions component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 18f06b741..5a556457f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -7,20 +7,23 @@ import { custom } from '../../custom'; const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const isHorizontal = props?.className?.includes('horizontal') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); const checkboxSpacing = 16 + custom.spacing.x2; - // shared list styles - const sharedStyles = css({ + return css({ '.ss__facet-list-options__option': { display: 'block', position: 'relative', margin: `0 0 ${custom.spacing.x1}px 0`, color: variables?.colors?.text, + padding: props?.hideCheckbox ? `` : `0 0 0 ${checkboxSpacing}px`, + '&:last-child': { + marginBottom: 0, + }, '.ss__checkbox, .ss__radio': { position: 'absolute', top: '1.5px', + left: 0, }, '.ss__facet-list-options__option__value': { margin: 0, @@ -39,47 +42,6 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { color: variables?.colors?.primary, }, }); - - // default list styles - const defaultStyles = css([ - sharedStyles, - { - '.ss__facet-list-options__option': { - padding: props?.hideCheckbox ? `` : `0 0 0 ${checkboxSpacing}px`, - '&:last-child': { - marginBottom: 0, - }, - '.ss__checkbox, .ss__radio': { - left: 0, - }, - }, - }, - ]); - - // horizontal list styles - const horizontalStyles = css([ - sharedStyles, - { - display: 'flex', - flexFlow: 'row wrap', - margin: `0 -${custom.spacing.x2}px`, - '.ss__facet-list-options__option': { - flex: '0 1 auto', - minWidth: '1px', - padding: props?.hideCheckbox ? `0 ${custom.spacing.x2}px` : `0 ${custom.spacing.x2}px 0 ${checkboxSpacing + custom.spacing.x2}px`, - boxSizing: 'border-box', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - '.ss__checkbox, .ss__radio': { - left: `${custom.spacing.x2}px`, - backgroundColor: custom.colors.white, - }, - }, - }, - ]); - - return isHorizontal ? horizontalStyles : defaultStyles; }; // FacetListOptions component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 96888446c..3897b3b83 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -7,7 +7,6 @@ import { custom } from '../../custom'; const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const isHorizontal = props?.className?.includes('horizontal') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); const hasCheckbox = !props?.hideCheckbox ? true : false; @@ -159,64 +158,30 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { const listCheckboxSize = 16; const listPadding = hasCheckbox ? custom.spacing.x4 + listSize + listCheckboxSize : custom.spacing.x2 + listSize; - // default palette list styles - const defaultListStyles = css({ - '&.ss__facet-palette-options--list': { - display: 'block', - }, - '.ss__facet-palette-options__option': { - padding: `${hasCheckbox ? 0 : '2px'} 0 0 ${listPadding}px`, - '&:last-child': { - marginBottom: 0, - }, - '.ss__checkbox, .ss__radio': { - left: 0, - }, - '.ss__facet-palette-options__option__wrapper': { - left: hasCheckbox ? `${listCheckboxSize + custom.spacing.x2}px` : 0, - }, - }, - }); - - // default palette list horizontal styles - const defaultListHorizontalStyles = css({ - '&.ss__facet-palette-options--list': { - display: 'flex', - flexFlow: 'row wrap', - margin: `0 -${custom.spacing.x2}px`, - }, - '.ss__facet-palette-options__option': { - flex: '0 1 auto', - minWidth: '1px', - padding: `${hasCheckbox ? 0 : '2px'} ${custom.spacing.x2}px 0 ${listPadding + custom.spacing.x2}px`, - boxSizing: 'border-box', - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - '.ss__checkbox, .ss__radio': { - left: `${custom.spacing.x2}px`, - backgroundColor: custom.colors.white, - }, - '.ss__facet-palette-options__option__wrapper': { - left: hasCheckbox ? `${listCheckboxSize + custom.spacing.x4}px` : `${custom.spacing.x2}px`, - }, - }, - }); - // list palette styles const listStyles = css([ sharedStyles, - isHorizontal ? defaultListHorizontalStyles : defaultListStyles, { + '&.ss__facet-palette-options--list': { + display: 'block', + }, '.ss__facet-palette-options__option': { position: 'relative', + padding: `${hasCheckbox ? 0 : '2px'} 0 0 ${listPadding}px`, margin: `0 0 ${custom.spacing.x1}px 0`, minHeight: hasCheckbox ? '' : `${listSize + 2}px`, + '&:last-child': { + marginBottom: 0, + }, '.ss__checkbox, .ss__radio, .ss__facet-palette-options__option__wrapper': { position: 'absolute', top: `${hasCheckbox ? 2 : 0.5}px`, }, + '.ss__checkbox, .ss__radio': { + left: 0, + }, '.ss__facet-palette-options__option__wrapper': { + left: hasCheckbox ? `${listCheckboxSize + custom.spacing.x2}px` : 0, width: `${listSize}px`, height: `${listSize}px`, lineHeight: `${listSize}px`, @@ -246,7 +211,7 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal hideIcon: true, gridSize: '52px', gapSize: `${custom.spacing.x1}px`, - layout: 'grid', + layout: 'list', colorMapping: { brown: { background: custom.colors.brown, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts index 94518739b..73ed84a26 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radio.ts @@ -7,9 +7,27 @@ import { custom } from '../../custom'; const radioStyleScript = (props: RadioProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const isSecondary = props?.className?.includes('secondary') ? true : false; const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); // shared radio styles + const sharedDefaultStyles = css({ + border: `1px solid ${custom.colors.gray02}`, + '&, & .ss__icon': { + borderRadius: '50%', + }, + '.ss__icon': { + display: 'none', + }, + '&.ss__radio--active': { + borderColor: darkGray, + '.ss__icon': { + display: 'block', + fill: variables?.colors?.primary, + stroke: variables?.colors?.primary, + }, + }, + }); const disabledStyles = css({ '&.ss__radio--disabled': { opacity: 0.65, @@ -19,30 +37,24 @@ const radioStyleScript = (props: RadioProps) => { }, }); - // default styles + // default radio styles const defaultStyles = css([ + sharedDefaultStyles, { backgroundColor: custom.colors.gray01, - border: `1px solid ${custom.colors.gray02}`, - '&, & .ss__icon': { - borderRadius: '50%', - }, - '.ss__icon': { - display: 'none', - }, - '&.ss__radio--active': { - borderColor: darkGray, - '.ss__icon': { - display: 'block', - fill: variables?.colors?.primary, - stroke: variables?.colors?.primary, - }, - }, }, disabledStyles, ]); - // native styles + // secondary radio styles + const secondaryStyles = css([ + sharedDefaultStyles, + { + backgroundColor: custom.colors.white, + }, + ]); + + // native radio styles const nativeStyles = css([ { lineHeight: 0, @@ -56,7 +68,14 @@ const radioStyleScript = (props: RadioProps) => { disabledStyles, ]); - return props?.native ? nativeStyles : defaultStyles; + // return radio styles + if (props?.native) { + return nativeStyles; + } else if (isSecondary) { + return secondaryStyles; + } else { + return defaultStyles; + } }; // Radio component props diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index 3ef6eaaa3..c55bbdeb5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -7,7 +7,7 @@ import { custom } from '../../custom'; const searchInputStyleScript = (props: SearchInputProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const isHorizontal = props?.className?.includes('horizontal') ? true : false; + const isSecondary = props?.className?.includes('secondary') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); // shared search input styles @@ -55,8 +55,8 @@ const searchInputStyleScript = (props: SearchInputProps) => { }, ]); - // horizontal search input styles - const horizontalStyles = css([ + // secondary search input styles + const secondaryStyles = css([ sharedStyles, { '&.ss__search-input': { @@ -65,7 +65,7 @@ const searchInputStyleScript = (props: SearchInputProps) => { }, ]); - return isHorizontal ? horizontalStyles : defaultStyles; + return isSecondary ? secondaryStyles : defaultStyles; }; // SearchInput component props diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts index 70f520c04..4aef88cd8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/facetsHorizontal.ts @@ -4,13 +4,12 @@ import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; // CSS in JS style script for the Facets component -const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { +const facetssecondaryStylescript = (props: FacetsHorizontalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; const mobileBp = variables?.breakpoints?.mobile || 767; const tabletBp = variables?.breakpoints?.tablet || 1024; - const columnsPaletteSelector = `.ss__facet-palette-options--list.ss__facet-palette-options--horizontal`; - const columnsSelector = `.ss__facet-hierarchy-options--horizontal, .ss__facet-list-options--horizontal, ${columnsPaletteSelector}`; + const columnsSelector = `.ss__facet-hierarchy-options, .ss__facet-list-options, .ss__facet-palette-options.ss__facet-palette-options--list`; return css({ margin: 0, @@ -111,13 +110,24 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { }, }, [columnsSelector]: { + display: 'flex', + flexFlow: 'row wrap', + gap: `0 ${custom.spacing.x2}px`, '& > *': { - width: `${100 / 4}%`, + flex: '0 1 auto', + width: `${100 / 4 - 2}%`, + minWidth: '1px', + boxSizing: 'border-box', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', }, }, - [columnsPaletteSelector]: { - '& > *': { - maxWidth: `24%`, + '.ss__facet-hierarchy-options': { + '.ss__facet-hierarchy-options__option.ss__facet-hierarchy-options__option--filtered': { + '& ~ .ss__facet-hierarchy-options__option:not(.ss__facet-hierarchy-options__option--filtered)': { + paddingLeft: 0, + }, }, }, '.ss__facet__show-more-less': { @@ -144,12 +154,7 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { '.ss__facets-horizontal__header__dropdown .ss__dropdown__content': { [columnsSelector]: { '& > *': { - width: `${100 / 3}%`, - }, - }, - [columnsPaletteSelector]: { - '& > *': { - maxWidth: `32%`, + width: `${100 / 3 - 2}%`, }, }, }, @@ -163,12 +168,7 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { '.ss__facets-horizontal__header__dropdown .ss__dropdown__content': { [columnsSelector]: { '& > *': { - width: `${100 / 2}%`, - }, - }, - [columnsPaletteSelector]: { - '& > *': { - maxWidth: `48%`, + width: `${100 / 2 - 2}%`, }, }, }, @@ -181,43 +181,41 @@ const facetsHorizontalStyleScript = (props: FacetsHorizontalProps) => { export const facetsHorizontal: ThemeComponent<'facetsHorizontal', FacetsHorizontalProps> = { default: { facetsHorizontal: { - themeStyleScript: facetsHorizontalStyleScript, + themeStyleScript: facetssecondaryStylescript, iconExpand: custom.icons.arrowDown, iconCollapse: custom.icons.arrowDown, }, 'facetsHorizontal dropdown button icon': { size: `${custom.sizes.icon12}px`, }, - 'facetsHorizontal facetGridOptions': { - className: 'ss__facet-grid-options--horizontal', - gridSize: '62px', + 'facetsHorizontal checkbox': { + className: 'ss__secondary', }, - 'facetsHorizontal mobileSidebar facetGridOptions': { + 'facetsHorizontal mobileSidebar checkbox': { className: '', - gridSize: '52px', }, - 'facetsHorizontal facetHierarchyOptions': { - className: 'ss__facet-hierarchy-options--horizontal', + 'facetsHorizontal radio': { + className: 'ss__secondary', }, - 'facetsHorizontal mobileSidebar facetHierarchyOptions': { + 'facetsHorizontal mobileSidebar radio': { className: '', }, - 'facetsHorizontal facetListOptions': { - className: 'ss__facet-list-options--horizontal', + 'facetsHorizontal facetGridOptions': { + className: 'ss__secondary', + gridSize: '62px', }, - 'facetsHorizontal mobileSidebar facetListOptions': { + 'facetsHorizontal mobileSidebar facetGridOptions': { className: '', + gridSize: '52px', }, 'facetsHorizontal facetPaletteOptions': { - className: 'ss__facet-palette-options--horizontal', gridSize: '62px', }, 'facetsHorizontal mobileSidebar facetPaletteOptions': { - className: '', gridSize: '52px', }, 'facetsHorizontal searchInput': { - className: 'ss__search-input--horizontal', + className: 'ss__secondary', }, 'facetsHorizontal mobileSidebar searchInput': { className: '', diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts index b82402eb4..f1b6f419d 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/searchHorizontal.ts @@ -5,7 +5,7 @@ import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; // CSS in JS style script for the Search component -const searchHorizontalStyleScript = (props: SearchHorizontalProps) => { +const searchsecondaryStylescript = (props: SearchHorizontalProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; @@ -17,7 +17,7 @@ export const searchHorizontal: ThemeComponent<'searchHorizontal', SearchHorizont ...searchHorizontalThemeComponentProps.default, searchHorizontal: { ...(searchHorizontalThemeComponentProps.default?.['searchHorizontal'] || {}), - themeStyleScript: searchHorizontalStyleScript, + themeStyleScript: searchsecondaryStylescript, }, 'searchHorizontal button.sidebar-toggle': { icon: custom.icons.filter, From a2f751f0b266f651ecced651a17a3499ac40124e Mon Sep 17 00:00:00 2001 From: adria Date: Mon, 4 Aug 2025 15:05:29 -0600 Subject: [PATCH 031/118] autocomplete desktop --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../molecules/facetPaletteOptions.ts | 2 +- .../pike/components/organisms/autocomplete.ts | 204 +++++++++++++++- .../pike/components/organisms/toolbar.ts | 40 ++- .../templates/autocompleteTemplate.ts | 231 +++++++++++++++++- 5 files changed, 449 insertions(+), 30 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index d737de1d3..49ca25bb3 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -65,7 +65,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchHorizontal', + component: 'SearchSnappy', }, ], // settings: { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 3897b3b83..d5879d0cf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -211,7 +211,7 @@ export const facetPaletteOptions: ThemeComponent<'facetPaletteOptions', FacetPal hideIcon: true, gridSize: '52px', gapSize: `${custom.spacing.x1}px`, - layout: 'list', + layout: 'grid', colorMapping: { brown: { background: custom.colors.brown, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts index 705075205..802b9aa90 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts @@ -1,13 +1,182 @@ import { css } from '@emotion/react'; import type { AutocompleteProps } from '../../../../components/Organisms/Autocomplete'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Autocomplete component const autocompleteStyleScript = (props: AutocompleteProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + border: `1px solid ${custom.colors.gray02}`, + backgroundColor: custom.colors.white, + width: props?.width, + right: 0, + left: 'auto', + top: 'auto', + margin: `${custom.spacing.x1}px 0 0 0`, + gap: `${custom.spacing.x4}px`, + 'a, div, p': { + fontSize: custom.utils.convertPxToEm(12), + lineHeight: 1.5, + color: variables?.colors?.text, + }, + a: { + display: 'block', + }, + '.ss__banner': { + img: { + maxWidth: '100%', + maxHeight: '150px', + height: 'auto', + }, + }, + '& > div': { + minWidth: '1px', + maxWidth: 'none', + flex: '0 1 auto', + padding: `${custom.spacing.x4}px 0`, + '&:first-child': { + paddingLeft: `${custom.spacing.x4}px`, + }, + '&:last-child': { + paddingRight: `${custom.spacing.x4}px`, + }, + }, + '.ss__autocomplete__terms .ss__autocomplete__title h5, .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__content__info a, .ss__no-results__recommendations h3': + { + margin: `0 0 ${custom.spacing.x4}px 0`, + fontSize: custom.utils.convertPxToEm(16), + fontWeight: custom.fonts.weight02, + lineHeight: 1.2, + color: variables?.colors?.secondary, + }, + '.ss__autocomplete__terms .ss__autocomplete__terms__options .ss__autocomplete__terms__option--active a, .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__content__info a:hover': + { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, + '.ss__autocomplete__terms': { + width: '200px', + backgroundColor: custom.colors.gray01, + padding: 0, + textAlign: 'left', + '& > div:first-child .ss__autocomplete__title': { + marginTop: `${custom.spacing.x2}px`, + }, + '& > div:last-child .ss__autocomplete__terms__options': { + marginBottom: `${custom.spacing.x2}px`, + }, + '& > div': { + '.ss__autocomplete__title': { + h5: { + margin: 0, + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + }, + }, + '.ss__autocomplete__terms__options': { + '.ss__autocomplete__terms__option': { + a: { + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + fontSize: custom.utils.convertPxToEm(14), + }, + }, + '.ss__autocomplete__terms__option--active': { + backgroundColor: custom.colors.white, + }, + }, + }, + }, + '.ss__autocomplete__facets': { + width: '200px', + padding: 0, + textAlign: 'left', + '.ss__facets': { + '.ss__facet': { + margin: `0 0 ${custom.spacing.x4}px 0`, + '&.ss__facet--showing-all': { + '.ss__facet__options': { + maxHeight: 'none', + overflow: 'visible', + padding: 0, + }, + }, + '&:last-child': { + marginBottom: 0, + }, + '.ss__facet__header': { + borderBottom: 0, + }, + '.ss__facet__options': { + '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { + padding: 0, + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-child': { + marginBottom: 0, + }, + }, + '.ss__facet-list-options': { + '.ss__facet-list-options__option': {}, + }, + }, + }, + }, + }, + '.ss__autocomplete__content': { + flex: '1 1 0%', + overflow: 'visible', + justifyContent: 'flex-start', + }, + '.ss__autocomplete__content__results': { + margin: `0 0 ${custom.spacing.x4}px 0`, + '.ss__results': { + overflowY: 'auto', + overflowX: 'hidden', + maxHeight: '75vh', + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + '.ss__result': { + '.ss__result__details': { + gap: `${custom.spacing.x1}px`, + }, + }, + '.ss__inline-banner': { + maxHeight: '250px', + overflow: 'hidden', + }, + }, + }, + '.ss__autocomplete__content__info': { + padding: 0, + a: { + margin: 0, + }, + }, + '.ss__autocomplete__content__no-results': { + '[ss-lang="noResultsText"]': { + p: { + display: 'inline', + margin: 0, + padding: 0, + '& ~ p': { + paddingLeft: '4px', + }, + }, + }, + '.ss__no-results__recommendations': { + margin: `${custom.spacing.x4}px 0 0 0`, + }, + }, + }); }; // Autocomplete component props @@ -15,6 +184,39 @@ export const autocomplete: ThemeComponent<'autocomplete', AutocompleteProps> = { default: { autocomplete: { themeStyleScript: autocompleteStyleScript, + width: '900px', + }, + 'autocomplete facet': { + limit: 5, + disableOverflow: true, + disableCollapse: true, + }, + 'autocomplete facets': { + limit: 3, + }, + 'autocomplete facetListOptions': { + hideCheckbox: true, + }, + 'autocomplete facetPaletteOptions': { + gridSize: '38px', + hideLabel: false, + }, + 'autocomplete facetGridOptions': { + gridSize: '38px', + }, + 'autocomplete results': { + rows: 2, + columns: 3, + gapSize: `${custom.spacing.x4}px`, + }, + 'autocomplete recommendationGrid': { + rows: 2, + columns: 4, + gapSize: `${custom.spacing.x4}px`, + }, + 'autocomplete icon': { + icon: custom.icons.arrowRight, + size: `${custom.sizes.icon12}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts index 905b35876..300a9dfbf 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/toolbar.ts @@ -9,39 +9,27 @@ const toolbarStyleScript = (props: ToolbarProps) => { const variables = props?.theme?.variables; const mobileBp = variables?.breakpoints?.mobile || 767; - // shared toolbar styles - const sharedStyles = css({ + return css({ + margin: `0 0 ${custom.spacing.x4}px 0`, + '&[class*="bottom"]': { + margin: `${custom.spacing.x4}px 0 0 0`, + '.ss__pagination-info': { + fontSize: custom.utils.convertPxToEm(14), + }, + }, + '.ss__pagination-info': { + fontSize: custom.utils.convertPxToEm(16), + }, '.ss__layout': { gap: `${custom.spacing.x2}px`, margin: 0, }, - }); - - // default toolbar styles - const defaultStyles = css([ - sharedStyles, - { - margin: `0 0 ${custom.spacing.x4}px 0`, + [`@media (max-width: ${mobileBp}px)`]: { '.ss__pagination-info': { - fontSize: custom.utils.convertPxToEm(16), + fontSize: custom.utils.convertPxToEm(18), }, - [`@media (max-width: ${mobileBp}px)`]: { - '.ss__pagination-info': { - fontSize: custom.utils.convertPxToEm(18), - }, - }, - }, - ]); - - // bottom toolbar styles - const bottomStyles = css([ - sharedStyles, - { - margin: `${custom.spacing.x4}px 0 0 0`, }, - ]); - - return props?.name == 'bottom' ? bottomStyles : defaultStyles; + }); }; // Toolbar component props diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts index 2c1e10086..bb1f9c8de 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts @@ -2,13 +2,207 @@ import { css } from '@emotion/react'; import type { AutocompleteTemplateProps } from '../../../../components/Templates/AutocompleteTemplate'; import { autocompleteThemeComponentProps } from '../../../themeComponents/autocompleteTemplate'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + border: `1px solid ${custom.colors.gray02}`, + backgroundColor: custom.colors.white, + width: props?.width, + right: 0, + left: 'auto', + top: 'auto', + margin: `${custom.spacing.x1}px 0 0 0`, + 'a, div, p': { + fontSize: custom.utils.convertPxToEm(12), + lineHeight: 1.5, + color: variables?.colors?.text, + }, + a: { + display: 'block', + }, + 'ul, ul li': { + padding: 0, + margin: 0, + listStyle: 'none', + }, + '.ss__banner': { + img: { + maxWidth: '100%', + maxHeight: '150px', + height: 'auto', + }, + }, + '& > .ss__autocomplete__row': { + gap: `${custom.spacing.x4}px`, + '.ss__autocomplete__column': { + alignContent: 'flex-start', + minWidth: '1px', + padding: `${custom.spacing.x4}px 0`, + '&:first-child': { + paddingLeft: `${custom.spacing.x4}px`, + }, + '&:last-child': { + paddingRight: `${custom.spacing.x4}px`, + }, + '&:has(.ss__autocomplete__terms-wrapper)': { + padding: 0, + }, + }, + }, + '.ss__terms .ss__terms__title h5, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a, .ss__no-results__recommendations h3': + { + margin: `0 0 ${custom.spacing.x4}px 0`, + padding: 0, + fontSize: custom.utils.convertPxToEm(16), + fontWeight: custom.fonts.weight02, + lineHeight: 1.2, + color: variables?.colors?.secondary, + }, + '.ss__terms .ss__terms__options .ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a:hover': + { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, + '.ss__autocomplete__terms-wrapper': { + backgroundColor: custom.colors.gray01, + }, + '.ss__terms-list': { + '.ss__terms-list__row': { + '&:first-child .ss__terms .ss__terms__title': { + marginTop: `${custom.spacing.x2}px`, + }, + '&:last-child .ss__terms .ss__terms__options': { + marginBottom: `${custom.spacing.x2}px`, + }, + }, + }, + '.ss__terms': { + width: '100%', + textAlign: 'left', + '.ss__terms__title': { + h5: { + margin: 0, + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + }, + }, + '.ss__terms__options': { + display: 'block', + margin: 0, + '.ss__terms__option': { + a: { + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + fontSize: custom.utils.convertPxToEm(14), + }, + }, + '.ss__terms__option--active': { + backgroundColor: custom.colors.white, + }, + }, + }, + '.ss__autocomplete__facets': { + padding: 0, + textAlign: 'left', + '.ss__facets': { + '.ss__facet': { + margin: `0 0 ${custom.spacing.x4}px 0`, + '&.ss__facet--showing-all': { + '.ss__facet__options': { + maxHeight: 'none', + overflow: 'visible', + padding: 0, + }, + }, + '&:last-child': { + marginBottom: 0, + }, + '.ss__facet__header': { + borderBottom: 0, + }, + '.ss__facet__options': { + '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { + padding: 0, + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-child': { + marginBottom: 0, + }, + }, + '.ss__facet-list-options': { + '.ss__facet-list-options__option': {}, + }, + }, + }, + }, + }, + '.ss__autocomplete__content': { + overflow: 'visible', + justifyContent: 'flex-start', + margin: `0 0 ${custom.spacing.x4}px 0`, + '.ss__autocomplete__content-inner': { + padding: 0, + }, + }, + '.ss__autocomplete__content__results': { + '.ss__results': { + overflowY: 'auto', + overflowX: 'hidden', + maxHeight: '75vh', + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + '.ss__result': { + '.ss__result__details': { + gap: `${custom.spacing.x1}px`, + }, + }, + '.ss__inline-banner': { + maxHeight: '250px', + overflow: 'hidden', + }, + }, + }, + '.ss__autocomplete__button--see-more': { + padding: 0, + height: 'auto', + '&, &:hover': { + backgroundColor: 'transparent', + border: 0, + }, + '.ss__button__content': { + '.ss__autocomplete__see-more': { + a: { + margin: 0, + }, + }, + }, + }, + '.ss__autocomplete__content__no-results': { + '[ss-lang="noResultsText"]': { + p: { + display: 'inline', + margin: 0, + padding: 0, + '& ~ p': { + paddingLeft: '4px', + }, + }, + }, + '.ss__no-results__recommendations': { + margin: `${custom.spacing.x4}px 0 0 0`, + }, + }, + }); }; // AutocompleteTemplate component props come from Template export @@ -18,6 +212,41 @@ export const autocompleteTemplate: ThemeComponent<'autocompleteTemplate', Autoco autocompleteTemplate: { ...(autocompleteThemeComponentProps.default?.['autocompleteTemplate'] || {}), themeStyleScript: autocompleteTemplateStyleScript, + width: '900px', + contentTitle: 'Product Suggestions', + column1: { + width: '200px', + layout: ['termsList'], + }, + column2: { + width: '160px', + layout: ['facets'], + }, + }, + 'autocompleteTemplate termsList': { + retainHistory: true, + retainTrending: true, + }, + 'autocompleteTemplate facetPaletteOptions': { + gridSize: '38px', + hideLabel: false, + }, + 'autocompleteTemplate facetGridOptions': { + gridSize: '38px', + }, + 'autocompleteTemplate results': { + rows: 2, + columns: 3, + gapSize: `${custom.spacing.x4}px`, + }, + 'autocompleteTemplate recommendationGrid': { + rows: 2, + columns: 4, + gapSize: `${custom.spacing.x4}px`, + }, + 'autocompleteTemplate icon': { + icon: custom.icons.arrowRight, + size: `${custom.sizes.icon12}px`, }, }, mobile: autocompleteThemeComponentProps.mobile, From 44f71a685760f7bca8cc382e80d2197d30d7db7b Mon Sep 17 00:00:00 2001 From: adria Date: Mon, 4 Aug 2025 18:06:32 -0600 Subject: [PATCH 032/118] kinda finish autocomplete --- .../pike/components/organisms/autocomplete.ts | 396 ++++++++++++------ .../pike/components/organisms/results.ts | 14 +- .../templates/autocompleteTemplate.ts | 208 ++++++++- 3 files changed, 471 insertions(+), 147 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts index 802b9aa90..24540447e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts @@ -7,174 +7,283 @@ import { custom } from '../../custom'; const autocompleteStyleScript = (props: AutocompleteProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const tabletBp = variables?.breakpoints?.tablet || 1024; + const textSelectors = 'a, div, p'; + const headerSelectors = + '.ss__autocomplete__terms .ss__autocomplete__title h5, .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__content__info a, .ss__no-results__recommendations h3'; + const activeSelectors = + '.ss__autocomplete__terms .ss__autocomplete__terms__options .ss__autocomplete__terms__option--active a, .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__content__info a:hover'; return css({ - border: `1px solid ${custom.colors.gray02}`, - backgroundColor: custom.colors.white, - width: props?.width, - right: 0, - left: 'auto', - top: 'auto', - margin: `${custom.spacing.x1}px 0 0 0`, - gap: `${custom.spacing.x4}px`, - 'a, div, p': { - fontSize: custom.utils.convertPxToEm(12), - lineHeight: 1.5, - color: variables?.colors?.text, - }, - a: { - display: 'block', - }, - '.ss__banner': { - img: { - maxWidth: '100%', - maxHeight: '150px', - height: 'auto', + '&.ss__autocomplete': { + border: `1px solid ${custom.colors.gray02}`, + backgroundColor: custom.colors.white, + width: props?.width, + right: 0, + left: 'auto', + top: 'auto', + margin: `${custom.spacing.x1}px 0 0 0`, + gap: `${custom.spacing.x4}px`, + [textSelectors]: { + fontSize: custom.utils.convertPxToEm(12), + lineHeight: 1.5, + color: variables?.colors?.text, }, - }, - '& > div': { - minWidth: '1px', - maxWidth: 'none', - flex: '0 1 auto', - padding: `${custom.spacing.x4}px 0`, - '&:first-child': { - paddingLeft: `${custom.spacing.x4}px`, + a: { + display: 'block', }, - '&:last-child': { - paddingRight: `${custom.spacing.x4}px`, + '.ss__banner': { + img: { + maxWidth: '100%', + maxHeight: '150px', + height: 'auto', + }, }, - }, - '.ss__autocomplete__terms .ss__autocomplete__title h5, .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__content__info a, .ss__no-results__recommendations h3': - { + [headerSelectors]: { margin: `0 0 ${custom.spacing.x4}px 0`, fontSize: custom.utils.convertPxToEm(16), fontWeight: custom.fonts.weight02, lineHeight: 1.2, color: variables?.colors?.secondary, }, - '.ss__autocomplete__terms .ss__autocomplete__terms__options .ss__autocomplete__terms__option--active a, .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__content__info a:hover': - { + [activeSelectors]: { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, }, - '.ss__autocomplete__terms': { - width: '200px', - backgroundColor: custom.colors.gray01, - padding: 0, - textAlign: 'left', - '& > div:first-child .ss__autocomplete__title': { - marginTop: `${custom.spacing.x2}px`, - }, - '& > div:last-child .ss__autocomplete__terms__options': { - marginBottom: `${custom.spacing.x2}px`, - }, '& > div': { - '.ss__autocomplete__title': { - h5: { - margin: 0, - padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, - }, + minWidth: '1px', + maxWidth: 'none', + flex: '0 1 auto', + padding: `${custom.spacing.x4}px 0`, + order: 0, + '&:first-of-type': { + paddingLeft: `${custom.spacing.x4}px`, + }, + '&:last-of-type': { + paddingRight: `${custom.spacing.x4}px`, + }, + '&.ss__autocomplete__terms': { + padding: 0, + }, + }, + '.ss__autocomplete__terms': { + width: '200px', + backgroundColor: custom.colors.gray01, + textAlign: 'left', + '& > div:first-child .ss__autocomplete__title': { + marginTop: `${custom.spacing.x2}px`, }, - '.ss__autocomplete__terms__options': { - '.ss__autocomplete__terms__option': { - a: { + '& > div:last-child .ss__autocomplete__terms__options': { + marginBottom: `${custom.spacing.x2}px`, + }, + '& > div': { + '.ss__autocomplete__title': { + padding: 0, + h5: { + margin: 0, padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, - fontSize: custom.utils.convertPxToEm(14), }, }, - '.ss__autocomplete__terms__option--active': { - backgroundColor: custom.colors.white, + '.ss__autocomplete__terms__options': { + '.ss__autocomplete__terms__option': { + a: { + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + fontSize: custom.utils.convertPxToEm(14), + }, + }, + '.ss__autocomplete__terms__option--active': { + backgroundColor: custom.colors.white, + }, }, }, }, - }, - '.ss__autocomplete__facets': { - width: '200px', - padding: 0, - textAlign: 'left', - '.ss__facets': { - '.ss__facet': { - margin: `0 0 ${custom.spacing.x4}px 0`, - '&.ss__facet--showing-all': { + '.ss__autocomplete__facets': { + width: '200px', + textAlign: 'left', + '.ss__facets': { + '.ss__facet': { + margin: `0 0 ${custom.spacing.x4}px 0`, + '&.ss__facet--showing-all': { + '.ss__facet__options': { + maxHeight: 'none', + overflow: 'visible', + padding: 0, + }, + }, + '&:last-child': { + marginBottom: 0, + }, + '.ss__facet__header': { + borderBottom: 0, + padding: 0, + }, '.ss__facet__options': { + margin: 0, maxHeight: 'none', overflow: 'visible', - padding: 0, + '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { + padding: 0, + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-child': { + marginBottom: 0, + }, + }, + '.ss__facet-list-options': { + '.ss__facet-list-options__option': {}, + }, }, }, - '&:last-child': { - marginBottom: 0, + }, + }, + '.ss__autocomplete__content': { + flex: '1 1 0%', + overflow: 'visible', + justifyContent: 'flex-start', + }, + '.ss__autocomplete__content__results': { + margin: `0 0 ${custom.spacing.x4}px 0`, + '.ss__results': { + overflowY: 'auto', + overflowX: 'hidden', + maxHeight: '75vh', + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', }, - '.ss__facet__header': { - borderBottom: 0, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, }, - '.ss__facet__options': { - '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { - padding: 0, - margin: `0 0 ${custom.spacing.x1}px 0`, - '&:last-child': { - marginBottom: 0, - }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + '.ss__result': { + '.ss__result__details': { + gap: `${custom.spacing.x1}px`, }, - '.ss__facet-list-options': { - '.ss__facet-list-options__option': {}, + }, + '.ss__inline-banner': { + maxHeight: '250px', + overflow: 'hidden', + }, + }, + }, + '.ss__autocomplete__content__info': { + padding: 0, + a: { + margin: 0, + '.ss__icon': { + fill: 'currentColor', + stroke: 'currentColor', + }, + }, + }, + '.ss__autocomplete__content__no-results': { + '[ss-lang="noResultsText"]': { + p: { + display: 'inline', + margin: 0, + padding: 0, + '& ~ p': { + paddingLeft: '4px', }, }, }, + '.ss__no-results__recommendations': { + margin: `${custom.spacing.x4}px 0 0 0`, + }, }, }, - '.ss__autocomplete__content': { - flex: '1 1 0%', - overflow: 'visible', - justifyContent: 'flex-start', - }, - '.ss__autocomplete__content__results': { - margin: `0 0 ${custom.spacing.x4}px 0`, - '.ss__results': { - overflowY: 'auto', - overflowX: 'hidden', - maxHeight: '75vh', - '&::-webkit-scrollbar': { - width: '8px', - height: '8px', + [`@media (max-width: ${tabletBp}px)`]: { + '&.ss__autocomplete': { + flexFlow: 'row wrap', + gap: 0, + width: props?.width, + left: 0, + right: 0, + [headerSelectors]: { + fontSize: custom.utils.convertPxToEm(14), + }, + '& > div': { + flex: '1 1 100%', + borderBottom: `1px solid ${custom.colors.gray02}`, + '&:last-child': { + borderBottom: 0, + }, + '&, &.ss__autocomplete__terms': { + padding: `${custom.spacing.x4}px`, + }, }, - '&::-webkit-scrollbar-track': { - backgroundColor: custom.colors.gray01, + '.ss__autocomplete__terms': { + backgroundColor: 'transparent', + display: 'flex', + flexFlow: 'row nowrap', + gap: `${custom.spacing.x4}px`, + width: 'auto', + '& > div': { + minWidth: '1px', + flex: '1 1 0%', + '&:first-child .ss__autocomplete__title': { + marginTop: 0, + }, + '&:last-child .ss__autocomplete__terms__options': { + marginBottom: 0, + }, + '.ss__autocomplete__title h5': { + padding: 0, + margin: `0 0 ${custom.spacing.x4}px 0`, + }, + '.ss__autocomplete__terms__options': { + gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, + flexFlow: 'row wrap', + justifyContent: 'flex-start', + '.ss__autocomplete__terms__option': { + flex: '0 1 auto', + a: { + padding: 0, + fontSize: custom.utils.convertPxToEm(12), + }, + }, + }, + }, + }, + '.ss__autocomplete__terms > div .ss__autocomplete__terms__options, .ss__autocomplete__facets .ss__facets': { + display: 'flex', }, - '&::-webkit-scrollbar-thumb': { - backgroundColor: custom.colors.gray02, + '.ss__autocomplete__terms > div .ss__autocomplete__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { + minWidth: '1px', }, - '.ss__result': { - '.ss__result__details': { - gap: `${custom.spacing.x1}px`, + '.ss__autocomplete__facets': { + width: 'auto', + '.ss__facets': { + gap: `0 ${custom.spacing.x4}px`, + flexFlow: 'row nowrap', + '.ss__facet': { + flex: '1 1 0%', + '&, &:last-child': { + margin: 0, + }, + }, }, }, - '.ss__inline-banner': { - maxHeight: '250px', - overflow: 'hidden', + '.ss__autocomplete__content__info': { + a: { + '.ss__icon': { + position: 'relative', + top: '1px', + }, + }, }, }, }, - '.ss__autocomplete__content__info': { - padding: 0, - a: { - margin: 0, - }, - }, - '.ss__autocomplete__content__no-results': { - '[ss-lang="noResultsText"]': { - p: { - display: 'inline', - margin: 0, - padding: 0, - '& ~ p': { - paddingLeft: '4px', + '@media (max-width: 540px)': { + '&.ss__autocomplete': { + '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { + gridTemplateColumns: `repeat(2, 1fr)`, + '& > div:nth-child(n+3)': { + display: 'none', }, }, }, - '.ss__no-results__recommendations': { - margin: `${custom.spacing.x4}px 0 0 0`, - }, }, }); }; @@ -219,4 +328,47 @@ export const autocomplete: ThemeComponent<'autocomplete', AutocompleteProps> = { size: `${custom.sizes.icon12}px`, }, }, + mobile: { + autocomplete: { + width: '100%', + }, + 'autocomplete results': { + rows: 1, + columns: 3, + gapSize: `${custom.spacing.x2}px`, + }, + 'autocomplete recommendationGrid': { + rows: 1, + columns: 3, + gapSize: `${custom.spacing.x2}px`, + }, + }, + tablet: { + autocomplete: { + width: '100%', + }, + 'autocomplete results': { + rows: 1, + columns: 4, + gapSize: `${custom.spacing.x4}px`, + }, + 'autocomplete recommendationGrid': { + rows: 1, + columns: 4, + gapSize: `${custom.spacing.x4}px`, + }, + }, + desktop: { + autocomplete: {}, + 'autocomplete results': { + rows: 2, + columns: 3, + gapSize: `${custom.spacing.x4}px`, + }, + 'autocomplete recommendationGrid': { + rows: 2, + columns: 4, + gapSize: `${custom.spacing.x4}px`, + }, + }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts index 490511722..07393df90 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts @@ -7,14 +7,8 @@ import { custom } from '../../custom'; const resultsStyleScript = (props: ResultsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const mobileBp = variables?.breakpoints?.mobile || 767; - return css({ - gap: `${custom.spacing.x6}px ${custom.spacing.x4}px`, - [`@media (max-width: ${mobileBp}px)`]: { - gap: `${custom.spacing.x6}px ${custom.spacing.x2}px`, - }, - }); + return css({}); }; // Results component props @@ -22,6 +16,12 @@ export const results: ThemeComponent<'results', ResultsProps> = { default: { results: { themeStyleScript: resultsStyleScript, + gapSize: `${custom.spacing.x6}px ${custom.spacing.x4}px`, + }, + }, + mobile: { + results: { + gapSize: `${custom.spacing.x6}px ${custom.spacing.x2}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts index bb1f9c8de..9a6c397ed 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts @@ -8,6 +8,12 @@ import { custom } from '../../custom'; const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const tabletBp = variables?.breakpoints?.tablet || 1024; + const textSelectors = 'a, div, p'; + const headerSelectors = + '.ss__terms .ss__terms__title h5, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a, .ss__no-results__recommendations h3'; + const activeSelectors = + '.ss__terms .ss__terms__options .ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a:hover'; return css({ border: `1px solid ${custom.colors.gray02}`, @@ -17,7 +23,7 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { left: 'auto', top: 'auto', margin: `${custom.spacing.x1}px 0 0 0`, - 'a, div, p': { + [textSelectors]: { fontSize: custom.utils.convertPxToEm(12), lineHeight: 1.5, color: variables?.colors?.text, @@ -37,6 +43,18 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { height: 'auto', }, }, + [headerSelectors]: { + margin: `0 0 ${custom.spacing.x4}px 0`, + padding: 0, + fontSize: custom.utils.convertPxToEm(16), + fontWeight: custom.fonts.weight02, + lineHeight: 1.2, + color: variables?.colors?.secondary, + }, + [activeSelectors]: { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, '& > .ss__autocomplete__row': { gap: `${custom.spacing.x4}px`, '.ss__autocomplete__column': { @@ -54,24 +72,12 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { }, }, }, - '.ss__terms .ss__terms__title h5, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a, .ss__no-results__recommendations h3': - { - margin: `0 0 ${custom.spacing.x4}px 0`, - padding: 0, - fontSize: custom.utils.convertPxToEm(16), - fontWeight: custom.fonts.weight02, - lineHeight: 1.2, - color: variables?.colors?.secondary, - }, - '.ss__terms .ss__terms__options .ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a:hover': - { - fontWeight: custom.fonts.weight01, - color: variables?.colors?.primary, - }, '.ss__autocomplete__terms-wrapper': { backgroundColor: custom.colors.gray01, + height: '100%', }, '.ss__terms-list': { + backgroundColor: 'transparent', '.ss__terms-list__row': { '&:first-child .ss__terms .ss__terms__title': { marginTop: `${custom.spacing.x2}px`, @@ -174,6 +180,7 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { }, '.ss__autocomplete__button--see-more': { padding: 0, + width: '100%', height: 'auto', '&, &:hover': { backgroundColor: 'transparent', @@ -202,6 +209,98 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { margin: `${custom.spacing.x4}px 0 0 0`, }, }, + [`@media (max-width: ${tabletBp}px)`]: { + width: props?.width, + left: 0, + right: 0, + [headerSelectors]: { + fontSize: custom.utils.convertPxToEm(14), + }, + '& > .ss__autocomplete__row': { + flexFlow: 'row wrap', + gap: 0, + '.ss__autocomplete__column': { + borderBottom: `1px solid ${custom.colors.gray02}`, + '&:last-child': { + borderBottom: 0, + }, + '&, &:has(.ss__autocomplete__terms-wrapper)': { + padding: `${custom.spacing.x4}px`, + }, + }, + }, + '.ss__autocomplete__terms-wrapper': { + backgroundColor: 'transparent', + }, + '.ss__terms-list': { + flexFlow: 'row nowrap', + gap: `${custom.spacing.x4}px`, + '.ss__terms-list__row': { + flex: '1 1 0%', + '&:first-child .ss__terms .ss__terms__title': { + marginTop: 0, + }, + '&:last-child .ss__terms .ss__terms__options': { + marginBottom: 0, + }, + }, + }, + '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { + display: 'flex', + }, + '.ss__terms .ss__terms__options .ss__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { + minWidth: '1px', + }, + '.ss__terms': { + '.ss__terms__title h5': { + padding: 0, + margin: `0 0 ${custom.spacing.x4}px 0`, + }, + '.ss__terms__options': { + gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, + flexFlow: 'row wrap', + '.ss__terms__option': { + flex: '0 1 auto', + a: { + padding: 0, + fontSize: custom.utils.convertPxToEm(12), + }, + }, + }, + }, + '.ss__autocomplete__facets': { + '.ss__facets': { + gap: `0 ${custom.spacing.x4}px`, + flexFlow: 'row nowrap', + '.ss__facet': { + flex: '1 1 0%', + '&, &:last-child': { + margin: 0, + }, + }, + }, + }, + '.ss__autocomplete__button--see-more': { + '.ss__button__content': { + '.ss__autocomplete__see-more': { + a: { + '.ss__icon': { + position: 'relative', + top: '1px', + }, + }, + }, + }, + }, + }, + '@media (max-width: 540px)': { + '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { + gridTemplateColumns: `repeat(2, 1fr)`, + '& > div:nth-child(n+3)': { + display: 'none', + }, + }, + }, }); }; @@ -222,6 +321,10 @@ export const autocompleteTemplate: ThemeComponent<'autocompleteTemplate', Autoco width: '160px', layout: ['facets'], }, + column3: { + width: 'auto', + layout: ['content', 'button.see-more'], + }, }, 'autocompleteTemplate termsList': { retainHistory: true, @@ -249,7 +352,76 @@ export const autocompleteTemplate: ThemeComponent<'autocompleteTemplate', Autoco size: `${custom.sizes.icon12}px`, }, }, - mobile: autocompleteThemeComponentProps.mobile, - desktop: autocompleteThemeComponentProps.desktop, - tablet: autocompleteThemeComponentProps.tablet, + mobile: { + ...autocompleteThemeComponentProps.mobile, + autocompleteTemplate: { + ...(autocompleteThemeComponentProps.mobile?.['autocompleteTemplate'] || {}), + width: '100%', + layout: [['c1', 'c2']], + column1: { + width: '100%', + layout: ['termsList'], + }, + column2: { + width: '100%', + layout: ['content', 'button.see-more'], + }, + }, + 'autocompleteTemplate results': { + rows: 1, + columns: 3, + gapSize: `${custom.spacing.x2}px`, + }, + 'autocompleteTemplate recommendationGrid': { + rows: 1, + columns: 3, + gapSize: `${custom.spacing.x2}px`, + }, + }, + tablet: { + ...autocompleteThemeComponentProps.tablet, + autocompleteTemplate: { + ...(autocompleteThemeComponentProps.tablet?.['autocompleteTemplate'] || {}), + width: '100%', + layout: [['c1', 'c2', 'c3']], + column1: { + width: '100%', + layout: ['termsList'], + }, + column2: { + width: '100%', + layout: ['facets'], + }, + column3: { + width: '100%', + layout: ['content', 'button.see-more'], + }, + }, + 'autocompleteTemplate results': { + rows: 1, + columns: 4, + gapSize: `${custom.spacing.x4}px`, + }, + 'autocompleteTemplate recommendationGrid': { + rows: 1, + columns: 4, + gapSize: `${custom.spacing.x4}px`, + }, + }, + desktop: { + ...autocompleteThemeComponentProps.desktop, + autocompleteTemplate: { + ...(autocompleteThemeComponentProps.desktop?.['autocompleteTemplate'] || {}), + }, + 'autocompleteTemplate results': { + rows: 2, + columns: 3, + gapSize: `${custom.spacing.x4}px`, + }, + 'autocompleteTemplate recommendationGrid': { + rows: 2, + columns: 4, + gapSize: `${custom.spacing.x4}px`, + }, + }, }; From b952de12b6a4807673d227345fbc4a1b138697e5 Mon Sep 17 00:00:00 2001 From: adria Date: Tue, 5 Aug 2025 12:02:41 -0600 Subject: [PATCH 033/118] autocomplete and update layout selector --- .../snap-preact-demo/templates/src/index.ts | 4 + .../components/molecules/layoutSelector.ts | 2 +- .../templates/autocompleteTemplate.ts | 371 ++++++++++++------ 3 files changed, 252 insertions(+), 125 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 49ca25bb3..f9a1eb8b2 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -34,6 +34,10 @@ let config: SnapTemplatesConfig = { style: globalStyles, overrides: { default: { + // 'autocompleteTemplate': { + // className: '', + // width: '200px', + // }, // 'toolbar.top': { // layout: ['breadcrumbs'] // } diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts index e4f495709..2e7409eca 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/layoutSelector.ts @@ -49,7 +49,7 @@ const layoutSelectorStyleScript = (props: LayoutSelectorProps) => { } else if (props?.type == 'list') { return listStyles; } else { - return css({}); + return dropdownStyles; } }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts index 9a6c397ed..dd688b7c8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts @@ -15,7 +15,19 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { const activeSelectors = '.ss__terms .ss__terms__options .ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a:hover'; - return css({ + // determine autocomplete layout and type + let acLayout = 'default'; + let acType = 'default'; + if (props?.className?.includes('slim')) { + acLayout = 'secondary'; + acType = 'slim'; + } else if (props?.className?.includes('terms')) { + acLayout = 'secondary'; + acType = 'terms'; + } + + // shared autocomplete styles + const sharedStyles = css({ border: `1px solid ${custom.colors.gray02}`, backgroundColor: custom.colors.white, width: props?.width, @@ -24,7 +36,7 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { top: 'auto', margin: `${custom.spacing.x1}px 0 0 0`, [textSelectors]: { - fontSize: custom.utils.convertPxToEm(12), + fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 12), lineHeight: 1.5, color: variables?.colors?.text, }, @@ -46,7 +58,7 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { [headerSelectors]: { margin: `0 0 ${custom.spacing.x4}px 0`, padding: 0, - fontSize: custom.utils.convertPxToEm(16), + fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 17 : 16), fontWeight: custom.fonts.weight02, lineHeight: 1.2, color: variables?.colors?.secondary, @@ -55,95 +67,29 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, }, - '& > .ss__autocomplete__row': { - gap: `${custom.spacing.x4}px`, - '.ss__autocomplete__column': { - alignContent: 'flex-start', - minWidth: '1px', - padding: `${custom.spacing.x4}px 0`, - '&:first-child': { - paddingLeft: `${custom.spacing.x4}px`, - }, - '&:last-child': { - paddingRight: `${custom.spacing.x4}px`, - }, - '&:has(.ss__autocomplete__terms-wrapper)': { - padding: 0, - }, + }); + const sharedFacetStyles = css({ + '&.ss__facet--showing-all': { + '.ss__facet__options': { + maxHeight: 'none', + overflow: 'visible', + padding: 0, }, }, - '.ss__autocomplete__terms-wrapper': { - backgroundColor: custom.colors.gray01, - height: '100%', + '.ss__facet__header': { + borderBottom: 0, }, - '.ss__terms-list': { - backgroundColor: 'transparent', - '.ss__terms-list__row': { - '&:first-child .ss__terms .ss__terms__title': { - marginTop: `${custom.spacing.x2}px`, - }, - '&:last-child .ss__terms .ss__terms__options': { - marginBottom: `${custom.spacing.x2}px`, - }, - }, - }, - '.ss__terms': { - width: '100%', - textAlign: 'left', - '.ss__terms__title': { - h5: { - margin: 0, - padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, - }, - }, - '.ss__terms__options': { - display: 'block', - margin: 0, - '.ss__terms__option': { - a: { - padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, - fontSize: custom.utils.convertPxToEm(14), - }, - }, - '.ss__terms__option--active': { - backgroundColor: custom.colors.white, - }, - }, - }, - '.ss__autocomplete__facets': { - padding: 0, - textAlign: 'left', - '.ss__facets': { - '.ss__facet': { - margin: `0 0 ${custom.spacing.x4}px 0`, - '&.ss__facet--showing-all': { - '.ss__facet__options': { - maxHeight: 'none', - overflow: 'visible', - padding: 0, - }, - }, - '&:last-child': { - marginBottom: 0, - }, - '.ss__facet__header': { - borderBottom: 0, - }, - '.ss__facet__options': { - '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { - padding: 0, - margin: `0 0 ${custom.spacing.x1}px 0`, - '&:last-child': { - marginBottom: 0, - }, - }, - '.ss__facet-list-options': { - '.ss__facet-list-options__option': {}, - }, - }, + '.ss__facet__options': { + '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { + padding: 0, + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-child': { + marginBottom: 0, }, }, }, + }); + const sharedContentStyles = css({ '.ss__autocomplete__content': { overflow: 'visible', justifyContent: 'flex-start', @@ -209,6 +155,8 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { margin: `${custom.spacing.x4}px 0 0 0`, }, }, + }); + const sharedTabletStyles = css({ [`@media (max-width: ${tabletBp}px)`]: { width: props?.width, left: 0, @@ -216,33 +164,213 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { [headerSelectors]: { fontSize: custom.utils.convertPxToEm(14), }, + '.ss__terms': { + '.ss__terms__options': { + '.ss__terms__option': { + a: { + fontSize: custom.utils.convertPxToEm(12), + }, + }, + }, + }, + '.ss__autocomplete__button--see-more': { + '.ss__button__content': { + '.ss__autocomplete__see-more': { + a: { + '.ss__icon': { + position: 'relative', + top: '1px', + }, + }, + }, + }, + }, + }, + }); + const sharedMobileStyles = css({ + '@media (max-width: 540px)': { + '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { + gridTemplateColumns: `repeat(2, 1fr)`, + '& > div:nth-child(n+3)': { + display: 'none', + }, + }, + }, + }); + + // default autocomplete styles + const defaultStyles = css([ + sharedStyles, + { + '& > .ss__autocomplete__row': { + gap: `${custom.spacing.x4}px`, + '.ss__autocomplete__column': { + alignContent: 'flex-start', + minWidth: '1px', + padding: `${custom.spacing.x4}px 0`, + '&:first-child': { + paddingLeft: `${custom.spacing.x4}px`, + }, + '&:last-child': { + paddingRight: `${custom.spacing.x4}px`, + }, + '&:has(.ss__autocomplete__terms-wrapper)': { + padding: 0, + }, + }, + }, + '.ss__autocomplete__terms-wrapper': { + backgroundColor: custom.colors.gray01, + height: '100%', + }, + '.ss__terms-list': { + backgroundColor: 'transparent', + '.ss__terms-list__row': { + '&:first-child .ss__terms .ss__terms__title': { + marginTop: `${custom.spacing.x2}px`, + }, + '&:last-child .ss__terms .ss__terms__options': { + marginBottom: `${custom.spacing.x2}px`, + }, + }, + }, + '.ss__terms': { + width: '100%', + textAlign: 'left', + '.ss__terms__title': { + h5: { + margin: 0, + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + }, + }, + '.ss__terms__options': { + display: 'block', + margin: 0, + '.ss__terms__option': { + a: { + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + fontSize: custom.utils.convertPxToEm(14), + }, + }, + '.ss__terms__option--active': { + backgroundColor: custom.colors.white, + }, + }, + }, + '.ss__autocomplete__facets': { + padding: 0, + textAlign: 'left', + '.ss__facets': { + '.ss__facet': { + margin: `0 0 ${custom.spacing.x4}px 0`, + '&:last-child': { + marginBottom: 0, + }, + ...sharedFacetStyles, + }, + }, + }, + [`@media (max-width: ${tabletBp}px)`]: { + '& > .ss__autocomplete__row': { + flexFlow: 'row wrap', + gap: 0, + '.ss__autocomplete__column': { + minWidth: '1px', + borderBottom: `1px solid ${custom.colors.gray02}`, + '&:last-child': { + borderBottom: 0, + }, + '&, &:has(.ss__autocomplete__terms-wrapper)': { + padding: `${custom.spacing.x4}px`, + }, + }, + }, + '.ss__autocomplete__terms-wrapper': { + backgroundColor: 'transparent', + }, + '.ss__terms-list': { + flexFlow: 'row nowrap', + gap: `${custom.spacing.x4}px`, + '.ss__terms-list__row': { + flex: '1 1 0%', + '&:first-child .ss__terms .ss__terms__title': { + marginTop: 0, + }, + '&:last-child .ss__terms .ss__terms__options': { + marginBottom: 0, + }, + }, + }, + '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { + display: 'flex', + }, + '.ss__terms .ss__terms__options .ss__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { + minWidth: '1px', + }, + '.ss__terms': { + '.ss__terms__title h5': { + padding: 0, + margin: `0 0 ${custom.spacing.x4}px 0`, + }, + '.ss__terms__options': { + gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, + flexFlow: 'row wrap', + '.ss__terms__option': { + flex: '0 1 auto', + a: { + padding: 0, + }, + }, + }, + }, + '.ss__autocomplete__facets': { + '.ss__facets': { + gap: `0 ${custom.spacing.x4}px`, + flexFlow: 'row nowrap', + '.ss__facet': { + flex: '1 1 0%', + '&, &:last-child': { + margin: 0, + }, + }, + }, + }, + }, + }, + sharedContentStyles, + sharedTabletStyles, + sharedMobileStyles, + ]); + + // secondary autocomplete styles + const secondaryStyles = css([ + sharedStyles, + { '& > .ss__autocomplete__row': { flexFlow: 'row wrap', gap: 0, '.ss__autocomplete__column': { + alignContent: 'flex-start', + flex: `1 1 100%`, + maxWidth: '100%', + minWidth: '1px', + padding: `${custom.spacing.x4}px`, borderBottom: `1px solid ${custom.colors.gray02}`, '&:last-child': { borderBottom: 0, }, - '&, &:has(.ss__autocomplete__terms-wrapper)': { - padding: `${custom.spacing.x4}px`, - }, }, }, '.ss__autocomplete__terms-wrapper': { - backgroundColor: 'transparent', + backgroundColor: custom.colors.white, + height: '100%', }, '.ss__terms-list': { flexFlow: 'row nowrap', gap: `${custom.spacing.x4}px`, + backgroundColor: 'transparent', '.ss__terms-list__row': { flex: '1 1 0%', - '&:first-child .ss__terms .ss__terms__title': { - marginTop: 0, - }, - '&:last-child .ss__terms .ss__terms__options': { - marginBottom: 0, - }, }, }, '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { @@ -252,56 +380,51 @@ const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { minWidth: '1px', }, '.ss__terms': { - '.ss__terms__title h5': { - padding: 0, - margin: `0 0 ${custom.spacing.x4}px 0`, + width: '100%', + textAlign: 'left', + '.ss__terms__title': { + h5: { + padding: 0, + }, }, '.ss__terms__options': { gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, flexFlow: 'row wrap', + margin: 0, '.ss__terms__option': { flex: '0 1 auto', a: { padding: 0, - fontSize: custom.utils.convertPxToEm(12), + fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 14), }, }, }, }, '.ss__autocomplete__facets': { + padding: 0, + textAlign: 'left', '.ss__facets': { gap: `0 ${custom.spacing.x4}px`, flexFlow: 'row nowrap', '.ss__facet': { flex: '1 1 0%', - '&, &:last-child': { - margin: 0, - }, - }, - }, - }, - '.ss__autocomplete__button--see-more': { - '.ss__button__content': { - '.ss__autocomplete__see-more': { - a: { - '.ss__icon': { - position: 'relative', - top: '1px', - }, - }, + margin: 0, + ...sharedFacetStyles, }, }, }, }, - '@media (max-width: 540px)': { - '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { - gridTemplateColumns: `repeat(2, 1fr)`, - '& > div:nth-child(n+3)': { - display: 'none', - }, - }, - }, - }); + sharedContentStyles, + sharedTabletStyles, + sharedMobileStyles, + ]); + + // return autocomplete styles + if (acLayout == 'secondary') { + return secondaryStyles; + } else { + return defaultStyles; + } }; // AutocompleteTemplate component props come from Template export From 40d9dc6cfcf63f279c17b2e5fcc38646133a4a52 Mon Sep 17 00:00:00 2001 From: adria Date: Tue, 5 Aug 2025 17:41:48 -0600 Subject: [PATCH 034/118] finish ac, start recommendations --- .../pike/components/molecules/carousel.ts | 10 +- .../components/templates/recommendation.ts | 105 +++++++- .../templates/recommendationBundle.ts | 235 +++++++++++++++++- 3 files changed, 335 insertions(+), 15 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts index 19102c068..8ff788ee0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/carousel.ts @@ -22,12 +22,12 @@ const carouselStyleScript = (props: CarouselProps) => { ...disabledStyles, }, '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { - width: '24px', - height: '24px', + width: '32px', + height: '32px', display: 'block', position: 'absolute', top: 0, - bottom: '33.33%', + bottom: '22%', zIndex: 2, margin: 'auto', '& > div': { @@ -110,11 +110,11 @@ export const carousel: ThemeComponent<'carousel', CarouselProps> = { }, 'carousel icon.prev': { icon: custom.icons.arrowLeft, - size: '8px', + size: `${custom.sizes.icon12}px`, }, 'carousel icon.next': { icon: custom.icons.arrowRight, - size: '8px', + size: `${custom.sizes.icon12}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts index c7c23594a..303b18a45 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts @@ -2,13 +2,79 @@ import { css } from '@emotion/react'; import type { RecommendationProps } from '../../../../components/Templates/Recommendation'; import { recommendationThemeComponentProps } from '../../../themeComponents/recommendation'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Recommendation component const recommendationStyleScript = (props: RecommendationProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const tabletBp = variables?.breakpoints?.tablet || 1024; + const mobileBp = variables?.breakpoints?.mobile || 767; + const arrowSizes = { + default: 32, + tablet: 28, + mobile: 24, + }; - return css({}); + return css({ + margin: `${custom.spacing.x8}px 0`, + '.ss__recommendation__title': { + fontSize: custom.utils.convertPxToEm(22), + fontWeight: custom.fonts.weight02, + color: variables?.colors?.secondary, + textAlign: 'center', + margin: `0 0 ${custom.spacing.x4}px 0`, + }, + '.ss__carousel': { + padding: `0 ${custom.spacing.x4 + arrowSizes.default}px`, + }, + [`@media (max-width: ${tabletBp}px)`]: { + '.ss__carousel': { + padding: `0 ${custom.spacing.x4 + arrowSizes.tablet}px`, + '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { + width: `${arrowSizes.tablet}px`, + height: `${arrowSizes.tablet}px`, + }, + }, + }, + [`@media (max-width: ${mobileBp}px)`]: { + position: 'relative', + '.ss__recommendation__title': { + textAlign: 'left', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + overflow: 'hidden', + paddingRight: `${arrowSizes.mobile * 2 + custom.spacing.x1 + custom.spacing.x4}px`, + }, + '.ss__carousel': { + padding: 0, + position: 'static', + '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { + top: '4.5px', + bottom: 'auto', + left: 'auto', + width: `${arrowSizes.mobile}px`, + height: `${arrowSizes.mobile}px`, + }, + '.ss__carousel__prev-wrapper': { + right: `${arrowSizes.mobile + custom.spacing.x1}px`, + }, + '.ss__carousel__next-wrapper': { + right: 0, + }, + }, + }, + '@media (max-width: 540px)': { + '.ss__recommendation__title': { + fontSize: custom.utils.convertPxToEm(18), + }, + '.ss__carousel': { + '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { + top: 0, + }, + }, + }, + }); }; // Recommendation component props come from Template export @@ -18,9 +84,40 @@ export const recommendation: ThemeComponent<'recommendation', RecommendationProp recommendation: { ...(recommendationThemeComponentProps.default?.['recommendation'] || {}), themeStyleScript: recommendationStyleScript, + spaceBetween: custom.spacing.x4, + }, + }, + mobile: { + ...recommendationThemeComponentProps.mobile, + recommendation: { + ...(recommendationThemeComponentProps.mobile?.['recommendation'] || {}), + spaceBetween: custom.spacing.x2, + }, + 'recommendation icon.prev': { + size: `${custom.sizes.icon08}px`, + }, + 'recommendation icon.next': { + size: `${custom.sizes.icon08}px`, + }, + }, + tablet: { + ...recommendationThemeComponentProps.tablet, + recommendation: { + ...(recommendationThemeComponentProps.tablet?.['recommendation'] || {}), + spaceBetween: custom.spacing.x4, + }, + 'recommendation icon.prev': { + size: `${custom.sizes.icon10}px`, + }, + 'recommendation icon.next': { + size: `${custom.sizes.icon10}px`, + }, + }, + desktop: { + ...recommendationThemeComponentProps.desktop, + recommendation: { + ...(recommendationThemeComponentProps.desktop?.['recommendation'] || {}), + spaceBetween: custom.spacing.x4, }, }, - mobile: recommendationThemeComponentProps.mobile, - desktop: recommendationThemeComponentProps.desktop, - tablet: recommendationThemeComponentProps.tablet, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts index f2afc2857..1458d5cd5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts @@ -8,8 +8,198 @@ import { custom } from '../../custom'; const recommendationBundleStyleScript = (props: RecommendationBundleProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); + const tabletBp = variables?.breakpoints?.tablet || 1024; + const mobileBp = variables?.breakpoints?.mobile || 767; - return css({}); + return css({ + margin: `${custom.spacing.x8}px 0`, + '.ss__recommendation-bundle__title': { + fontSize: custom.utils.convertPxToEm(22), + fontWeight: custom.fonts.weight02, + color: variables?.colors?.secondary, + margin: `0 0 ${custom.spacing.x4}px 0`, + }, + '.ss__recommendation-bundle__wrapper': { + flexFlow: `row nowrap`, + margin: `0 -${custom.spacing.x2}px`, + '& > *': { + flex: '0 1 auto', + minWidth: '1px', + padding: `0 ${custom.spacing.x2}px`, + boxSizing: 'border-box', + }, + '.ss__recommendation-bundle__wrapper__seed-container, .ss__recommendation-bundle__wrapper__cta': { + width: `20%`, + }, + '.ss__recommendation-bundle__wrapper__carousel': { + width: `60%`, + }, + }, + '.ss__recommendation-result-tracker, .ss__recommendation-bundle__wrapper__selector, .ss__recommendation-bundle__wrapper .ss__recommendation-bundle__wrapper__selector__result-wrapper': + { + height: '100%', + margin: 0, + }, + '.ss__recommendation-bundle__wrapper__seed-container': { + '.ss__recommendation-bundle__wrapper__selector__result-wrapper__seed-badge': { + top: '5px', + left: '5px', + backgroundColor: variables?.colors?.primary, + fontSize: custom.utils.convertPxToEm(10), + fontWeight: custom.fonts.weight01, + lineHeight: `18px`, + color: custom.colors.white, + padding: `0 ${custom.spacing.x2}px`, + }, + }, + '.ss__recommendation-bundle__wrapper__selector': { + width: 'auto !important', + }, + '.ss__recommendation-bundle__wrapper__selector__result-wrapper, .ss__carousel .swiper-container > .swiper-wrapper > .swiper-slide': { + '.ss__result': { + width: '100%', + flex: '1 1 0%', + }, + }, + '.ss__recommendation-bundle__wrapper__selector__result-wrapper': { + display: 'flex', + flexFlow: `column wrap`, + '&, .ss__result': { + position: 'relative', + }, + '&:has(.ss__overlay-badge)': { + '.ss__result': { + '.ss__overlay-badge .ss__overlay-badge__grid-wrapper': { + top: '30px', + }, + }, + }, + '.ss__checkbox': { + top: '5px', + right: '5px', + }, + }, + '.ss__icon--plus': { + display: 'none', + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + margin: 'auto 0', + fill: variables?.colors?.secondary, + stroke: variables?.colors?.secondary, + }, + '.ss__recommendation-bundle__wrapper__cta': { + position: 'relative', + paddingTop: `${custom.spacing.x4}px`, + paddingBottom: `${custom.spacing.x4}px`, + display: 'flex', + flexFlow: 'column nowrap', + justifyContent: 'center', + alignItems: 'center', + gap: `${custom.spacing.x4}px`, + '& > *': { + flex: '0 1 auto', + minWidth: '1px', + margin: `0 ${custom.spacing.x2}px 0 ${custom.spacing.x4}px`, + }, + '.ss__recommendation-bundle__wrapper__cta__subtotal, .ss__recommendation-bundle__wrapper__cta__button': { + position: 'relative', + zIndex: 2, + }, + '.ss__recommendation-bundle__wrapper__cta__subtotal': { + color: variables?.colors?.text, + '.icon': { + position: 'relative', + top: '-1.5px', + margin: `0 0 ${custom.spacing.x1}px 0`, + '.ss__icon': { + fill: variables?.colors?.secondary, + stroke: variables?.colors?.secondary, + }, + }, + '.ss__recommendation-bundle__wrapper__cta__subtotal__prices': { + margin: `${custom.spacing.x1}px 0 0 0`, + label: { + margin: 0, + padding: 0, + '& ~ label': { + paddingLeft: `${custom.spacing.x1}px`, + }, + }, + '.ss__recommendation-bundle__wrapper__cta__subtotal__strike': { + color: lightGray, + '*': { + color: 'inherit', + }, + }, + '.ss__recommendation-bundle__wrapper__cta__subtotal__price': { + fontSize: custom.utils.convertPxToEm(16), + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + '*': { + color: 'inherit', + }, + }, + }, + }, + '&:after': { + content: '""', + display: 'block', + backgroundColor: custom.colors.gray01, + border: `1px solid ${custom.colors.gray02}`, + boxSizing: 'border-box', + position: 'absolute', + top: 0, + left: '10px', + right: 0, + bottom: 0, + zIndex: 1, + margin: 'auto', + }, + }, + [`@media (max-width: ${tabletBp}px)`]: { + '.ss__recommendation-bundle__wrapper': { + '.ss__recommendation-bundle__wrapper__seed-container, .ss__recommendation-bundle__wrapper__cta': { + width: `25%`, + }, + '.ss__recommendation-bundle__wrapper__carousel': { + width: `50%`, + }, + }, + }, + [`@media (max-width: ${mobileBp}px)`]: { + '.ss__recommendation-bundle__wrapper': { + flexFlow: 'row wrap', + width: 'auto', + maxWidth: 'none', + margin: `0 -${custom.spacing.x1}px`, + '& > *': { + padding: `0 ${custom.spacing.x1}px`, + }, + '.ss__recommendation-bundle__wrapper__seed-container, .ss__recommendation-bundle__wrapper__carousel': { + width: `50%`, + }, + }, + '.ss__recommendation-bundle__wrapper__cta': { + width: 'auto', + margin: `${custom.spacing.x4}px 0 0 0`, + padding: `${custom.spacing.x4}px`, + '& > *': { + margin: 0, + }, + '&:after': { + left: 0, + }, + }, + }, + '@media (max-width: 540px)': { + '.ss__recommendation-bundle__title': { + fontSize: custom.utils.convertPxToEm(18), + }, + }, + }); }; // RecommendationBundle component props come from Template export @@ -18,12 +208,45 @@ export const recommendationBundle: ThemeComponent<'recommendationBundle', Recomm ...recommendationBundleThemeComponentProps.default, recommendationBundle: { ...(recommendationBundleThemeComponentProps.default?.['recommendationBundle'] || {}), - separatorIcon: custom.icons.plus, - ctaIcon: custom.icons.bag, themeStyleScript: recommendationBundleStyleScript, }, + 'recommendationBundle icon.bundle-cart': { + icon: custom.icons.bag, + size: `${custom.sizes.icon16 * 2}px`, + }, + 'recommendationBundle icon.bundle-selector': { + icon: custom.icons.plus, + size: `${custom.sizes.icon14}px`, + }, + 'recommendationBundle carousel': { + spaceBetween: custom.spacing.x4, + }, + }, + mobile: { + ...recommendationBundleThemeComponentProps.mobile, + recommendationBundle: { + ...(recommendationBundleThemeComponentProps.mobile?.['recommendationBundle'] || {}), + }, + 'recommendationBundle carousel': { + spaceBetween: 0, + }, + }, + tablet: { + ...recommendationBundleThemeComponentProps.tablet, + recommendationBundle: { + ...(recommendationBundleThemeComponentProps.tablet?.['recommendationBundle'] || {}), + }, + 'recommendationBundle carousel': { + spaceBetween: custom.spacing.x4, + }, + }, + desktop: { + ...recommendationBundleThemeComponentProps.desktop, + recommendationBundle: { + ...(recommendationBundleThemeComponentProps.desktop?.['recommendationBundle'] || {}), + }, + 'recommendationBundle carousel': { + spaceBetween: custom.spacing.x4, + }, }, - mobile: recommendationBundleThemeComponentProps.mobile, - desktop: recommendationBundleThemeComponentProps.desktop, - tablet: recommendationBundleThemeComponentProps.tablet, }; From 724cacbe68e3438b92c2a5978f679a8cf0ce3ab3 Mon Sep 17 00:00:00 2001 From: adria Date: Fri, 8 Aug 2025 17:41:19 -0600 Subject: [PATCH 035/118] update build and files --- .../snap-preact-demo/templates/src/index.ts | 5 +- .../components/templates/autocompleteFixed.ts | 22 + .../components/templates/autocompleteModal.ts | 22 + .../templates/autocompleteSlideout.ts | 22 + .../templates/autocompleteTemplate.ts | 1086 ++++++++--------- .../themes/pike/components/templates/index.ts | 11 +- 6 files changed, 618 insertions(+), 550 deletions(-) create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/autocompleteModal.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index c5df65cd3..0e1a3264a 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -39,7 +39,8 @@ let config: SnapTemplatesConfig = { // width: '200px', // }, // 'toolbar.top': { - // layout: ['breadcrumbs'] + // className: 'aaa', + // //layout: ['breadcrumbs'] // } // 'toolbar.bottom': { // layout: ['loadMore'] @@ -69,7 +70,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchSnappy', + component: 'SearchHorizontal', }, ], // settings: { diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts new file mode 100644 index 000000000..56e502bf8 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts @@ -0,0 +1,22 @@ +import { css } from '@emotion/react'; +import { autocompleteFixedThemeComponentProps } from '../../../themeComponents/autocompleteFixed'; +import { ThemeComponent } from '../../../../providers'; +import { AutocompleteFixedProps } from '../../../../components/Templates/AutocompleteFixed'; + +// CSS in JS style script for the Search component +const autocompleteFixedStyleScript = ({}: AutocompleteFixedProps) => { + return css({}); +}; + +export const autocompleteFixed: ThemeComponent<'autocompleteFixed', AutocompleteFixedProps> = { + default: { + ...autocompleteFixedThemeComponentProps.default, + autocompleteFixed: { + ...(autocompleteFixedThemeComponentProps.default?.['autocompleteFixed'] || {}), + themeStyleScript: autocompleteFixedStyleScript, + }, + }, + mobile: autocompleteFixedThemeComponentProps.mobile, + desktop: autocompleteFixedThemeComponentProps.desktop, + tablet: autocompleteFixedThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteModal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteModal.ts new file mode 100644 index 000000000..6f4c05dbc --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteModal.ts @@ -0,0 +1,22 @@ +import { css } from '@emotion/react'; +import { autocompleteModalThemeComponentProps } from '../../../themeComponents/autocompleteModal'; +import { ThemeComponent } from '../../../../providers'; +import { AutocompleteModalProps } from '../../../../components/Templates/AutocompleteModal'; + +// CSS in JS style script for the Search component +const autocompleteModalStyleScript = ({}: AutocompleteModalProps) => { + return css({}); +}; + +export const autocompleteModal: ThemeComponent<'autocompleteModal', AutocompleteModalProps> = { + default: { + ...autocompleteModalThemeComponentProps.default, + autocompleteModal: { + ...(autocompleteModalThemeComponentProps.default?.['autocompleteModal'] || {}), + themeStyleScript: autocompleteModalStyleScript, + }, + }, + mobile: autocompleteModalThemeComponentProps.mobile, + desktop: autocompleteModalThemeComponentProps.desktop, + tablet: autocompleteModalThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts new file mode 100644 index 000000000..fe5ed9f99 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts @@ -0,0 +1,22 @@ +import { css } from '@emotion/react'; +import { autocompleteSlideoutThemeComponentProps } from '../../../themeComponents/autocompleteSlideout'; +import { ThemeComponent } from '../../../../providers'; +import { AutocompleteSlideoutProps } from '../../../../components/Templates/AutocompleteSlideout'; + +// CSS in JS style script for the Search component +const autocompleteSlideoutStyleScript = ({}: AutocompleteSlideoutProps) => { + return css({}); +}; + +export const autocompleteSlideout: ThemeComponent<'autocompleteSlideout', AutocompleteSlideoutProps> = { + default: { + ...autocompleteSlideoutThemeComponentProps.default, + autocompleteSlideout: { + ...(autocompleteSlideoutThemeComponentProps.default?.['autocompleteSlideout'] || {}), + themeStyleScript: autocompleteSlideoutStyleScript, + }, + }, + mobile: autocompleteSlideoutThemeComponentProps.mobile, + desktop: autocompleteSlideoutThemeComponentProps.desktop, + tablet: autocompleteSlideoutThemeComponentProps.tablet, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts index dd688b7c8..0d385ae5b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts @@ -1,550 +1,550 @@ -import { css } from '@emotion/react'; -import type { AutocompleteTemplateProps } from '../../../../components/Templates/AutocompleteTemplate'; -import { autocompleteThemeComponentProps } from '../../../themeComponents/autocompleteTemplate'; -import { ThemeComponent } from '../../../../providers'; -import { custom } from '../../custom'; +// import { css } from '@emotion/react'; +// import type { AutocompleteTemplateProps } from '../../../../components/Templates/AutocompleteTemplate'; +// import { autocompleteThemeComponentProps } from '../../../themeComponents/autocompleteTemplate'; +// import { ThemeComponent } from '../../../../providers'; +// import { custom } from '../../custom'; -// CSS in JS style script for the Search component -const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const variables = props?.theme?.variables; - const tabletBp = variables?.breakpoints?.tablet || 1024; - const textSelectors = 'a, div, p'; - const headerSelectors = - '.ss__terms .ss__terms__title h5, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a, .ss__no-results__recommendations h3'; - const activeSelectors = - '.ss__terms .ss__terms__options .ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a:hover'; +// // CSS in JS style script for the Search component +// const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { +// // eslint-disable-next-line @typescript-eslint/no-unused-vars +// const variables = props?.theme?.variables; +// const tabletBp = variables?.breakpoints?.tablet || 1024; +// const textSelectors = 'a, div, p'; +// const headerSelectors = +// '.ss__terms .ss__terms__title h5, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a, .ss__no-results__recommendations h3'; +// const activeSelectors = +// '.ss__terms .ss__terms__options .ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a:hover'; - // determine autocomplete layout and type - let acLayout = 'default'; - let acType = 'default'; - if (props?.className?.includes('slim')) { - acLayout = 'secondary'; - acType = 'slim'; - } else if (props?.className?.includes('terms')) { - acLayout = 'secondary'; - acType = 'terms'; - } +// // determine autocomplete layout and type +// let acLayout = 'default'; +// let acType = 'default'; +// if (props?.className?.includes('slim')) { +// acLayout = 'secondary'; +// acType = 'slim'; +// } else if (props?.className?.includes('terms')) { +// acLayout = 'secondary'; +// acType = 'terms'; +// } - // shared autocomplete styles - const sharedStyles = css({ - border: `1px solid ${custom.colors.gray02}`, - backgroundColor: custom.colors.white, - width: props?.width, - right: 0, - left: 'auto', - top: 'auto', - margin: `${custom.spacing.x1}px 0 0 0`, - [textSelectors]: { - fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 12), - lineHeight: 1.5, - color: variables?.colors?.text, - }, - a: { - display: 'block', - }, - 'ul, ul li': { - padding: 0, - margin: 0, - listStyle: 'none', - }, - '.ss__banner': { - img: { - maxWidth: '100%', - maxHeight: '150px', - height: 'auto', - }, - }, - [headerSelectors]: { - margin: `0 0 ${custom.spacing.x4}px 0`, - padding: 0, - fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 17 : 16), - fontWeight: custom.fonts.weight02, - lineHeight: 1.2, - color: variables?.colors?.secondary, - }, - [activeSelectors]: { - fontWeight: custom.fonts.weight01, - color: variables?.colors?.primary, - }, - }); - const sharedFacetStyles = css({ - '&.ss__facet--showing-all': { - '.ss__facet__options': { - maxHeight: 'none', - overflow: 'visible', - padding: 0, - }, - }, - '.ss__facet__header': { - borderBottom: 0, - }, - '.ss__facet__options': { - '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { - padding: 0, - margin: `0 0 ${custom.spacing.x1}px 0`, - '&:last-child': { - marginBottom: 0, - }, - }, - }, - }); - const sharedContentStyles = css({ - '.ss__autocomplete__content': { - overflow: 'visible', - justifyContent: 'flex-start', - margin: `0 0 ${custom.spacing.x4}px 0`, - '.ss__autocomplete__content-inner': { - padding: 0, - }, - }, - '.ss__autocomplete__content__results': { - '.ss__results': { - overflowY: 'auto', - overflowX: 'hidden', - maxHeight: '75vh', - '&::-webkit-scrollbar': { - width: '8px', - height: '8px', - }, - '&::-webkit-scrollbar-track': { - backgroundColor: custom.colors.gray01, - }, - '&::-webkit-scrollbar-thumb': { - backgroundColor: custom.colors.gray02, - }, - '.ss__result': { - '.ss__result__details': { - gap: `${custom.spacing.x1}px`, - }, - }, - '.ss__inline-banner': { - maxHeight: '250px', - overflow: 'hidden', - }, - }, - }, - '.ss__autocomplete__button--see-more': { - padding: 0, - width: '100%', - height: 'auto', - '&, &:hover': { - backgroundColor: 'transparent', - border: 0, - }, - '.ss__button__content': { - '.ss__autocomplete__see-more': { - a: { - margin: 0, - }, - }, - }, - }, - '.ss__autocomplete__content__no-results': { - '[ss-lang="noResultsText"]': { - p: { - display: 'inline', - margin: 0, - padding: 0, - '& ~ p': { - paddingLeft: '4px', - }, - }, - }, - '.ss__no-results__recommendations': { - margin: `${custom.spacing.x4}px 0 0 0`, - }, - }, - }); - const sharedTabletStyles = css({ - [`@media (max-width: ${tabletBp}px)`]: { - width: props?.width, - left: 0, - right: 0, - [headerSelectors]: { - fontSize: custom.utils.convertPxToEm(14), - }, - '.ss__terms': { - '.ss__terms__options': { - '.ss__terms__option': { - a: { - fontSize: custom.utils.convertPxToEm(12), - }, - }, - }, - }, - '.ss__autocomplete__button--see-more': { - '.ss__button__content': { - '.ss__autocomplete__see-more': { - a: { - '.ss__icon': { - position: 'relative', - top: '1px', - }, - }, - }, - }, - }, - }, - }); - const sharedMobileStyles = css({ - '@media (max-width: 540px)': { - '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { - gridTemplateColumns: `repeat(2, 1fr)`, - '& > div:nth-child(n+3)': { - display: 'none', - }, - }, - }, - }); +// // shared autocomplete styles +// const sharedStyles = css({ +// border: `1px solid ${custom.colors.gray02}`, +// backgroundColor: custom.colors.white, +// width: props?.width, +// right: 0, +// left: 'auto', +// top: 'auto', +// margin: `${custom.spacing.x1}px 0 0 0`, +// [textSelectors]: { +// fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 12), +// lineHeight: 1.5, +// color: variables?.colors?.text, +// }, +// a: { +// display: 'block', +// }, +// 'ul, ul li': { +// padding: 0, +// margin: 0, +// listStyle: 'none', +// }, +// '.ss__banner': { +// img: { +// maxWidth: '100%', +// maxHeight: '150px', +// height: 'auto', +// }, +// }, +// [headerSelectors]: { +// margin: `0 0 ${custom.spacing.x4}px 0`, +// padding: 0, +// fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 17 : 16), +// fontWeight: custom.fonts.weight02, +// lineHeight: 1.2, +// color: variables?.colors?.secondary, +// }, +// [activeSelectors]: { +// fontWeight: custom.fonts.weight01, +// color: variables?.colors?.primary, +// }, +// }); +// const sharedFacetStyles = css({ +// '&.ss__facet--showing-all': { +// '.ss__facet__options': { +// maxHeight: 'none', +// overflow: 'visible', +// padding: 0, +// }, +// }, +// '.ss__facet__header': { +// borderBottom: 0, +// }, +// '.ss__facet__options': { +// '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { +// padding: 0, +// margin: `0 0 ${custom.spacing.x1}px 0`, +// '&:last-child': { +// marginBottom: 0, +// }, +// }, +// }, +// }); +// const sharedContentStyles = css({ +// '.ss__autocomplete__content': { +// overflow: 'visible', +// justifyContent: 'flex-start', +// margin: `0 0 ${custom.spacing.x4}px 0`, +// '.ss__autocomplete__content-inner': { +// padding: 0, +// }, +// }, +// '.ss__autocomplete__content__results': { +// '.ss__results': { +// overflowY: 'auto', +// overflowX: 'hidden', +// maxHeight: '75vh', +// '&::-webkit-scrollbar': { +// width: '8px', +// height: '8px', +// }, +// '&::-webkit-scrollbar-track': { +// backgroundColor: custom.colors.gray01, +// }, +// '&::-webkit-scrollbar-thumb': { +// backgroundColor: custom.colors.gray02, +// }, +// '.ss__result': { +// '.ss__result__details': { +// gap: `${custom.spacing.x1}px`, +// }, +// }, +// '.ss__inline-banner': { +// maxHeight: '250px', +// overflow: 'hidden', +// }, +// }, +// }, +// '.ss__autocomplete__button--see-more': { +// padding: 0, +// width: '100%', +// height: 'auto', +// '&, &:hover': { +// backgroundColor: 'transparent', +// border: 0, +// }, +// '.ss__button__content': { +// '.ss__autocomplete__see-more': { +// a: { +// margin: 0, +// }, +// }, +// }, +// }, +// '.ss__autocomplete__content__no-results': { +// '[ss-lang="noResultsText"]': { +// p: { +// display: 'inline', +// margin: 0, +// padding: 0, +// '& ~ p': { +// paddingLeft: '4px', +// }, +// }, +// }, +// '.ss__no-results__recommendations': { +// margin: `${custom.spacing.x4}px 0 0 0`, +// }, +// }, +// }); +// const sharedTabletStyles = css({ +// [`@media (max-width: ${tabletBp}px)`]: { +// width: props?.width, +// left: 0, +// right: 0, +// [headerSelectors]: { +// fontSize: custom.utils.convertPxToEm(14), +// }, +// '.ss__terms': { +// '.ss__terms__options': { +// '.ss__terms__option': { +// a: { +// fontSize: custom.utils.convertPxToEm(12), +// }, +// }, +// }, +// }, +// '.ss__autocomplete__button--see-more': { +// '.ss__button__content': { +// '.ss__autocomplete__see-more': { +// a: { +// '.ss__icon': { +// position: 'relative', +// top: '1px', +// }, +// }, +// }, +// }, +// }, +// }, +// }); +// const sharedMobileStyles = css({ +// '@media (max-width: 540px)': { +// '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { +// gridTemplateColumns: `repeat(2, 1fr)`, +// '& > div:nth-child(n+3)': { +// display: 'none', +// }, +// }, +// }, +// }); - // default autocomplete styles - const defaultStyles = css([ - sharedStyles, - { - '& > .ss__autocomplete__row': { - gap: `${custom.spacing.x4}px`, - '.ss__autocomplete__column': { - alignContent: 'flex-start', - minWidth: '1px', - padding: `${custom.spacing.x4}px 0`, - '&:first-child': { - paddingLeft: `${custom.spacing.x4}px`, - }, - '&:last-child': { - paddingRight: `${custom.spacing.x4}px`, - }, - '&:has(.ss__autocomplete__terms-wrapper)': { - padding: 0, - }, - }, - }, - '.ss__autocomplete__terms-wrapper': { - backgroundColor: custom.colors.gray01, - height: '100%', - }, - '.ss__terms-list': { - backgroundColor: 'transparent', - '.ss__terms-list__row': { - '&:first-child .ss__terms .ss__terms__title': { - marginTop: `${custom.spacing.x2}px`, - }, - '&:last-child .ss__terms .ss__terms__options': { - marginBottom: `${custom.spacing.x2}px`, - }, - }, - }, - '.ss__terms': { - width: '100%', - textAlign: 'left', - '.ss__terms__title': { - h5: { - margin: 0, - padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, - }, - }, - '.ss__terms__options': { - display: 'block', - margin: 0, - '.ss__terms__option': { - a: { - padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, - fontSize: custom.utils.convertPxToEm(14), - }, - }, - '.ss__terms__option--active': { - backgroundColor: custom.colors.white, - }, - }, - }, - '.ss__autocomplete__facets': { - padding: 0, - textAlign: 'left', - '.ss__facets': { - '.ss__facet': { - margin: `0 0 ${custom.spacing.x4}px 0`, - '&:last-child': { - marginBottom: 0, - }, - ...sharedFacetStyles, - }, - }, - }, - [`@media (max-width: ${tabletBp}px)`]: { - '& > .ss__autocomplete__row': { - flexFlow: 'row wrap', - gap: 0, - '.ss__autocomplete__column': { - minWidth: '1px', - borderBottom: `1px solid ${custom.colors.gray02}`, - '&:last-child': { - borderBottom: 0, - }, - '&, &:has(.ss__autocomplete__terms-wrapper)': { - padding: `${custom.spacing.x4}px`, - }, - }, - }, - '.ss__autocomplete__terms-wrapper': { - backgroundColor: 'transparent', - }, - '.ss__terms-list': { - flexFlow: 'row nowrap', - gap: `${custom.spacing.x4}px`, - '.ss__terms-list__row': { - flex: '1 1 0%', - '&:first-child .ss__terms .ss__terms__title': { - marginTop: 0, - }, - '&:last-child .ss__terms .ss__terms__options': { - marginBottom: 0, - }, - }, - }, - '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { - display: 'flex', - }, - '.ss__terms .ss__terms__options .ss__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { - minWidth: '1px', - }, - '.ss__terms': { - '.ss__terms__title h5': { - padding: 0, - margin: `0 0 ${custom.spacing.x4}px 0`, - }, - '.ss__terms__options': { - gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, - flexFlow: 'row wrap', - '.ss__terms__option': { - flex: '0 1 auto', - a: { - padding: 0, - }, - }, - }, - }, - '.ss__autocomplete__facets': { - '.ss__facets': { - gap: `0 ${custom.spacing.x4}px`, - flexFlow: 'row nowrap', - '.ss__facet': { - flex: '1 1 0%', - '&, &:last-child': { - margin: 0, - }, - }, - }, - }, - }, - }, - sharedContentStyles, - sharedTabletStyles, - sharedMobileStyles, - ]); +// // default autocomplete styles +// const defaultStyles = css([ +// sharedStyles, +// { +// '& > .ss__autocomplete__row': { +// gap: `${custom.spacing.x4}px`, +// '.ss__autocomplete__column': { +// alignContent: 'flex-start', +// minWidth: '1px', +// padding: `${custom.spacing.x4}px 0`, +// '&:first-child': { +// paddingLeft: `${custom.spacing.x4}px`, +// }, +// '&:last-child': { +// paddingRight: `${custom.spacing.x4}px`, +// }, +// '&:has(.ss__autocomplete__terms-wrapper)': { +// padding: 0, +// }, +// }, +// }, +// '.ss__autocomplete__terms-wrapper': { +// backgroundColor: custom.colors.gray01, +// height: '100%', +// }, +// '.ss__terms-list': { +// backgroundColor: 'transparent', +// '.ss__terms-list__row': { +// '&:first-child .ss__terms .ss__terms__title': { +// marginTop: `${custom.spacing.x2}px`, +// }, +// '&:last-child .ss__terms .ss__terms__options': { +// marginBottom: `${custom.spacing.x2}px`, +// }, +// }, +// }, +// '.ss__terms': { +// width: '100%', +// textAlign: 'left', +// '.ss__terms__title': { +// h5: { +// margin: 0, +// padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, +// }, +// }, +// '.ss__terms__options': { +// display: 'block', +// margin: 0, +// '.ss__terms__option': { +// a: { +// padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, +// fontSize: custom.utils.convertPxToEm(14), +// }, +// }, +// '.ss__terms__option--active': { +// backgroundColor: custom.colors.white, +// }, +// }, +// }, +// '.ss__autocomplete__facets': { +// padding: 0, +// textAlign: 'left', +// '.ss__facets': { +// '.ss__facet': { +// margin: `0 0 ${custom.spacing.x4}px 0`, +// '&:last-child': { +// marginBottom: 0, +// }, +// ...sharedFacetStyles, +// }, +// }, +// }, +// [`@media (max-width: ${tabletBp}px)`]: { +// '& > .ss__autocomplete__row': { +// flexFlow: 'row wrap', +// gap: 0, +// '.ss__autocomplete__column': { +// minWidth: '1px', +// borderBottom: `1px solid ${custom.colors.gray02}`, +// '&:last-child': { +// borderBottom: 0, +// }, +// '&, &:has(.ss__autocomplete__terms-wrapper)': { +// padding: `${custom.spacing.x4}px`, +// }, +// }, +// }, +// '.ss__autocomplete__terms-wrapper': { +// backgroundColor: 'transparent', +// }, +// '.ss__terms-list': { +// flexFlow: 'row nowrap', +// gap: `${custom.spacing.x4}px`, +// '.ss__terms-list__row': { +// flex: '1 1 0%', +// '&:first-child .ss__terms .ss__terms__title': { +// marginTop: 0, +// }, +// '&:last-child .ss__terms .ss__terms__options': { +// marginBottom: 0, +// }, +// }, +// }, +// '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { +// display: 'flex', +// }, +// '.ss__terms .ss__terms__options .ss__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { +// minWidth: '1px', +// }, +// '.ss__terms': { +// '.ss__terms__title h5': { +// padding: 0, +// margin: `0 0 ${custom.spacing.x4}px 0`, +// }, +// '.ss__terms__options': { +// gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, +// flexFlow: 'row wrap', +// '.ss__terms__option': { +// flex: '0 1 auto', +// a: { +// padding: 0, +// }, +// }, +// }, +// }, +// '.ss__autocomplete__facets': { +// '.ss__facets': { +// gap: `0 ${custom.spacing.x4}px`, +// flexFlow: 'row nowrap', +// '.ss__facet': { +// flex: '1 1 0%', +// '&, &:last-child': { +// margin: 0, +// }, +// }, +// }, +// }, +// }, +// }, +// sharedContentStyles, +// sharedTabletStyles, +// sharedMobileStyles, +// ]); - // secondary autocomplete styles - const secondaryStyles = css([ - sharedStyles, - { - '& > .ss__autocomplete__row': { - flexFlow: 'row wrap', - gap: 0, - '.ss__autocomplete__column': { - alignContent: 'flex-start', - flex: `1 1 100%`, - maxWidth: '100%', - minWidth: '1px', - padding: `${custom.spacing.x4}px`, - borderBottom: `1px solid ${custom.colors.gray02}`, - '&:last-child': { - borderBottom: 0, - }, - }, - }, - '.ss__autocomplete__terms-wrapper': { - backgroundColor: custom.colors.white, - height: '100%', - }, - '.ss__terms-list': { - flexFlow: 'row nowrap', - gap: `${custom.spacing.x4}px`, - backgroundColor: 'transparent', - '.ss__terms-list__row': { - flex: '1 1 0%', - }, - }, - '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { - display: 'flex', - }, - '.ss__terms .ss__terms__options .ss__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { - minWidth: '1px', - }, - '.ss__terms': { - width: '100%', - textAlign: 'left', - '.ss__terms__title': { - h5: { - padding: 0, - }, - }, - '.ss__terms__options': { - gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, - flexFlow: 'row wrap', - margin: 0, - '.ss__terms__option': { - flex: '0 1 auto', - a: { - padding: 0, - fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 14), - }, - }, - }, - }, - '.ss__autocomplete__facets': { - padding: 0, - textAlign: 'left', - '.ss__facets': { - gap: `0 ${custom.spacing.x4}px`, - flexFlow: 'row nowrap', - '.ss__facet': { - flex: '1 1 0%', - margin: 0, - ...sharedFacetStyles, - }, - }, - }, - }, - sharedContentStyles, - sharedTabletStyles, - sharedMobileStyles, - ]); +// // secondary autocomplete styles +// const secondaryStyles = css([ +// sharedStyles, +// { +// '& > .ss__autocomplete__row': { +// flexFlow: 'row wrap', +// gap: 0, +// '.ss__autocomplete__column': { +// alignContent: 'flex-start', +// flex: `1 1 100%`, +// maxWidth: '100%', +// minWidth: '1px', +// padding: `${custom.spacing.x4}px`, +// borderBottom: `1px solid ${custom.colors.gray02}`, +// '&:last-child': { +// borderBottom: 0, +// }, +// }, +// }, +// '.ss__autocomplete__terms-wrapper': { +// backgroundColor: custom.colors.white, +// height: '100%', +// }, +// '.ss__terms-list': { +// flexFlow: 'row nowrap', +// gap: `${custom.spacing.x4}px`, +// backgroundColor: 'transparent', +// '.ss__terms-list__row': { +// flex: '1 1 0%', +// }, +// }, +// '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { +// display: 'flex', +// }, +// '.ss__terms .ss__terms__options .ss__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { +// minWidth: '1px', +// }, +// '.ss__terms': { +// width: '100%', +// textAlign: 'left', +// '.ss__terms__title': { +// h5: { +// padding: 0, +// }, +// }, +// '.ss__terms__options': { +// gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, +// flexFlow: 'row wrap', +// margin: 0, +// '.ss__terms__option': { +// flex: '0 1 auto', +// a: { +// padding: 0, +// fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 14), +// }, +// }, +// }, +// }, +// '.ss__autocomplete__facets': { +// padding: 0, +// textAlign: 'left', +// '.ss__facets': { +// gap: `0 ${custom.spacing.x4}px`, +// flexFlow: 'row nowrap', +// '.ss__facet': { +// flex: '1 1 0%', +// margin: 0, +// ...sharedFacetStyles, +// }, +// }, +// }, +// }, +// sharedContentStyles, +// sharedTabletStyles, +// sharedMobileStyles, +// ]); - // return autocomplete styles - if (acLayout == 'secondary') { - return secondaryStyles; - } else { - return defaultStyles; - } -}; +// // return autocomplete styles +// if (acLayout == 'secondary') { +// return secondaryStyles; +// } else { +// return defaultStyles; +// } +// }; -// AutocompleteTemplate component props come from Template export -export const autocompleteTemplate: ThemeComponent<'autocompleteTemplate', AutocompleteTemplateProps> = { - default: { - ...autocompleteThemeComponentProps.default, - autocompleteTemplate: { - ...(autocompleteThemeComponentProps.default?.['autocompleteTemplate'] || {}), - themeStyleScript: autocompleteTemplateStyleScript, - width: '900px', - contentTitle: 'Product Suggestions', - column1: { - width: '200px', - layout: ['termsList'], - }, - column2: { - width: '160px', - layout: ['facets'], - }, - column3: { - width: 'auto', - layout: ['content', 'button.see-more'], - }, - }, - 'autocompleteTemplate termsList': { - retainHistory: true, - retainTrending: true, - }, - 'autocompleteTemplate facetPaletteOptions': { - gridSize: '38px', - hideLabel: false, - }, - 'autocompleteTemplate facetGridOptions': { - gridSize: '38px', - }, - 'autocompleteTemplate results': { - rows: 2, - columns: 3, - gapSize: `${custom.spacing.x4}px`, - }, - 'autocompleteTemplate recommendationGrid': { - rows: 2, - columns: 4, - gapSize: `${custom.spacing.x4}px`, - }, - 'autocompleteTemplate icon': { - icon: custom.icons.arrowRight, - size: `${custom.sizes.icon12}px`, - }, - }, - mobile: { - ...autocompleteThemeComponentProps.mobile, - autocompleteTemplate: { - ...(autocompleteThemeComponentProps.mobile?.['autocompleteTemplate'] || {}), - width: '100%', - layout: [['c1', 'c2']], - column1: { - width: '100%', - layout: ['termsList'], - }, - column2: { - width: '100%', - layout: ['content', 'button.see-more'], - }, - }, - 'autocompleteTemplate results': { - rows: 1, - columns: 3, - gapSize: `${custom.spacing.x2}px`, - }, - 'autocompleteTemplate recommendationGrid': { - rows: 1, - columns: 3, - gapSize: `${custom.spacing.x2}px`, - }, - }, - tablet: { - ...autocompleteThemeComponentProps.tablet, - autocompleteTemplate: { - ...(autocompleteThemeComponentProps.tablet?.['autocompleteTemplate'] || {}), - width: '100%', - layout: [['c1', 'c2', 'c3']], - column1: { - width: '100%', - layout: ['termsList'], - }, - column2: { - width: '100%', - layout: ['facets'], - }, - column3: { - width: '100%', - layout: ['content', 'button.see-more'], - }, - }, - 'autocompleteTemplate results': { - rows: 1, - columns: 4, - gapSize: `${custom.spacing.x4}px`, - }, - 'autocompleteTemplate recommendationGrid': { - rows: 1, - columns: 4, - gapSize: `${custom.spacing.x4}px`, - }, - }, - desktop: { - ...autocompleteThemeComponentProps.desktop, - autocompleteTemplate: { - ...(autocompleteThemeComponentProps.desktop?.['autocompleteTemplate'] || {}), - }, - 'autocompleteTemplate results': { - rows: 2, - columns: 3, - gapSize: `${custom.spacing.x4}px`, - }, - 'autocompleteTemplate recommendationGrid': { - rows: 2, - columns: 4, - gapSize: `${custom.spacing.x4}px`, - }, - }, -}; +// // AutocompleteTemplate component props come from Template export +// export const autocompleteTemplate: ThemeComponent<'autocompleteTemplate', AutocompleteTemplateProps> = { +// default: { +// ...autocompleteThemeComponentProps.default, +// autocompleteTemplate: { +// ...(autocompleteThemeComponentProps.default?.['autocompleteTemplate'] || {}), +// themeStyleScript: autocompleteTemplateStyleScript, +// width: '900px', +// contentTitle: 'Product Suggestions', +// column1: { +// width: '200px', +// layout: ['termsList'], +// }, +// column2: { +// width: '160px', +// layout: ['facets'], +// }, +// column3: { +// width: 'auto', +// layout: ['content', 'button.see-more'], +// }, +// }, +// 'autocompleteTemplate termsList': { +// retainHistory: true, +// retainTrending: true, +// }, +// 'autocompleteTemplate facetPaletteOptions': { +// gridSize: '38px', +// hideLabel: false, +// }, +// 'autocompleteTemplate facetGridOptions': { +// gridSize: '38px', +// }, +// 'autocompleteTemplate results': { +// rows: 2, +// columns: 3, +// gapSize: `${custom.spacing.x4}px`, +// }, +// 'autocompleteTemplate recommendationGrid': { +// rows: 2, +// columns: 4, +// gapSize: `${custom.spacing.x4}px`, +// }, +// 'autocompleteTemplate icon': { +// icon: custom.icons.arrowRight, +// size: `${custom.sizes.icon12}px`, +// }, +// }, +// mobile: { +// ...autocompleteThemeComponentProps.mobile, +// autocompleteTemplate: { +// ...(autocompleteThemeComponentProps.mobile?.['autocompleteTemplate'] || {}), +// width: '100%', +// layout: [['c1', 'c2']], +// column1: { +// width: '100%', +// layout: ['termsList'], +// }, +// column2: { +// width: '100%', +// layout: ['content', 'button.see-more'], +// }, +// }, +// 'autocompleteTemplate results': { +// rows: 1, +// columns: 3, +// gapSize: `${custom.spacing.x2}px`, +// }, +// 'autocompleteTemplate recommendationGrid': { +// rows: 1, +// columns: 3, +// gapSize: `${custom.spacing.x2}px`, +// }, +// }, +// tablet: { +// ...autocompleteThemeComponentProps.tablet, +// autocompleteTemplate: { +// ...(autocompleteThemeComponentProps.tablet?.['autocompleteTemplate'] || {}), +// width: '100%', +// layout: [['c1', 'c2', 'c3']], +// column1: { +// width: '100%', +// layout: ['termsList'], +// }, +// column2: { +// width: '100%', +// layout: ['facets'], +// }, +// column3: { +// width: '100%', +// layout: ['content', 'button.see-more'], +// }, +// }, +// 'autocompleteTemplate results': { +// rows: 1, +// columns: 4, +// gapSize: `${custom.spacing.x4}px`, +// }, +// 'autocompleteTemplate recommendationGrid': { +// rows: 1, +// columns: 4, +// gapSize: `${custom.spacing.x4}px`, +// }, +// }, +// desktop: { +// ...autocompleteThemeComponentProps.desktop, +// autocompleteTemplate: { +// ...(autocompleteThemeComponentProps.desktop?.['autocompleteTemplate'] || {}), +// }, +// 'autocompleteTemplate results': { +// rows: 2, +// columns: 3, +// gapSize: `${custom.spacing.x4}px`, +// }, +// 'autocompleteTemplate recommendationGrid': { +// rows: 2, +// columns: 4, +// gapSize: `${custom.spacing.x4}px`, +// }, +// }, +// }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/index.ts b/packages/snap-preact/components/src/themes/pike/components/templates/index.ts index c1b0ec424..f5c5df677 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/index.ts @@ -1,7 +1,8 @@ import { ThemeResponsiveComplete } from '../../../../providers'; // TEMPLATES -import { autocompleteTemplate } from './autocompleteTemplate'; +//import { autocompleteTemplate } from './autocompleteTemplate'; +import { autocompleteFixed } from './autocompleteFixed'; import { recommendation } from './recommendation'; import { recommendationBundle } from './recommendationBundle'; import { recommendationBundleEasyAdd } from './recommendationBundleEasyAdd'; @@ -17,7 +18,7 @@ import { searchSnappy } from './searchSnappy'; export const templates: ThemeResponsiveComplete = { default: { - ...autocompleteTemplate.default, + ...autocompleteFixed.default, ...recommendation.default, ...recommendationBundle.default, ...recommendationBundleEasyAdd.default, @@ -32,7 +33,7 @@ export const templates: ThemeResponsiveComplete = { ...searchHorizontal.default, }, mobile: { - ...autocompleteTemplate.mobile, + //...autocompleteTemplate.mobile, ...recommendation.mobile, ...recommendationBundle.mobile, ...recommendationBundleEasyAdd.mobile, @@ -47,7 +48,7 @@ export const templates: ThemeResponsiveComplete = { ...searchHorizontal.mobile, }, tablet: { - ...autocompleteTemplate.tablet, + //...autocompleteTemplate.tablet, ...recommendation.tablet, ...recommendationBundle.tablet, ...recommendationBundleEasyAdd.tablet, @@ -62,7 +63,7 @@ export const templates: ThemeResponsiveComplete = { ...searchHorizontal.tablet, }, desktop: { - ...autocompleteTemplate.desktop, + //...autocompleteTemplate.desktop, ...recommendation.desktop, ...recommendationBundle.desktop, ...recommendationBundleEasyAdd.desktop, From 895bdb5df0be6941f2955af942158bc5ecb35e98 Mon Sep 17 00:00:00 2001 From: adria Date: Sun, 10 Aug 2025 17:40:06 -0600 Subject: [PATCH 036/118] Mostly autocomplete layout work --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../pike/components/atoms/breadcrumbs.ts | 2 +- .../themes/pike/components/atoms/button.ts | 13 +- .../themes/pike/components/atoms/dropdown.ts | 2 +- .../themes/pike/components/atoms/overlay.ts | 1 + .../molecules/facetHierarchyOptions.ts | 2 +- .../components/molecules/facetListOptions.ts | 2 +- .../molecules/facetPaletteOptions.ts | 2 +- .../themes/pike/components/molecules/index.ts | 5 + .../themes/pike/components/molecules/list.ts | 2 +- .../themes/pike/components/molecules/modal.ts | 20 + .../pike/components/molecules/radioList.ts | 2 +- .../pike/components/molecules/searchInput.ts | 55 +- .../pike/components/molecules/select.ts | 2 +- .../pike/components/organisms/autocomplete.ts | 44 +- .../organisms/autocompleteLayout.ts | 463 +++++++++++++++ .../components/organisms/filterSummary.ts | 2 +- .../themes/pike/components/organisms/index.ts | 5 + .../components/organisms/mobileSidebar.ts | 2 +- .../pike/components/organisms/noResults.ts | 2 +- .../pike/components/organisms/results.ts | 8 + .../pike/components/organisms/sidebar.ts | 2 +- .../pike/components/organisms/termsList.ts | 66 ++- .../components/templates/autocompleteFixed.ts | 124 +++- .../templates/autocompleteTemplate.ts | 550 ------------------ .../themes/pike/components/templates/index.ts | 17 +- .../components/templates/recommendation.ts | 3 - .../templates/recommendationBundle.ts | 9 - .../templates/recommendationGrid.ts | 39 +- .../components/src/themes/pike/custom.ts | 1 + 30 files changed, 825 insertions(+), 624 deletions(-) create mode 100644 packages/snap-preact/components/src/themes/pike/components/molecules/modal.ts create mode 100644 packages/snap-preact/components/src/themes/pike/components/organisms/autocompleteLayout.ts delete mode 100644 packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 0e1a3264a..3d76a5849 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -70,7 +70,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: '#searchspring-layout', - component: 'SearchHorizontal', + component: 'SearchSnappy', }, ], // settings: { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts index b75a87cda..38864b310 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/breadcrumbs.ts @@ -19,7 +19,7 @@ const breadcrumbsStyleScript = (props: BreadcrumbsProps) => { }, '.ss__breadcrumbs__crumbs__crumb': { padding: `0 ${custom.spacing.x1}px`, - '&:last-child': { + '&:last-of-type': { color: variables?.colors?.primary, fontWeight: custom?.fonts?.weight01, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts index c54099aef..92eb6f082 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/button.ts @@ -8,15 +8,14 @@ import Color from 'color'; const buttonStyleScript = (props: ButtonProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const buttonDisabledSelectors = '&.ss__button--disabled'; + const buttonSelector = `&, &:hover, &:not(.ss__button--disabled):hover, ${buttonDisabledSelectors}`; const buttonColor = new Color(props?.backgroundColor || variables?.colors?.primary); const fontColor = buttonColor.isDark() || buttonColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); - // disabled button selectors - const disabledSelectors = '&.ss__button--disabled, &.ss__load-more__button.ss__load-more__button--disabled'; - // shared button styles const disabledStyles = css({ - [`${disabledSelectors}`]: { + [buttonDisabledSelectors]: { opacity: 0.65, '&, & *': { cursor: 'not-allowed', @@ -36,8 +35,8 @@ const buttonStyleScript = (props: ButtonProps) => { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - [`&, &:hover, &:not(.ss__button--disabled):hover, ${disabledSelectors}`]: { - borderColor: buttonColor.hex(), + [buttonSelector]: { + border: `1px solid ${buttonColor.hex()}`, backgroundColor: buttonColor.hex(), }, }, @@ -49,7 +48,7 @@ const buttonStyleScript = (props: ButtonProps) => { { cursor: 'pointer', padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, - [`&, &:hover, &:not(.ss__button--disabled):hover, ${disabledSelectors}`]: { + [buttonSelector]: { border: `1px solid ${custom.colors.gray02}`, backgroundColor: custom.colors.white, fontSize: custom.utils.convertPxToEm(14), diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts index 9c42708ae..0069235b2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/dropdown.ts @@ -11,7 +11,7 @@ const dropdownStyleScript = ({ theme }: DropdownProps) => { width: 'auto', '&.ss__dropdown--open': { '.ss__dropdown__content': { - zIndex: 2, + zIndex: 4, }, }, '.ss__dropdown__content': { diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts index cf8ada961..fbdce2654 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/overlay.ts @@ -7,6 +7,7 @@ const overlayStyleScript = (props: OverlayProps) => { const backgroundColor = props?.color || 'rgba(0, 0, 0, 0.80)'; return css({ + cursor: 'pointer', '&, &.ss__overlay--active': { background: backgroundColor, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts index 68cd9e1b3..4ddb8c885 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetHierarchyOptions.ts @@ -15,7 +15,7 @@ const facetHierarchyOptionsStyleScript = (props: FacetHierarchyOptionsProps) => margin: `0 0 ${custom.spacing.x1}px 0`, padding: 0, color: variables?.colors?.text, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, '.ss__facet-hierarchy-options__option__value': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts index 5a556457f..ea4116149 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetListOptions.ts @@ -17,7 +17,7 @@ const facetListOptionsStyleScript = (props: FacetListOptionsProps) => { margin: `0 0 ${custom.spacing.x1}px 0`, color: variables?.colors?.text, padding: props?.hideCheckbox ? `` : `0 0 0 ${checkboxSpacing}px`, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, '.ss__checkbox, .ss__radio': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index d5879d0cf..0effc07db 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -170,7 +170,7 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { padding: `${hasCheckbox ? 0 : '2px'} 0 0 ${listPadding}px`, margin: `0 0 ${custom.spacing.x1}px 0`, minHeight: hasCheckbox ? '' : `${listSize + 2}px`, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, '.ss__checkbox, .ss__radio, .ss__facet-palette-options__option__wrapper': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts index c97b851ad..44627f4ca 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/index.ts @@ -15,6 +15,7 @@ import { grid } from './grid'; import { layoutSelector } from './layoutSelector'; import { list } from './list'; import { loadMore } from './loadMore'; +import { modal } from './modal'; import { overlayBadge } from './overlayBadge'; import { pagination } from './pagination'; import { radio } from './radio'; @@ -45,6 +46,7 @@ export const molecules: ThemeResponsiveComplete = { ...layoutSelector.default, ...list.default, ...loadMore.default, + ...modal.default, ...overlayBadge.default, ...pagination.default, ...radio.default, @@ -75,6 +77,7 @@ export const molecules: ThemeResponsiveComplete = { ...layoutSelector.mobile, ...list.mobile, ...loadMore.mobile, + ...modal.mobile, ...overlayBadge.mobile, ...pagination.mobile, ...radio.mobile, @@ -105,6 +108,7 @@ export const molecules: ThemeResponsiveComplete = { ...layoutSelector.tablet, ...list.tablet, ...loadMore.tablet, + ...modal.tablet, ...overlayBadge.tablet, ...pagination.tablet, ...radio.tablet, @@ -135,6 +139,7 @@ export const molecules: ThemeResponsiveComplete = { ...layoutSelector.desktop, ...list.desktop, ...loadMore.desktop, + ...modal.desktop, ...overlayBadge.desktop, ...pagination.desktop, ...radio.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts index 2b0531420..b70514a73 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/list.ts @@ -24,7 +24,7 @@ const listStyleScript = (props: ListProps) => { '.ss__list__options': { '.ss__list__option': { gap: `${custom.spacing.x2}px`, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/modal.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/modal.ts new file mode 100644 index 000000000..3027377f6 --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/modal.ts @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import type { ModalProps } from '../../../../components/Molecules/Modal'; +import { ThemeComponent } from '../../../../providers'; + +// CSS in JS style script for the Modal component +const modalStyleScript = (props: Partial) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + + return css({}); +}; + +// Modal component props +export const modal: ThemeComponent<'modal', ModalProps> = { + default: { + modal: { + themeStyleScript: modalStyleScript, + }, + }, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts index 23a8375f7..6f0ab671b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/radioList.ts @@ -22,7 +22,7 @@ const radioListStyleScript = (props: RadioListProps) => { '.ss__radio-list__options-wrapper': { '.ss__radio-list__option': { gap: `${custom.spacing.x2}px`, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, '.ss__radio-list__option__icon, .ss__radio-list__option__label': { diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts index c55bbdeb5..6f1997a19 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/searchInput.ts @@ -9,27 +9,39 @@ const searchInputStyleScript = (props: SearchInputProps) => { const variables = props?.theme?.variables; const isSecondary = props?.className?.includes('secondary') ? true : false; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); + const darkPrimary = custom.utils.darkenColor(variables?.colors?.primary, 0.15); // shared search input styles const sharedStyles = css({ '&.ss__search-input': { margin: `0 0 ${custom.spacing.x2}px`, - border: `1px solid ${custom.colors.gray02}`, - '.ss__icon, .ss__search-input__input': { + border: 0, + height: '35px', + '& > *': { minWidth: '1px', - padding: 0, }, - '.ss__icon': { + '.ss__search-input__input, .ss__search-input__icons, .ss__button': { + height: '100%', + lineHeight: 1, + }, + '.ss__search-input__icons, .ss__search-input__button--close-search-button': { flex: '0 1 auto', - margin: `0 0 0 ${custom.spacing.x2}px`, + }, + '.ss__button': { + padding: `0 ${custom.spacing.x2}px`, + border: 0, + justifyContent: 'center', + '.ss__icon': { + padding: 0, + fill: custom.colors.white, + stroke: custom.colors.white, + }, }, '.ss__search-input__input': { flex: '1 1 0%', - margin: `0 ${custom.spacing.x2}px`, - backgroundColor: `inherit`, + border: `1px solid ${custom.colors.gray02}`, + padding: `0 ${custom.spacing.x2}px`, minHeight: '1px', - height: `${custom.sizes.height}px`, - lineHeight: `${custom.sizes.height}px`, fontSize: custom.utils.convertPxToEm(14), color: variables?.colors?.text, '&::-webkit-input-placeholder': { @@ -42,6 +54,14 @@ const searchInputStyleScript = (props: SearchInputProps) => { color: lightGray, }, }, + '.ss__search-input__icons': { + gap: '2px', + margin: '0 0 0 -1px', + backgroundColor: darkPrimary, + }, + '.ss__search-input__button--close-search-button': { + margin: '0 -1px 0 0', + }, }, }); @@ -50,7 +70,9 @@ const searchInputStyleScript = (props: SearchInputProps) => { sharedStyles, { '&.ss__search-input': { - backgroundColor: `${custom.colors.gray01}`, + '.ss__search-input__input': { + backgroundColor: `${custom.colors.gray01}`, + }, }, }, ]); @@ -60,7 +82,9 @@ const searchInputStyleScript = (props: SearchInputProps) => { sharedStyles, { '&.ss__search-input': { - backgroundColor: `${custom.colors.white}`, + '.ss__search-input__input': { + backgroundColor: `${custom.colors.white}`, + }, }, }, ]); @@ -77,5 +101,14 @@ export const searchInput: ThemeComponent<'searchInput', SearchInputProps> = { 'searchInput icon': { size: `${custom.sizes.icon14}px`, }, + 'searchInput button.close-search icon': { + icon: custom.icons.arrowLeft, + }, + 'searchInput button.clear-search icon': { + icon: custom.icons.close, + }, + 'searchInput button.submit-search icon': { + icon: custom.icons.search, + }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts index 0396043c7..8db890b94 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/select.ts @@ -57,7 +57,7 @@ const selectStyleScript = (props: SelectProps) => { padding: 0, margin: `0 0 ${custom.spacing.x1}px 0`, color: 'inherit', - '&:last-child': { + '&:last-of-type': { marginBottom: '0', }, '&:hover': { diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts index 24540447e..2077b60f2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts @@ -70,10 +70,10 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { width: '200px', backgroundColor: custom.colors.gray01, textAlign: 'left', - '& > div:first-child .ss__autocomplete__title': { + '& > div:first-of-type .ss__autocomplete__title': { marginTop: `${custom.spacing.x2}px`, }, - '& > div:last-child .ss__autocomplete__terms__options': { + '& > div:last-of-type .ss__autocomplete__terms__options': { marginBottom: `${custom.spacing.x2}px`, }, '& > div': { @@ -86,9 +86,22 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { }, '.ss__autocomplete__terms__options': { '.ss__autocomplete__terms__option': { + '&:not(.ss__autocomplete__terms__option--active)': { + a: { + color: variables?.colors?.primary, + em: { + color: variables?.colors?.text, + }, + }, + }, a: { padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, fontSize: custom.utils.convertPxToEm(14), + em: { + fontStyle: 'normal', + fontSize: 'inherit', + fontWeight: 'inherit', + }, }, }, '.ss__autocomplete__terms__option--active': { @@ -110,12 +123,17 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { padding: 0, }, }, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, '.ss__facet__header': { borderBottom: 0, padding: 0, + '.ss__facet__header__inner': { + fontSize: 'inherit', + fontWeight: 'inherit', + color: 'inherit', + }, }, '.ss__facet__options': { margin: 0, @@ -124,7 +142,7 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { padding: 0, margin: `0 0 ${custom.spacing.x1}px 0`, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, }, @@ -206,7 +224,7 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { '& > div': { flex: '1 1 100%', borderBottom: `1px solid ${custom.colors.gray02}`, - '&:last-child': { + '&:last-of-type': { borderBottom: 0, }, '&, &.ss__autocomplete__terms': { @@ -222,10 +240,10 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { '& > div': { minWidth: '1px', flex: '1 1 0%', - '&:first-child .ss__autocomplete__title': { + '&:first-of-type .ss__autocomplete__title': { marginTop: 0, }, - '&:last-child .ss__autocomplete__terms__options': { + '&:last-of-type .ss__autocomplete__terms__options': { marginBottom: 0, }, '.ss__autocomplete__title h5': { @@ -259,7 +277,7 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { flexFlow: 'row nowrap', '.ss__facet': { flex: '1 1 0%', - '&, &:last-child': { + '&, &:last-of-type': { margin: 0, }, }, @@ -279,7 +297,7 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { '&.ss__autocomplete': { '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { gridTemplateColumns: `repeat(2, 1fr)`, - '& > div:nth-child(n+3)': { + '& > div:nth-of-type(n+3)': { display: 'none', }, }, @@ -316,12 +334,10 @@ export const autocomplete: ThemeComponent<'autocomplete', AutocompleteProps> = { 'autocomplete results': { rows: 2, columns: 3, - gapSize: `${custom.spacing.x4}px`, }, 'autocomplete recommendationGrid': { rows: 2, columns: 4, - gapSize: `${custom.spacing.x4}px`, }, 'autocomplete icon': { icon: custom.icons.arrowRight, @@ -335,12 +351,10 @@ export const autocomplete: ThemeComponent<'autocomplete', AutocompleteProps> = { 'autocomplete results': { rows: 1, columns: 3, - gapSize: `${custom.spacing.x2}px`, }, 'autocomplete recommendationGrid': { rows: 1, columns: 3, - gapSize: `${custom.spacing.x2}px`, }, }, tablet: { @@ -350,12 +364,10 @@ export const autocomplete: ThemeComponent<'autocomplete', AutocompleteProps> = { 'autocomplete results': { rows: 1, columns: 4, - gapSize: `${custom.spacing.x4}px`, }, 'autocomplete recommendationGrid': { rows: 1, columns: 4, - gapSize: `${custom.spacing.x4}px`, }, }, desktop: { @@ -363,12 +375,10 @@ export const autocomplete: ThemeComponent<'autocomplete', AutocompleteProps> = { 'autocomplete results': { rows: 2, columns: 3, - gapSize: `${custom.spacing.x4}px`, }, 'autocomplete recommendationGrid': { rows: 2, columns: 4, - gapSize: `${custom.spacing.x4}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/autocompleteLayout.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/autocompleteLayout.ts new file mode 100644 index 000000000..575acc60b --- /dev/null +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/autocompleteLayout.ts @@ -0,0 +1,463 @@ +import { css } from '@emotion/react'; +import type { AutocompleteLayoutProps } from '../../../../components/Organisms/AutocompleteLayout'; +import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; + +// CSS in JS style script for the Autocomplete Layout component +const autocompleteLayoutStyleScript = (props: AutocompleteLayoutProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + const tabletBp = variables?.breakpoints?.tablet || 1024; + const textSelectors = 'a, div, p'; + const headerSelectors = + '.ss__terms-list .ss__terms .ss__terms__title h5, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__button--see-more .ss__button__content, .ss__no-results__recommendations h3'; + const activeSelectors = + '.ss__terms .ss__terms__options .ss__terms__option.ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more:hover .ss__button__content'; + + // determine autocomplete layout and type + let acLayout = 'default'; + let acType = 'default'; + if (props?.className?.includes('slim')) { + acLayout = 'secondary'; + acType = 'slim'; + } else if (props?.className?.includes('terms')) { + acLayout = 'secondary'; + acType = 'terms'; + } + + // shared autocomplete styles + const sharedStyles = css({ + border: `1px solid ${custom.colors.gray02}`, + backgroundColor: custom.colors.white, + [textSelectors]: { + fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 12), + lineHeight: 1.5, + color: variables?.colors?.text, + }, + a: { + display: 'block', + }, + 'ul, ul li': { + padding: 0, + margin: 0, + listStyle: 'none', + }, + '.ss__banner': { + img: { + maxWidth: '100%', + maxHeight: '150px', + height: 'auto', + }, + }, + [headerSelectors]: { + margin: `0 0 ${custom.spacing.x4}px 0`, + padding: 0, + fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 17 : 16), + fontWeight: custom.fonts.weight02, + lineHeight: 1.2, + color: variables?.colors?.secondary, + }, + [activeSelectors]: { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, + '.ss__autocomplete__row .ss__autocomplete__column .ss__search-input': { + background: 'transparent', + width: 'auto', + height: '30px', + margin: `0 0 ${custom.spacing.x2}px 0`, + }, + }); + const sharedFacetStyles = css({ + '&.ss__facet--showing-all': { + '.ss__facet__options': { + maxHeight: 'none', + overflow: 'visible', + padding: 0, + }, + }, + '.ss__facet__header': { + borderBottom: 0, + '.ss__facet__header__inner': { + fontSize: 'inherit', + fontWeight: 'inherit', + color: 'inherit', + }, + }, + '.ss__facet__options': { + '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { + padding: 0, + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-of-type': { + marginBottom: 0, + }, + }, + }, + }); + const sharedContentStyles = css({ + '.ss__autocomplete__content': { + overflow: 'visible', + justifyContent: 'flex-start', + margin: `0 0 ${custom.spacing.x4}px 0`, + '.ss__autocomplete__content-inner': { + padding: 0, + }, + }, + '.ss__autocomplete__content__results': { + '.ss__results': { + overflowY: 'auto', + overflowX: 'hidden', + maxHeight: '75vh', + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + '.ss__result': { + '.ss__result__details': { + gap: `${custom.spacing.x1}px`, + }, + }, + '.ss__inline-banner': { + maxHeight: '250px', + overflow: 'hidden', + }, + }, + }, + '.ss__autocomplete__button--see-more': { + padding: 0, + width: '100%', + height: 'auto', + '&, &:hover': { + backgroundColor: 'transparent', + border: 0, + }, + '.ss__button__content': { + margin: 0, + '.ss__icon': { + margin: `0 0 0 ${custom.spacing.x1}px`, + }, + }, + }, + '.ss__autocomplete__content__no-results': { + '[ss-lang="noResultsText"]': { + p: { + display: 'inline', + margin: 0, + padding: 0, + '& ~ p': { + paddingLeft: '4px', + }, + }, + }, + '.ss__no-results__recommendations': { + '.ss__recommendation-grid': { + margin: `${custom.spacing.x4}px 0 0 0`, + '.ss__recommendation-grid__title': { + textAlign: 'left', + }, + }, + }, + }, + }); + const sharedTabletStyles = css({ + [`@media (max-width: ${tabletBp}px)`]: { + [headerSelectors]: { + fontSize: custom.utils.convertPxToEm(14), + }, + '.ss__terms-list': { + '.ss__terms': { + '.ss__terms__options': { + '.ss__terms__option': { + a: { + fontSize: custom.utils.convertPxToEm(12), + }, + }, + }, + }, + }, + '.ss__autocomplete__button--see-more': { + '.ss__button__content': { + '.ss__icon': { + position: 'relative', + top: '1px', + }, + }, + }, + }, + }); + const sharedMobileStyles = css({ + '@media (max-width: 540px)': { + '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { + gridTemplateColumns: `repeat(2, 1fr)`, + '& > *:nth-of-type(n+3)': { + display: 'none', + }, + }, + }, + }); + + // default autocomplete styles + const defaultStyles = css([ + sharedStyles, + { + '& > .ss__autocomplete__row': { + gap: `${custom.spacing.x4}px`, + '.ss__autocomplete__column': { + alignContent: 'flex-start', + minWidth: '1px', + padding: `${custom.spacing.x4}px 0`, + '&:first-of-type': { + paddingLeft: `${custom.spacing.x4}px`, + }, + '&:last-of-type': { + paddingRight: `${custom.spacing.x4}px`, + }, + '&:has(.ss__autocomplete__terms-wrapper)': { + padding: 0, + }, + }, + }, + '.ss__autocomplete__terms-wrapper': { + backgroundColor: custom.colors.gray01, + height: '100%', + }, + '.ss__terms-list': { + backgroundColor: 'transparent', + '.ss__terms-list__row': { + '&:first-of-type .ss__terms .ss__terms__title': { + marginTop: `${custom.spacing.x2}px`, + }, + '&:last-of-type .ss__terms .ss__terms__options': { + marginBottom: `${custom.spacing.x2}px`, + }, + }, + '.ss__terms': { + '.ss__terms__title': { + h5: { + margin: 0, + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + }, + }, + '.ss__terms__options': { + display: 'block', + margin: 0, + '.ss__terms__option': { + a: { + padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, + fontSize: custom.utils.convertPxToEm(14), + }, + }, + '.ss__terms__option--active': { + backgroundColor: custom.colors.white, + }, + }, + }, + }, + '.ss__autocomplete__facets': { + padding: 0, + textAlign: 'left', + '.ss__facets': { + '.ss__facet': { + margin: `0 0 ${custom.spacing.x4}px 0`, + '&:last-of-type': { + marginBottom: 0, + }, + ...sharedFacetStyles, + }, + }, + }, + [`@media (max-width: ${tabletBp}px)`]: { + '& > .ss__autocomplete__row': { + flexFlow: 'row wrap', + gap: 0, + '.ss__autocomplete__column': { + minWidth: '1px', + borderBottom: `1px solid ${custom.colors.gray02}`, + '&:last-of-type': { + borderBottom: 0, + }, + '&, &:has(.ss__autocomplete__terms-wrapper)': { + padding: `${custom.spacing.x4}px`, + }, + }, + }, + '.ss__autocomplete__terms-wrapper': { + backgroundColor: 'transparent', + }, + '.ss__terms-list': { + flexFlow: 'row nowrap', + gap: `${custom.spacing.x4}px`, + '.ss__terms-list__row': { + flex: '1 1 0%', + '&:first-of-type .ss__terms .ss__terms__title': { + marginTop: 0, + }, + '&:last-of-type .ss__terms .ss__terms__options': { + marginBottom: 0, + }, + }, + '.ss__terms': { + '.ss__terms__title': { + h5: { + padding: 0, + margin: `0 0 ${custom.spacing.x4}px 0`, + }, + }, + '.ss__terms__options': { + display: 'flex', + '.ss__terms__option': { + a: { + padding: 0, + }, + }, + }, + }, + }, + '.ss__autocomplete__facets': { + display: 'flex', + '.ss__facets': { + gap: `0 ${custom.spacing.x4}px`, + flexFlow: 'row nowrap', + minWidth: '1px', + '.ss__facet': { + flex: '1 1 0%', + '&, &:last-of-type': { + margin: 0, + }, + }, + }, + }, + }, + }, + sharedContentStyles, + sharedTabletStyles, + sharedMobileStyles, + ]); + + // secondary autocomplete styles + const secondaryStyles = css([ + sharedStyles, + { + '& > .ss__autocomplete__row': { + flexFlow: 'row wrap', + gap: 0, + '.ss__autocomplete__column': { + alignContent: 'flex-start', + flex: `1 1 100%`, + maxWidth: '100%', + minWidth: '1px', + padding: `${custom.spacing.x4}px`, + borderBottom: `1px solid ${custom.colors.gray02}`, + '&:last-of-type': { + borderBottom: 0, + }, + }, + }, + '.ss__autocomplete__terms-wrapper': { + backgroundColor: custom.colors.white, + height: '100%', + }, + '.ss__terms-list': { + flexFlow: 'row nowrap', + gap: `${custom.spacing.x4}px`, + backgroundColor: 'transparent', + '.ss__terms-list__row': { + flex: '1 1 0%', + }, + '.ss__terms': { + '.ss__terms__options': { + '.ss__terms__option': { + a: { + fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 14), + }, + }, + }, + }, + }, + '.ss__autocomplete__facets': { + padding: 0, + textAlign: 'left', + '.ss__facets': { + display: 'flex', + flexFlow: 'row nowrap', + gap: `0 ${custom.spacing.x4}px`, + '.ss__facet': { + flex: '1 1 0%', + minWidth: '1px', + margin: 0, + ...sharedFacetStyles, + }, + }, + }, + }, + sharedContentStyles, + sharedTabletStyles, + sharedMobileStyles, + ]); + + // return autocomplete styles + if (acLayout == 'secondary') { + return secondaryStyles; + } else { + return defaultStyles; + } +}; + +// Autocomplete Layout component props +export const autocompleteLayout: ThemeComponent<'autocompleteLayout', AutocompleteLayoutProps> = { + default: { + autocompleteLayout: { + themeStyleScript: autocompleteLayoutStyleScript, + contentTitle: 'Product Suggestions', + column1: { + width: '200px', + layout: ['termsList'], + }, + column2: { + width: '160px', + layout: ['facets'], + }, + column3: { + width: 'auto', + layout: ['content', 'button.see-more'], + }, + }, + }, + mobile: { + autocompleteLayout: { + layout: [['c1', 'c2']], + column1: { + width: '100%', + layout: ['termsList'], + }, + column2: { + width: '100%', + layout: ['content', 'button.see-more'], + }, + }, + }, + tablet: { + autocompleteLayout: { + layout: [['c1', 'c2', 'c3']], + column1: { + width: '100%', + layout: ['termsList'], + }, + column2: { + width: '100%', + layout: ['facets'], + }, + column3: { + width: '100%', + layout: ['content', 'button.see-more'], + }, + }, + }, + desktop: {}, +}; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts index c715375d5..03a170bb2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/filterSummary.ts @@ -23,7 +23,7 @@ const filterSummaryStyleScript = (props: FilterSummaryProps) => { '.ss__filter-summary__filters': { '.ss__filter': { margin: `0 0 ${custom.spacing.x1}px 0`, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, '.ss__filter__button': { diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts index 2a6ce520d..11cfc74d3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/index.ts @@ -2,6 +2,7 @@ import { ThemeResponsiveComplete } from '../../../../providers'; // ORGANISMS Imports import { autocomplete } from './autocomplete'; +import { autocompleteLayout } from './autocompleteLayout'; import { facet } from './facet'; import { facets } from './facets'; import { facetsHorizontal } from './facetsHorizontal'; @@ -16,6 +17,7 @@ import { toolbar } from './toolbar'; export const organisms: ThemeResponsiveComplete = { default: { ...autocomplete.default, + ...autocompleteLayout.default, ...facet.default, ...facets.default, ...facetsHorizontal.default, @@ -29,6 +31,7 @@ export const organisms: ThemeResponsiveComplete = { }, mobile: { ...autocomplete.mobile, + ...autocompleteLayout.mobile, ...facet.mobile, ...facets.mobile, ...facetsHorizontal.mobile, @@ -42,6 +45,7 @@ export const organisms: ThemeResponsiveComplete = { }, tablet: { ...autocomplete.tablet, + ...autocompleteLayout.tablet, ...facet.tablet, ...facets.tablet, ...facetsHorizontal.tablet, @@ -55,6 +59,7 @@ export const organisms: ThemeResponsiveComplete = { }, desktop: { ...autocomplete.desktop, + ...autocompleteLayout.desktop, ...facet.desktop, ...facets.desktop, ...facetsHorizontal.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index f1804cada..c62c7eb20 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -73,7 +73,7 @@ const mobileSidebarStyleScript = (props: MobileSidebarProps) => { '& > *': { borderBottom: `1px solid ${custom.colors.gray02}`, padding: `${custom.spacing.x4}px`, - '&:last-child': { + '&:last-of-type': { borderBottom: 0, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts index 966521326..6a02e9908 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/noResults.ts @@ -33,7 +33,7 @@ const noResultsStyleScript = (props: NoResultsProps) => { li: { listStyle: 'disc', margin: `0 0 ${custom.spacing.x1}px 0`, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts index 07393df90..68b2b9b12 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts @@ -17,11 +17,19 @@ export const results: ThemeComponent<'results', ResultsProps> = { results: { themeStyleScript: resultsStyleScript, gapSize: `${custom.spacing.x6}px ${custom.spacing.x4}px`, + columns: 4, }, }, mobile: { results: { gapSize: `${custom.spacing.x6}px ${custom.spacing.x2}px`, + columns: 2, }, }, + tablet: { + results: { + columns: 3, + }, + }, + desktop: {}, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts index dbaa4bc09..b565c0efa 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/sidebar.ts @@ -32,7 +32,7 @@ const sidebarStyleScript = (props: SidebarProps) => { }, '.ss__layout .ss__layout__row, .ss__facets .ss__facet': { margin: `0 0 ${custom.spacing.x6}px 0`, - '&:last-child': { + '&:last-of-type': { marginBottom: 0, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts index 2b190254d..57e90ebb2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/termsList.ts @@ -1,13 +1,77 @@ import { css } from '@emotion/react'; import type { TermsListProps } from '../../../../components/Organisms/TermsList'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the TermsList component const termsListStyleScript = (props: TermsListProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const tabletBp = variables?.breakpoints?.tablet || 1024; - return css({}); + return css({ + backgroundColor: 'transparent', + 'ul, ul li': { + padding: 0, + margin: 0, + listStyle: 'none', + }, + '.ss__terms': { + width: '100%', + textAlign: 'left', + '.ss__terms__title': { + h5: { + padding: 0, + margin: `0 0 ${custom.spacing.x4}px 0`, + fontSize: custom.utils.convertPxToEm(16), + fontWeight: custom.fonts.weight02, + lineHeight: 1.2, + color: variables?.colors?.secondary, + }, + }, + '.ss__terms__options': { + gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, + display: 'flex', + flexFlow: 'row wrap', + margin: 0, + '.ss__terms__option': { + flex: '0 1 auto', + minWidth: '1px', + '&.ss__terms__option--active': { + a: { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, + }, + a: { + padding: 0, + fontSize: custom.utils.convertPxToEm(14), + color: variables?.colors?.text, + em: { + fontStyle: 'normal', + fontSize: 'inherit', + }, + }, + }, + }, + }, + [`@media (max-width: ${tabletBp}px)`]: { + '.ss__terms': { + '.ss__terms__title': { + h5: { + fontSize: custom.utils.convertPxToEm(14), + }, + }, + '.ss__terms__options': { + '.ss__terms__option': { + a: { + fontSize: custom.utils.convertPxToEm(12), + }, + }, + }, + }, + }, + }); }; // TermsList component props diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts index 56e502bf8..011df4e9f 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts @@ -2,10 +2,58 @@ import { css } from '@emotion/react'; import { autocompleteFixedThemeComponentProps } from '../../../themeComponents/autocompleteFixed'; import { ThemeComponent } from '../../../../providers'; import { AutocompleteFixedProps } from '../../../../components/Templates/AutocompleteFixed'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component -const autocompleteFixedStyleScript = ({}: AutocompleteFixedProps) => { - return css({}); +const autocompleteFixedStyleScript = (props: AutocompleteFixedProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + const tabletBp = variables?.breakpoints?.tablet || 1024; + + return css({ + width: props?.width, + '.ss__modal': { + '.ss__slideout__overlay': { + display: 'none', + }, + '.ss__modal__content': { + '.ss__autocomplete-fixed__inner': { + '& > .ss__search-input': { + height: '40px', + margin: `0 0 ${custom.spacing.x1}px 0`, + '.ss__search-input__icons .ss__button, .ss__search-input__button--close-search-button': { + width: '40px', + padding: 0, + }, + }, + '.ss__autocomplete-fixed__inner__layout-wrapper': { + '.ss__autocomplete': { + width: props?.width, + right: 0, + left: '-102px', + top: 'auto', + margin: 'auto', + }, + }, + }, + }, + }, + [`@media (max-width: ${tabletBp}px)`]: { + '.ss__modal': { + '.ss__modal__content': { + '.ss__autocomplete-fixed__inner': { + '.ss__autocomplete-fixed__inner__layout-wrapper': { + '.ss__autocomplete': { + width: props?.width, + left: 0, + right: 0, + }, + }, + }, + }, + }, + }, + }); }; export const autocompleteFixed: ThemeComponent<'autocompleteFixed', AutocompleteFixedProps> = { @@ -14,9 +62,75 @@ export const autocompleteFixed: ThemeComponent<'autocompleteFixed', Autocomplete autocompleteFixed: { ...(autocompleteFixedThemeComponentProps.default?.['autocompleteFixed'] || {}), themeStyleScript: autocompleteFixedStyleScript, + width: '900px', + }, + 'autocompleteFixed searchInput': { + className: 'ss__secondary', + }, + 'autocompleteFixed facet searchInput': { + className: '', + }, + 'autocompleteFixed termsList': { + retainHistory: true, + retainTrending: true, + }, + 'autocompleteFixed facetPaletteOptions': { + gridSize: '38px', + hideLabel: false, + }, + 'autocompleteFixed facetGridOptions': { + gridSize: '38px', + }, + 'autocompleteFixed results': { + rows: 2, + columns: 3, + }, + 'autocompleteFixed recommendationGrid': { + rows: 2, + columns: 4, + }, + 'autocompleteFixed button.see-more icon': { + icon: custom.icons.arrowRight, + size: `${custom.sizes.icon12}px`, + }, + }, + mobile: { + ...autocompleteFixedThemeComponentProps.mobile, + autocompleteFixed: { + width: '100%', + }, + 'autocompleteFixed results': { + rows: 1, + columns: 3, + }, + 'autocompleteFixed recommendationGrid': { + rows: 1, + columns: 3, + }, + }, + tablet: { + ...autocompleteFixedThemeComponentProps.tablet, + autocompleteFixed: { + width: '100%', + }, + 'autocompleteFixed results': { + rows: 1, + columns: 4, + }, + 'autocompleteFixed recommendationGrid': { + rows: 1, + columns: 4, + }, + }, + desktop: { + ...autocompleteFixedThemeComponentProps.desktop, + 'autocompleteFixed results': { + rows: 2, + columns: 3, + }, + 'autocompleteFixed recommendationGrid': { + rows: 2, + columns: 4, }, }, - mobile: autocompleteFixedThemeComponentProps.mobile, - desktop: autocompleteFixedThemeComponentProps.desktop, - tablet: autocompleteFixedThemeComponentProps.tablet, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts deleted file mode 100644 index 0d385ae5b..000000000 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteTemplate.ts +++ /dev/null @@ -1,550 +0,0 @@ -// import { css } from '@emotion/react'; -// import type { AutocompleteTemplateProps } from '../../../../components/Templates/AutocompleteTemplate'; -// import { autocompleteThemeComponentProps } from '../../../themeComponents/autocompleteTemplate'; -// import { ThemeComponent } from '../../../../providers'; -// import { custom } from '../../custom'; - -// // CSS in JS style script for the Search component -// const autocompleteTemplateStyleScript = (props: AutocompleteTemplateProps) => { -// // eslint-disable-next-line @typescript-eslint/no-unused-vars -// const variables = props?.theme?.variables; -// const tabletBp = variables?.breakpoints?.tablet || 1024; -// const textSelectors = 'a, div, p'; -// const headerSelectors = -// '.ss__terms .ss__terms__title h5, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__header, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__autocomplete__title h5, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a, .ss__no-results__recommendations h3'; -// const activeSelectors = -// '.ss__terms .ss__terms__options .ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more .ss__button__content .ss__autocomplete__see-more a:hover'; - -// // determine autocomplete layout and type -// let acLayout = 'default'; -// let acType = 'default'; -// if (props?.className?.includes('slim')) { -// acLayout = 'secondary'; -// acType = 'slim'; -// } else if (props?.className?.includes('terms')) { -// acLayout = 'secondary'; -// acType = 'terms'; -// } - -// // shared autocomplete styles -// const sharedStyles = css({ -// border: `1px solid ${custom.colors.gray02}`, -// backgroundColor: custom.colors.white, -// width: props?.width, -// right: 0, -// left: 'auto', -// top: 'auto', -// margin: `${custom.spacing.x1}px 0 0 0`, -// [textSelectors]: { -// fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 12), -// lineHeight: 1.5, -// color: variables?.colors?.text, -// }, -// a: { -// display: 'block', -// }, -// 'ul, ul li': { -// padding: 0, -// margin: 0, -// listStyle: 'none', -// }, -// '.ss__banner': { -// img: { -// maxWidth: '100%', -// maxHeight: '150px', -// height: 'auto', -// }, -// }, -// [headerSelectors]: { -// margin: `0 0 ${custom.spacing.x4}px 0`, -// padding: 0, -// fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 17 : 16), -// fontWeight: custom.fonts.weight02, -// lineHeight: 1.2, -// color: variables?.colors?.secondary, -// }, -// [activeSelectors]: { -// fontWeight: custom.fonts.weight01, -// color: variables?.colors?.primary, -// }, -// }); -// const sharedFacetStyles = css({ -// '&.ss__facet--showing-all': { -// '.ss__facet__options': { -// maxHeight: 'none', -// overflow: 'visible', -// padding: 0, -// }, -// }, -// '.ss__facet__header': { -// borderBottom: 0, -// }, -// '.ss__facet__options': { -// '.ss__facet-hierarchy-options .ss__facet-hierarchy-options__option, .ss__facet-list-options .ss__facet-list-options__option': { -// padding: 0, -// margin: `0 0 ${custom.spacing.x1}px 0`, -// '&:last-child': { -// marginBottom: 0, -// }, -// }, -// }, -// }); -// const sharedContentStyles = css({ -// '.ss__autocomplete__content': { -// overflow: 'visible', -// justifyContent: 'flex-start', -// margin: `0 0 ${custom.spacing.x4}px 0`, -// '.ss__autocomplete__content-inner': { -// padding: 0, -// }, -// }, -// '.ss__autocomplete__content__results': { -// '.ss__results': { -// overflowY: 'auto', -// overflowX: 'hidden', -// maxHeight: '75vh', -// '&::-webkit-scrollbar': { -// width: '8px', -// height: '8px', -// }, -// '&::-webkit-scrollbar-track': { -// backgroundColor: custom.colors.gray01, -// }, -// '&::-webkit-scrollbar-thumb': { -// backgroundColor: custom.colors.gray02, -// }, -// '.ss__result': { -// '.ss__result__details': { -// gap: `${custom.spacing.x1}px`, -// }, -// }, -// '.ss__inline-banner': { -// maxHeight: '250px', -// overflow: 'hidden', -// }, -// }, -// }, -// '.ss__autocomplete__button--see-more': { -// padding: 0, -// width: '100%', -// height: 'auto', -// '&, &:hover': { -// backgroundColor: 'transparent', -// border: 0, -// }, -// '.ss__button__content': { -// '.ss__autocomplete__see-more': { -// a: { -// margin: 0, -// }, -// }, -// }, -// }, -// '.ss__autocomplete__content__no-results': { -// '[ss-lang="noResultsText"]': { -// p: { -// display: 'inline', -// margin: 0, -// padding: 0, -// '& ~ p': { -// paddingLeft: '4px', -// }, -// }, -// }, -// '.ss__no-results__recommendations': { -// margin: `${custom.spacing.x4}px 0 0 0`, -// }, -// }, -// }); -// const sharedTabletStyles = css({ -// [`@media (max-width: ${tabletBp}px)`]: { -// width: props?.width, -// left: 0, -// right: 0, -// [headerSelectors]: { -// fontSize: custom.utils.convertPxToEm(14), -// }, -// '.ss__terms': { -// '.ss__terms__options': { -// '.ss__terms__option': { -// a: { -// fontSize: custom.utils.convertPxToEm(12), -// }, -// }, -// }, -// }, -// '.ss__autocomplete__button--see-more': { -// '.ss__button__content': { -// '.ss__autocomplete__see-more': { -// a: { -// '.ss__icon': { -// position: 'relative', -// top: '1px', -// }, -// }, -// }, -// }, -// }, -// }, -// }); -// const sharedMobileStyles = css({ -// '@media (max-width: 540px)': { -// '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { -// gridTemplateColumns: `repeat(2, 1fr)`, -// '& > div:nth-child(n+3)': { -// display: 'none', -// }, -// }, -// }, -// }); - -// // default autocomplete styles -// const defaultStyles = css([ -// sharedStyles, -// { -// '& > .ss__autocomplete__row': { -// gap: `${custom.spacing.x4}px`, -// '.ss__autocomplete__column': { -// alignContent: 'flex-start', -// minWidth: '1px', -// padding: `${custom.spacing.x4}px 0`, -// '&:first-child': { -// paddingLeft: `${custom.spacing.x4}px`, -// }, -// '&:last-child': { -// paddingRight: `${custom.spacing.x4}px`, -// }, -// '&:has(.ss__autocomplete__terms-wrapper)': { -// padding: 0, -// }, -// }, -// }, -// '.ss__autocomplete__terms-wrapper': { -// backgroundColor: custom.colors.gray01, -// height: '100%', -// }, -// '.ss__terms-list': { -// backgroundColor: 'transparent', -// '.ss__terms-list__row': { -// '&:first-child .ss__terms .ss__terms__title': { -// marginTop: `${custom.spacing.x2}px`, -// }, -// '&:last-child .ss__terms .ss__terms__options': { -// marginBottom: `${custom.spacing.x2}px`, -// }, -// }, -// }, -// '.ss__terms': { -// width: '100%', -// textAlign: 'left', -// '.ss__terms__title': { -// h5: { -// margin: 0, -// padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, -// }, -// }, -// '.ss__terms__options': { -// display: 'block', -// margin: 0, -// '.ss__terms__option': { -// a: { -// padding: `${custom.spacing.x2}px ${custom.spacing.x4}px`, -// fontSize: custom.utils.convertPxToEm(14), -// }, -// }, -// '.ss__terms__option--active': { -// backgroundColor: custom.colors.white, -// }, -// }, -// }, -// '.ss__autocomplete__facets': { -// padding: 0, -// textAlign: 'left', -// '.ss__facets': { -// '.ss__facet': { -// margin: `0 0 ${custom.spacing.x4}px 0`, -// '&:last-child': { -// marginBottom: 0, -// }, -// ...sharedFacetStyles, -// }, -// }, -// }, -// [`@media (max-width: ${tabletBp}px)`]: { -// '& > .ss__autocomplete__row': { -// flexFlow: 'row wrap', -// gap: 0, -// '.ss__autocomplete__column': { -// minWidth: '1px', -// borderBottom: `1px solid ${custom.colors.gray02}`, -// '&:last-child': { -// borderBottom: 0, -// }, -// '&, &:has(.ss__autocomplete__terms-wrapper)': { -// padding: `${custom.spacing.x4}px`, -// }, -// }, -// }, -// '.ss__autocomplete__terms-wrapper': { -// backgroundColor: 'transparent', -// }, -// '.ss__terms-list': { -// flexFlow: 'row nowrap', -// gap: `${custom.spacing.x4}px`, -// '.ss__terms-list__row': { -// flex: '1 1 0%', -// '&:first-child .ss__terms .ss__terms__title': { -// marginTop: 0, -// }, -// '&:last-child .ss__terms .ss__terms__options': { -// marginBottom: 0, -// }, -// }, -// }, -// '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { -// display: 'flex', -// }, -// '.ss__terms .ss__terms__options .ss__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { -// minWidth: '1px', -// }, -// '.ss__terms': { -// '.ss__terms__title h5': { -// padding: 0, -// margin: `0 0 ${custom.spacing.x4}px 0`, -// }, -// '.ss__terms__options': { -// gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, -// flexFlow: 'row wrap', -// '.ss__terms__option': { -// flex: '0 1 auto', -// a: { -// padding: 0, -// }, -// }, -// }, -// }, -// '.ss__autocomplete__facets': { -// '.ss__facets': { -// gap: `0 ${custom.spacing.x4}px`, -// flexFlow: 'row nowrap', -// '.ss__facet': { -// flex: '1 1 0%', -// '&, &:last-child': { -// margin: 0, -// }, -// }, -// }, -// }, -// }, -// }, -// sharedContentStyles, -// sharedTabletStyles, -// sharedMobileStyles, -// ]); - -// // secondary autocomplete styles -// const secondaryStyles = css([ -// sharedStyles, -// { -// '& > .ss__autocomplete__row': { -// flexFlow: 'row wrap', -// gap: 0, -// '.ss__autocomplete__column': { -// alignContent: 'flex-start', -// flex: `1 1 100%`, -// maxWidth: '100%', -// minWidth: '1px', -// padding: `${custom.spacing.x4}px`, -// borderBottom: `1px solid ${custom.colors.gray02}`, -// '&:last-child': { -// borderBottom: 0, -// }, -// }, -// }, -// '.ss__autocomplete__terms-wrapper': { -// backgroundColor: custom.colors.white, -// height: '100%', -// }, -// '.ss__terms-list': { -// flexFlow: 'row nowrap', -// gap: `${custom.spacing.x4}px`, -// backgroundColor: 'transparent', -// '.ss__terms-list__row': { -// flex: '1 1 0%', -// }, -// }, -// '.ss__terms .ss__terms__options, .ss__autocomplete__facets .ss__facets': { -// display: 'flex', -// }, -// '.ss__terms .ss__terms__options .ss__terms__options, .ss__autocomplete__facets .ss__facets .ss__facet': { -// minWidth: '1px', -// }, -// '.ss__terms': { -// width: '100%', -// textAlign: 'left', -// '.ss__terms__title': { -// h5: { -// padding: 0, -// }, -// }, -// '.ss__terms__options': { -// gap: `${custom.spacing.x1}px ${custom.spacing.x4}px`, -// flexFlow: 'row wrap', -// margin: 0, -// '.ss__terms__option': { -// flex: '0 1 auto', -// a: { -// padding: 0, -// fontSize: custom.utils.convertPxToEm(acType == 'terms' ? 15 : 14), -// }, -// }, -// }, -// }, -// '.ss__autocomplete__facets': { -// padding: 0, -// textAlign: 'left', -// '.ss__facets': { -// gap: `0 ${custom.spacing.x4}px`, -// flexFlow: 'row nowrap', -// '.ss__facet': { -// flex: '1 1 0%', -// margin: 0, -// ...sharedFacetStyles, -// }, -// }, -// }, -// }, -// sharedContentStyles, -// sharedTabletStyles, -// sharedMobileStyles, -// ]); - -// // return autocomplete styles -// if (acLayout == 'secondary') { -// return secondaryStyles; -// } else { -// return defaultStyles; -// } -// }; - -// // AutocompleteTemplate component props come from Template export -// export const autocompleteTemplate: ThemeComponent<'autocompleteTemplate', AutocompleteTemplateProps> = { -// default: { -// ...autocompleteThemeComponentProps.default, -// autocompleteTemplate: { -// ...(autocompleteThemeComponentProps.default?.['autocompleteTemplate'] || {}), -// themeStyleScript: autocompleteTemplateStyleScript, -// width: '900px', -// contentTitle: 'Product Suggestions', -// column1: { -// width: '200px', -// layout: ['termsList'], -// }, -// column2: { -// width: '160px', -// layout: ['facets'], -// }, -// column3: { -// width: 'auto', -// layout: ['content', 'button.see-more'], -// }, -// }, -// 'autocompleteTemplate termsList': { -// retainHistory: true, -// retainTrending: true, -// }, -// 'autocompleteTemplate facetPaletteOptions': { -// gridSize: '38px', -// hideLabel: false, -// }, -// 'autocompleteTemplate facetGridOptions': { -// gridSize: '38px', -// }, -// 'autocompleteTemplate results': { -// rows: 2, -// columns: 3, -// gapSize: `${custom.spacing.x4}px`, -// }, -// 'autocompleteTemplate recommendationGrid': { -// rows: 2, -// columns: 4, -// gapSize: `${custom.spacing.x4}px`, -// }, -// 'autocompleteTemplate icon': { -// icon: custom.icons.arrowRight, -// size: `${custom.sizes.icon12}px`, -// }, -// }, -// mobile: { -// ...autocompleteThemeComponentProps.mobile, -// autocompleteTemplate: { -// ...(autocompleteThemeComponentProps.mobile?.['autocompleteTemplate'] || {}), -// width: '100%', -// layout: [['c1', 'c2']], -// column1: { -// width: '100%', -// layout: ['termsList'], -// }, -// column2: { -// width: '100%', -// layout: ['content', 'button.see-more'], -// }, -// }, -// 'autocompleteTemplate results': { -// rows: 1, -// columns: 3, -// gapSize: `${custom.spacing.x2}px`, -// }, -// 'autocompleteTemplate recommendationGrid': { -// rows: 1, -// columns: 3, -// gapSize: `${custom.spacing.x2}px`, -// }, -// }, -// tablet: { -// ...autocompleteThemeComponentProps.tablet, -// autocompleteTemplate: { -// ...(autocompleteThemeComponentProps.tablet?.['autocompleteTemplate'] || {}), -// width: '100%', -// layout: [['c1', 'c2', 'c3']], -// column1: { -// width: '100%', -// layout: ['termsList'], -// }, -// column2: { -// width: '100%', -// layout: ['facets'], -// }, -// column3: { -// width: '100%', -// layout: ['content', 'button.see-more'], -// }, -// }, -// 'autocompleteTemplate results': { -// rows: 1, -// columns: 4, -// gapSize: `${custom.spacing.x4}px`, -// }, -// 'autocompleteTemplate recommendationGrid': { -// rows: 1, -// columns: 4, -// gapSize: `${custom.spacing.x4}px`, -// }, -// }, -// desktop: { -// ...autocompleteThemeComponentProps.desktop, -// autocompleteTemplate: { -// ...(autocompleteThemeComponentProps.desktop?.['autocompleteTemplate'] || {}), -// }, -// 'autocompleteTemplate results': { -// rows: 2, -// columns: 3, -// gapSize: `${custom.spacing.x4}px`, -// }, -// 'autocompleteTemplate recommendationGrid': { -// rows: 2, -// columns: 4, -// gapSize: `${custom.spacing.x4}px`, -// }, -// }, -// }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/index.ts b/packages/snap-preact/components/src/themes/pike/components/templates/index.ts index f5c5df677..d65486761 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/index.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/index.ts @@ -1,8 +1,9 @@ import { ThemeResponsiveComplete } from '../../../../providers'; // TEMPLATES -//import { autocompleteTemplate } from './autocompleteTemplate'; import { autocompleteFixed } from './autocompleteFixed'; +import { autocompleteModal } from './autocompleteModal'; +import { autocompleteSlideout } from './autocompleteSlideout'; import { recommendation } from './recommendation'; import { recommendationBundle } from './recommendationBundle'; import { recommendationBundleEasyAdd } from './recommendationBundleEasyAdd'; @@ -19,6 +20,8 @@ import { searchSnappy } from './searchSnappy'; export const templates: ThemeResponsiveComplete = { default: { ...autocompleteFixed.default, + ...autocompleteModal.default, + ...autocompleteSlideout.default, ...recommendation.default, ...recommendationBundle.default, ...recommendationBundleEasyAdd.default, @@ -33,7 +36,9 @@ export const templates: ThemeResponsiveComplete = { ...searchHorizontal.default, }, mobile: { - //...autocompleteTemplate.mobile, + ...autocompleteFixed.mobile, + ...autocompleteModal.mobile, + ...autocompleteSlideout.mobile, ...recommendation.mobile, ...recommendationBundle.mobile, ...recommendationBundleEasyAdd.mobile, @@ -48,7 +53,9 @@ export const templates: ThemeResponsiveComplete = { ...searchHorizontal.mobile, }, tablet: { - //...autocompleteTemplate.tablet, + ...autocompleteFixed.tablet, + ...autocompleteModal.tablet, + ...autocompleteSlideout.tablet, ...recommendation.tablet, ...recommendationBundle.tablet, ...recommendationBundleEasyAdd.tablet, @@ -63,7 +70,9 @@ export const templates: ThemeResponsiveComplete = { ...searchHorizontal.tablet, }, desktop: { - //...autocompleteTemplate.desktop, + ...autocompleteFixed.desktop, + ...autocompleteModal.desktop, + ...autocompleteSlideout.desktop, ...recommendation.desktop, ...recommendationBundle.desktop, ...recommendationBundleEasyAdd.desktop, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts index 303b18a45..7bdef64db 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendation.ts @@ -90,7 +90,6 @@ export const recommendation: ThemeComponent<'recommendation', RecommendationProp mobile: { ...recommendationThemeComponentProps.mobile, recommendation: { - ...(recommendationThemeComponentProps.mobile?.['recommendation'] || {}), spaceBetween: custom.spacing.x2, }, 'recommendation icon.prev': { @@ -103,7 +102,6 @@ export const recommendation: ThemeComponent<'recommendation', RecommendationProp tablet: { ...recommendationThemeComponentProps.tablet, recommendation: { - ...(recommendationThemeComponentProps.tablet?.['recommendation'] || {}), spaceBetween: custom.spacing.x4, }, 'recommendation icon.prev': { @@ -116,7 +114,6 @@ export const recommendation: ThemeComponent<'recommendation', RecommendationProp desktop: { ...recommendationThemeComponentProps.desktop, recommendation: { - ...(recommendationThemeComponentProps.desktop?.['recommendation'] || {}), spaceBetween: custom.spacing.x4, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts index 1458d5cd5..fb0bd0630 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationBundle.ts @@ -224,27 +224,18 @@ export const recommendationBundle: ThemeComponent<'recommendationBundle', Recomm }, mobile: { ...recommendationBundleThemeComponentProps.mobile, - recommendationBundle: { - ...(recommendationBundleThemeComponentProps.mobile?.['recommendationBundle'] || {}), - }, 'recommendationBundle carousel': { spaceBetween: 0, }, }, tablet: { ...recommendationBundleThemeComponentProps.tablet, - recommendationBundle: { - ...(recommendationBundleThemeComponentProps.tablet?.['recommendationBundle'] || {}), - }, 'recommendationBundle carousel': { spaceBetween: custom.spacing.x4, }, }, desktop: { ...recommendationBundleThemeComponentProps.desktop, - recommendationBundle: { - ...(recommendationBundleThemeComponentProps.desktop?.['recommendationBundle'] || {}), - }, 'recommendationBundle carousel': { spaceBetween: custom.spacing.x4, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts index c2a0c9fde..ec4160a0b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts @@ -2,13 +2,29 @@ import { css } from '@emotion/react'; import type { RecommendationGridProps } from '../../../../components/Templates/RecommendationGrid'; import { recommendationGridThemeComponentProps } from '../../../themeComponents/recommendationGrid'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the RecommendationBundle component const recommendationGridStyleScript = (props: RecommendationGridProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const mobileBp = variables?.breakpoints?.mobile || 767; - return css({}); + return css({ + margin: `${custom.spacing.x8}px 0`, + '.ss__recommendation-grid__title': { + fontSize: custom.utils.convertPxToEm(22), + fontWeight: custom.fonts.weight02, + color: variables?.colors?.secondary, + textAlign: 'center', + margin: `0 0 ${custom.spacing.x4}px 0`, + }, + [`@media (max-width: ${mobileBp}px)`]: { + '.ss__recommendation__title': { + textAlign: 'left', + }, + }, + }); }; // RecommendationGrid component props come from Template export @@ -18,9 +34,24 @@ export const recommendationGrid: ThemeComponent<'recommendationGrid', Recommenda recommendationGrid: { ...(recommendationGridThemeComponentProps.default?.['recommendationGrid'] || {}), themeStyleScript: recommendationGridStyleScript, + gapSize: `${custom.spacing.x6}px ${custom.spacing.x4}px`, + columns: 4, + }, + }, + mobile: { + ...recommendationGridThemeComponentProps.mobile, + recommendationGrid: { + gapSize: `${custom.spacing.x6}px ${custom.spacing.x2}px`, + columns: 2, }, }, - mobile: recommendationGridThemeComponentProps.mobile, - desktop: recommendationGridThemeComponentProps.desktop, - tablet: recommendationGridThemeComponentProps.tablet, + tablet: { + ...recommendationGridThemeComponentProps.tablet, + recommendationGrid: { + columns: 3, + }, + }, + desktop: { + ...recommendationGridThemeComponentProps.desktop, + }, }; diff --git a/packages/snap-preact/components/src/themes/pike/custom.ts b/packages/snap-preact/components/src/themes/pike/custom.ts index c7f3d0bc2..3a2734bdc 100644 --- a/packages/snap-preact/components/src/themes/pike/custom.ts +++ b/packages/snap-preact/components/src/themes/pike/custom.ts @@ -50,6 +50,7 @@ export const custom: { minus: 'minus', plus: 'plus', filter: 'filter', + search: 'search', sort: 'sort', }, sizes: { From f4a5bd6c287d933ec30251042bfff3082731ff3c Mon Sep 17 00:00:00 2001 From: adria Date: Mon, 11 Aug 2025 17:21:20 -0600 Subject: [PATCH 037/118] autocomplete layouts --- .../snap-preact-demo/templates/src/index.ts | 2 +- .../pike/components/organisms/autocomplete.ts | 2 +- .../organisms/autocompleteLayout.ts | 23 +- .../components/organisms/mobileSidebar.ts | 2 +- .../components/templates/autocompleteFixed.ts | 10 +- .../components/templates/autocompleteModal.ts | 173 +++++++++++++- .../templates/autocompleteSlideout.ts | 213 +++++++++++++++++- .../templates/recommendationGrid.ts | 17 +- 8 files changed, 417 insertions(+), 25 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 3d76a5849..bbeec6fef 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -83,7 +83,7 @@ let config: SnapTemplatesConfig = { targets: [ { selector: 'input.searchspring-ac', - component: 'AutocompleteFixed', + component: 'AutocompleteSlideout', }, ], }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts index 2077b60f2..6b706f1f7 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/autocomplete.ts @@ -225,7 +225,7 @@ const autocompleteStyleScript = (props: AutocompleteProps) => { flex: '1 1 100%', borderBottom: `1px solid ${custom.colors.gray02}`, '&:last-of-type': { - borderBottom: 0, + borderBottomWidth: 0, }, '&, &.ss__autocomplete__terms': { padding: `${custom.spacing.x4}px`, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/autocompleteLayout.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/autocompleteLayout.ts index 575acc60b..946da5aa4 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/autocompleteLayout.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/autocompleteLayout.ts @@ -14,10 +14,14 @@ const autocompleteLayoutStyleScript = (props: AutocompleteLayoutProps) => { const activeSelectors = '.ss__terms .ss__terms__options .ss__terms__option.ss__terms__option--active a, .ss__autocomplete__facets-wrapper .ss__autocomplete__facets .ss__facets .ss__facet .ss__facet__options .ss__facet-list-options .ss__facet-list-options__option--filtered, .ss__autocomplete__content .ss__autocomplete__content-inner .ss__autocomplete__content__results .ss__results .ss__result:hover .ss__result__details .ss__result__details__title a, .ss__autocomplete__button--see-more:hover .ss__button__content'; + // check if there is a slideout autocomplete + const hasAcSlideout = + document.querySelectorAll('.ss__autocomplete-slideout') && document.querySelectorAll('.ss__autocomplete-slideout').length !== 0 ? true : false; + // determine autocomplete layout and type let acLayout = 'default'; let acType = 'default'; - if (props?.className?.includes('slim')) { + if (props?.className?.includes('slim') || hasAcSlideout) { acLayout = 'secondary'; acType = 'slim'; } else if (props?.className?.includes('terms')) { @@ -103,6 +107,14 @@ const autocompleteLayoutStyleScript = (props: AutocompleteLayoutProps) => { padding: 0, }, }, + '.ss__autocomplete__content__results, .ss__autocomplete__content__no-results .ss__no-results__recommendations .ss__recommendation-grid .ss__recommendation-grid__results': + { + '.ss__result': { + '.ss__result__details': { + gap: `${custom.spacing.x1}px`, + }, + }, + }, '.ss__autocomplete__content__results': { '.ss__results': { overflowY: 'auto', @@ -118,11 +130,6 @@ const autocompleteLayoutStyleScript = (props: AutocompleteLayoutProps) => { '&::-webkit-scrollbar-thumb': { backgroundColor: custom.colors.gray02, }, - '.ss__result': { - '.ss__result__details': { - gap: `${custom.spacing.x1}px`, - }, - }, '.ss__inline-banner': { maxHeight: '250px', overflow: 'hidden', @@ -280,7 +287,7 @@ const autocompleteLayoutStyleScript = (props: AutocompleteLayoutProps) => { minWidth: '1px', borderBottom: `1px solid ${custom.colors.gray02}`, '&:last-of-type': { - borderBottom: 0, + borderBottomWidth: 0, }, '&, &:has(.ss__autocomplete__terms-wrapper)': { padding: `${custom.spacing.x4}px`, @@ -355,7 +362,7 @@ const autocompleteLayoutStyleScript = (props: AutocompleteLayoutProps) => { padding: `${custom.spacing.x4}px`, borderBottom: `1px solid ${custom.colors.gray02}`, '&:last-of-type': { - borderBottom: 0, + borderBottomWidth: 0, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts index c62c7eb20..648d5b027 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/mobileSidebar.ts @@ -74,7 +74,7 @@ const mobileSidebarStyleScript = (props: MobileSidebarProps) => { borderBottom: `1px solid ${custom.colors.gray02}`, padding: `${custom.spacing.x4}px`, '&:last-of-type': { - borderBottom: 0, + borderBottomWidth: 0, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts index 011df4e9f..a5a34fb43 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteFixed.ts @@ -11,7 +11,6 @@ const autocompleteFixedStyleScript = (props: AutocompleteFixedProps) => { const tabletBp = variables?.breakpoints?.tablet || 1024; return css({ - width: props?.width, '.ss__modal': { '.ss__slideout__overlay': { display: 'none', @@ -27,7 +26,11 @@ const autocompleteFixedStyleScript = (props: AutocompleteFixedProps) => { }, }, '.ss__autocomplete-fixed__inner__layout-wrapper': { + overflowY: 'visible', + maxHeight: 'none', + width: 'auto', '.ss__autocomplete': { + maxWidth: 'none', width: props?.width, right: 0, left: '-102px', @@ -44,6 +47,7 @@ const autocompleteFixedStyleScript = (props: AutocompleteFixedProps) => { '.ss__autocomplete-fixed__inner': { '.ss__autocomplete-fixed__inner__layout-wrapper': { '.ss__autocomplete': { + maxWidth: '100%', width: props?.width, left: 0, right: 0, @@ -97,7 +101,7 @@ export const autocompleteFixed: ThemeComponent<'autocompleteFixed', Autocomplete mobile: { ...autocompleteFixedThemeComponentProps.mobile, autocompleteFixed: { - width: '100%', + width: 'auto', }, 'autocompleteFixed results': { rows: 1, @@ -111,7 +115,7 @@ export const autocompleteFixed: ThemeComponent<'autocompleteFixed', Autocomplete tablet: { ...autocompleteFixedThemeComponentProps.tablet, autocompleteFixed: { - width: '100%', + width: 'auto', }, 'autocompleteFixed results': { rows: 1, diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteModal.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteModal.ts index 6f4c05dbc..296d37adb 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteModal.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteModal.ts @@ -2,10 +2,110 @@ import { css } from '@emotion/react'; import { autocompleteModalThemeComponentProps } from '../../../themeComponents/autocompleteModal'; import { ThemeComponent } from '../../../../providers'; import { AutocompleteModalProps } from '../../../../components/Templates/AutocompleteModal'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component -const autocompleteModalStyleScript = ({}: AutocompleteModalProps) => { - return css({}); +const autocompleteModalStyleScript = (props: AutocompleteModalProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + const tabletBp = variables?.breakpoints?.tablet || 1024; + const mobileBp = variables?.breakpoints?.mobile || 767; + + return css({ + '.ss__modal': { + '&, .ss__modal__content': { + height: '100%', + }, + '.ss__modal__content': { + backgroundColor: 'transparent', + '&, .ss__autocomplete-modal__inner': { + position: 'static', + display: 'flex', + flexFlow: 'column nowrap', + justifyContent: 'center', + }, + '.ss__autocomplete-modal__inner': { + width: props?.width, + maxHeight: 'none', + height: '80vh', + overflow: 'hidden', + '& > .ss__search-input, .ss__autocomplete': { + minHeight: '1px', + minWidth: '1px', + }, + '& > .ss__search-input': { + flex: '0 1 auto', + height: '40px', + margin: 0, + '.ss__search-input__icons .ss__button, .ss__search-input__button--close-search-button': { + width: '40px', + padding: 0, + }, + }, + '.ss__autocomplete': { + flex: '1 1 0%', + overflowY: 'auto', + borderWidth: 0, + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + '.ss__autocomplete__content__results .ss__results': { + maxHeight: 'none', + overflow: 'visible', + }, + }, + }, + }, + }, + [`@media (max-width: ${tabletBp}px)`]: { + '.ss__modal': { + '.ss__modal__content': { + '.ss__autocomplete-modal__inner': { + '.ss__autocomplete': { + '& > .ss__autocomplete__row': { + alignContent: 'flex-start', + }, + }, + }, + }, + }, + }, + [`@media (max-width: ${mobileBp}px)`]: { + '.ss__modal': { + '.ss__modal__content': { + '.ss__autocomplete-modal__inner': { + width: props?.width, + height: '100%', + }, + }, + }, + }, + '@media (max-width: 540px)': { + '.ss__modal': { + '.ss__modal__content': { + '.ss__autocomplete-modal__inner': { + '.ss__autocomplete': { + '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { + '& > *:nth-of-type(n+3)': { + display: 'block', + }, + '& > *:nth-of-type(n+5)': { + display: 'none', + }, + }, + }, + }, + }, + }, + }, + }); }; export const autocompleteModal: ThemeComponent<'autocompleteModal', AutocompleteModalProps> = { @@ -14,9 +114,72 @@ export const autocompleteModal: ThemeComponent<'autocompleteModal', Autocomplete autocompleteModal: { ...(autocompleteModalThemeComponentProps.default?.['autocompleteModal'] || {}), themeStyleScript: autocompleteModalStyleScript, + width: '80vw', + }, + 'autocompleteModal searchInput': { + className: 'ss__secondary', + }, + 'autocompleteModal facet searchInput': { + className: '', + }, + 'autocompleteModal termsList': { + retainHistory: true, + retainTrending: true, + }, + 'autocompleteModal facetPaletteOptions': { + gridSize: '38px', + hideLabel: false, + }, + 'autocompleteModal facetGridOptions': { + gridSize: '38px', + }, + 'autocompleteModal results': { + rows: 2, + columns: 3, + }, + 'autocompleteModal recommendationGrid': { + rows: 2, + columns: 4, + }, + 'autocompleteModal button.see-more icon': { + icon: custom.icons.arrowRight, + size: `${custom.sizes.icon12}px`, + }, + }, + mobile: { + ...autocompleteModalThemeComponentProps.mobile, + autocompleteModal: { + width: '100%', + }, + 'autocompleteModal results': { + rows: 2, + columns: 3, + }, + 'autocompleteModal recommendationGrid': { + rows: 2, + columns: 3, + }, + }, + tablet: { + ...autocompleteModalThemeComponentProps.tablet, + 'autocompleteModal results': { + rows: 1, + columns: 4, + }, + 'autocompleteModal recommendationGrid': { + rows: 1, + columns: 4, + }, + }, + desktop: { + ...autocompleteModalThemeComponentProps.desktop, + 'autocompleteModal results': { + rows: 2, + columns: 3, + }, + 'autocompleteModal recommendationGrid': { + rows: 2, + columns: 4, }, }, - mobile: autocompleteModalThemeComponentProps.mobile, - desktop: autocompleteModalThemeComponentProps.desktop, - tablet: autocompleteModalThemeComponentProps.tablet, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts index fe5ed9f99..20a2b3bd5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts @@ -2,10 +2,110 @@ import { css } from '@emotion/react'; import { autocompleteSlideoutThemeComponentProps } from '../../../themeComponents/autocompleteSlideout'; import { ThemeComponent } from '../../../../providers'; import { AutocompleteSlideoutProps } from '../../../../components/Templates/AutocompleteSlideout'; +import { custom } from '../../custom'; // CSS in JS style script for the Search component -const autocompleteSlideoutStyleScript = ({}: AutocompleteSlideoutProps) => { - return css({}); +const autocompleteSlideoutStyleScript = (props: AutocompleteSlideoutProps) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const variables = props?.theme?.variables; + // const tabletBp = variables?.breakpoints?.tablet || 1024; + // const mobileBp = variables?.breakpoints?.mobile || 767; + + return css({ + // '.ss__modal': { + // '&, .ss__modal__content': { + // height: '100%', + // }, + // '.ss__modal__content': { + // backgroundColor: 'transparent', + // '&, .ss__autocomplete-modal__inner': { + // position: 'static', + // display: 'flex', + // flexFlow: 'column nowrap', + // justifyContent: 'center', + // }, + // '.ss__autocomplete-modal__inner': { + // width: props?.width, + // maxHeight: 'none', + // height: '80vh', + // overflow: 'hidden', + // '& > .ss__search-input, .ss__autocomplete': { + // minHeight: '1px', + // minWidth: '1px', + // }, + // '& > .ss__search-input': { + // flex: '0 1 auto', + // height: '40px', + // margin: 0, + // '.ss__search-input__icons .ss__button, .ss__search-input__button--close-search-button': { + // width: '40px', + // padding: 0, + // }, + // }, + // '.ss__autocomplete': { + // flex: '1 1 0%', + // overflowY: 'auto', + // borderWidth: 0, + // '&::-webkit-scrollbar': { + // width: '8px', + // height: '8px', + // }, + // '&::-webkit-scrollbar-track': { + // backgroundColor: custom.colors.gray01, + // }, + // '&::-webkit-scrollbar-thumb': { + // backgroundColor: custom.colors.gray02, + // }, + // '.ss__autocomplete__content__results .ss__results': { + // maxHeight: 'none', + // overflow: 'visible', + // } + // }, + // }, + // }, + // }, + // [`@media (max-width: ${tabletBp}px)`]: { + // '.ss__modal': { + // '.ss__modal__content': { + // '.ss__autocomplete-modal__inner': { + // '.ss__autocomplete': { + // '& > .ss__autocomplete__row': { + // alignContent: 'flex-start', + // } + // } + // } + // } + // } + // }, + // [`@media (max-width: ${mobileBp}px)`]: { + // '.ss__modal': { + // '.ss__modal__content': { + // '.ss__autocomplete-modal__inner': { + // width: props?.width, + // height: '100%', + // } + // } + // } + // }, + // '@media (max-width: 540px)': { + // '.ss__modal': { + // '.ss__modal__content': { + // '.ss__autocomplete-modal__inner': { + // '.ss__autocomplete': { + // '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { + // '& > *:nth-of-type(n+3)': { + // display: 'block', + // }, + // '& > *:nth-of-type(n+5)': { + // display: 'none', + // }, + // }, + // } + // } + // } + // } + // }, + }); }; export const autocompleteSlideout: ThemeComponent<'autocompleteSlideout', AutocompleteSlideoutProps> = { @@ -14,9 +114,112 @@ export const autocompleteSlideout: ThemeComponent<'autocompleteSlideout', Autoco autocompleteSlideout: { ...(autocompleteSlideoutThemeComponentProps.default?.['autocompleteSlideout'] || {}), themeStyleScript: autocompleteSlideoutStyleScript, + //className: 'ss__autocomplete-slideout__slideout--slim', + layout: [['c1', 'c3']], + column1: { + width: '100%', + layout: ['termsList'], + }, + column3: { + width: '100%', + layout: ['content', 'button.see-more'], + }, + //width: '80vw', + }, + 'autocompleteSlideout searchInput': { + className: 'ss__secondary', + }, + // 'autocompleteSlideout facet searchInput': { + // className: '', + // }, + 'autocompleteSlideout termsList': { + retainHistory: true, + retainTrending: true, + }, + // 'autocompleteSlideout facetPaletteOptions': { + // gridSize: '38px', + // hideLabel: false, + // }, + // 'autocompleteSlideout facetGridOptions': { + // gridSize: '38px', + // }, + 'autocompleteSlideout results': { + rows: 2, + columns: 3, + }, + 'autocompleteSlideout recommendationGrid': { + rows: 2, + columns: 3, + }, + 'autocompleteSlideout button.see-more icon': { + icon: custom.icons.arrowRight, + size: `${custom.sizes.icon12}px`, + }, + }, + mobile: { + ...autocompleteSlideoutThemeComponentProps.mobile, + autocompleteSlideout: { + layout: [['c1', 'c3']], + column1: { + width: '100%', + layout: ['termsList'], + }, + column3: { + width: '100%', + layout: ['content', 'button.see-more'], + }, + }, + 'autocompleteSlideout results': { + rows: 2, + columns: 2, + }, + 'autocompleteSlideout recommendationGrid': { + rows: 2, + columns: 2, + }, + }, + tablet: { + ...autocompleteSlideoutThemeComponentProps.tablet, + autocompleteSlideout: { + layout: [['c1', 'c2']], + column1: { + width: '100%', + layout: ['termsList'], + }, + column2: { + width: '100%', + layout: ['content', 'button.see-more'], + }, + }, + 'autocompleteSlideout results': { + rows: 2, + columns: 3, + }, + 'autocompleteSlideout recommendationGrid': { + rows: 2, + columns: 3, + }, + }, + desktop: { + ...autocompleteSlideoutThemeComponentProps.desktop, + autocompleteSlideout: { + layout: [['c1', 'c3']], + column1: { + width: '100%', + layout: ['termsList'], + }, + column3: { + width: '100%', + layout: ['content', 'button.see-more'], + }, + }, + 'autocompleteSlideout results': { + rows: 2, + columns: 3, + }, + 'autocompleteSlideout recommendationGrid': { + rows: 2, + columns: 3, }, }, - mobile: autocompleteSlideoutThemeComponentProps.mobile, - desktop: autocompleteSlideoutThemeComponentProps.desktop, - tablet: autocompleteSlideoutThemeComponentProps.tablet, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts index ec4160a0b..1891a5982 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/recommendationGrid.ts @@ -12,6 +12,8 @@ const recommendationGridStyleScript = (props: RecommendationGridProps) => { return css({ margin: `${custom.spacing.x8}px 0`, + overflow: 'visible', + maxHeight: 'none', '.ss__recommendation-grid__title': { fontSize: custom.utils.convertPxToEm(22), fontWeight: custom.fonts.weight02, @@ -19,8 +21,21 @@ const recommendationGridStyleScript = (props: RecommendationGridProps) => { textAlign: 'center', margin: `0 0 ${custom.spacing.x4}px 0`, }, + '.ss__recommendation-grid__results': { + overflowX: 'auto', + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + }, [`@media (max-width: ${mobileBp}px)`]: { - '.ss__recommendation__title': { + '.ss__recommendation-grid__title': { textAlign: 'left', }, }, From 055db6f2ae327856c45c0519a5c33ba3691b232b Mon Sep 17 00:00:00 2001 From: adria Date: Tue, 12 Aug 2025 17:09:54 -0600 Subject: [PATCH 038/118] working on swatches and variants --- .../templates/src/components/Result.tsx | 15 +- .../snap-preact-demo/templates/src/index.ts | 19 ++- .../src/themes/pike/components/atoms/image.ts | 2 +- .../pike/components/molecules/swatches.ts | 113 +++++++++++- .../components/molecules/variantSelection.ts | 134 ++++++++++++++- .../pike/components/organisms/results.ts | 6 +- .../templates/autocompleteSlideout.ts | 161 ++++++------------ 7 files changed, 324 insertions(+), 126 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/components/Result.tsx b/packages/snap-preact-demo/templates/src/components/Result.tsx index 2cc0a924e..7cef5dd88 100644 --- a/packages/snap-preact-demo/templates/src/components/Result.tsx +++ b/packages/snap-preact-demo/templates/src/components/Result.tsx @@ -1,9 +1,10 @@ import { h, Fragment } from 'preact'; -import { Price, Image, OverlayBadge, CalloutBadge, Rating } from '@searchspring/snap-preact/components'; +import { Price, Image, OverlayBadge, CalloutBadge, Rating, VariantSelection } from '@searchspring/snap-preact/components'; export const CustomResult = (props) => { const { result, controller } = props; const core = result.mappings.core; + const variants = result.variants; return (
@@ -25,6 +26,18 @@ export const CustomResult = (props) => { /> +
+
+ + {variants?.selections + ? variants.selections.map((selection) => { + return ; + }) + : null} + +
+
+
{core.price < core.msrp ? ( diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index bbeec6fef..d4b136547 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -6,7 +6,7 @@ import type { SnapTemplatesConfig } from '@searchspring/snap-preact'; let config: SnapTemplatesConfig = { config: { - siteId: '8uyt2m', // prvb79 // 8uyt2m + siteId: 'c52bzz', // prvb79 // 8uyt2m language: 'en', currency: 'usd', platform: 'other', @@ -18,7 +18,7 @@ let config: SnapTemplatesConfig = { }, theme: { extends: 'pike', - //resultComponent: 'CustomResult', + resultComponent: 'CustomResult', variables: { breakpoints: { mobile: 767, @@ -73,17 +73,20 @@ let config: SnapTemplatesConfig = { component: 'SearchSnappy', }, ], - // settings: { - // infinite: { - // backfill: 5, - // }, - // }, + settings: { + variants: { + field: 'ss_variants', + }, + // infinite: { + // backfill: 5, + // }, + }, }, autocomplete: { targets: [ { selector: 'input.searchspring-ac', - component: 'AutocompleteSlideout', + component: 'AutocompleteFixed', }, ], }, diff --git a/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts b/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts index a48a80c0d..9432ec4fc 100644 --- a/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts +++ b/packages/snap-preact/components/src/themes/pike/components/atoms/image.ts @@ -29,7 +29,7 @@ const imageStyleScript = (props: ImageProps & { visibility: React.CSSProperties[ maxHeight: '100%', border: 0, objectFit: 'contain', - objectPosition: 'center 0', + objectPosition: 'center center', }, }); }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts index 87b0bd968..2de69796e 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts @@ -1,13 +1,123 @@ import { css } from '@emotion/react'; import type { SwatchesProps } from '../../../../components/Molecules/Swatches'; import { ThemeComponent } from '../../../../providers'; +//import { custom } from '../../custom'; // CSS in JS style script for the Swatches component const swatchesStyleScript = (props: SwatchesProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const buttonSize = 20; - return css({}); + // light colors selector + // const swatchesPrefix = '&.ss__swatches__carousel__swatch'; + // const lightColors = `${swatchesPrefix}--white, ${swatchesPrefix}--ivory, ${swatchesPrefix}--clear, ${swatchesPrefix}--transparent`; + + // carousel styles + const carouselStyles = css({ + margin: 0, + '.ss__carousel': { + '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { + bottom: 0, + width: `${buttonSize}px`, + height: 'auto', + '& > div': { + backgroundColor: 'transparent', + color: variables?.colors?.text, + }, + }, + '.ss__carousel__prev-wrapper': { + '& > div': { + alignItems: 'flex-start', + }, + }, + '.ss__carousel__next-wrapper': { + '& > div': { + alignItems: 'flex-end', + }, + }, + '.swiper-container': { + margin: `0 ${buttonSize + 5}px`, + '& > .swiper-wrapper': { + '& > .swiper-slide': { + // '& > *, .ss__result': { + // padding: 0, + // margin: 0, + // width: 'auto', + // height: '100%', + // }, + }, + }, + }, + '.ss__swatches__carousel__swatch': { + border: 0, + position: 'relative', + '&.ss__swatches__carousel__swatch--selected': { + // '&:before': { + // borderColor: custom.colors.black, + // opacity: 0.3, + // }, + // '&:after': { + // visibility: 'visible', + // opacity: 1, + // }, + // '&[style*="background"]': { + // '&:before': { + // opacity: 0.3, + // } + // }, + // [lightColors]: { + // '&:before': { + // borderColor: custom.colors.black, + // opacity: 0.3, + // }, + // '&:after': { + // } + // }, + }, + // '&[style*="background"]': { + // '&:before': { + // borderColor: custom.colors.black, + // visibility: 'hidden', + // opacity: 0, + // } + // }, + // [lightColors]: { + // '&:before': { + // borderColor: custom.colors.gray02, + // visibility: 'visible', + // opacity: 1, + // } + // }, + // '&, &:before, &:after': { + // boxSizing: 'border-box' + // }, + // '&:before, &:after': { + // content: '""', + // display: 'block', + // width: 'auto', + // height: 'auto', + // position: 'absolute', + // top: 0, + // bottom: 0, + // left: 0, + // right: 0, + // transform: 'none', + // }, + // '&:before': { + // border: `1px solid ${custom.colors.gray02}` + // }, + // '&:after': { + // border: `3px solid ${custom.colors.white}`, + // margin: '1px', + // visibility: 'hidden', + // opacity: 0, + // } + }, + }, + }); + + return carouselStyles; }; // Swatches component props @@ -15,6 +125,7 @@ export const swatches: ThemeComponent<'swatches', SwatchesProps> = { default: { swatches: { themeStyleScript: swatchesStyleScript, + type: 'carousel', // carousel, grid, image }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts index cbc2b1b60..f5dbb9643 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts @@ -1,13 +1,140 @@ import { css } from '@emotion/react'; import type { VariantSelectionProps } from '../../../../components/Molecules/VariantSelection'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; // CSS in JS style script for the Swatches component const variantSelectionStyleScript = (props: VariantSelectionProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - return css({}); + // shared styles for variant menus + // const sharedStyles = css({ + // border: `1px solid ${custom.colors.gray02}`, + // color: variables?.colors?.text, + // backgroundColor: custom.colors.gray01, + // }); + + // dropdown styles + const dropdownStyles = css({ + '.ss__dropdown': { + '.ss__dropdown__button, .ss__dropdown__content': { + border: `1px solid ${custom.colors.gray02}`, + color: variables?.colors?.text, + backgroundColor: custom.colors.gray01, + }, + '.ss__dropdown__button': { + width: 'auto', + display: 'flex', + padding: `0 ${custom.spacing.x2}px`, + textAlign: 'left', + height: `${custom.sizes.height}px`, + lineHeight: `${custom.sizes.height}px`, + '& > *': { + minWidth: '1px', + flex: '0 1 auto', + }, + '.ss__dropdown__button-wrapper': { + flex: '1 1 0%', + gap: `${custom.spacing.x1}px`, + paddingRight: `${custom.spacing.x1}px`, + fontWeight: 'normal', + '.ss__dropdown__button-wrapper__label': { + fontWeight: custom?.fonts?.weight01, + textTransform: 'capitalize', + }, + }, + '.ss__variant-selection__icon': { + transition: 'transform ease .5s', + }, + }, + '.ss__dropdown__content': { + marginTop: `-1px`, + padding: `${custom.spacing.x2}px`, + '.ss__variant-selection__options': { + '&, .ss__variant-selection__option': { + listStyle: 'none', + padding: 0, + margin: 0, + }, + '.ss__variant-selection__option': { + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-of-type': { + marginBottom: '0', + }, + '&:hover': { + fontWeight: 'normal', + }, + }, + '.ss__variant-selection__option--selected': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, + '.ss__variant-selection__option--unavailable': { + color: lightGray, + cursor: 'not-allowed', + }, + }, + }, + }, + '.ss__dropdown--open': { + '.ss__dropdown__button': { + '.ss__variant-selection__icon': { + transform: 'rotate(180deg)', + }, + }, + }, + }); + + // dropdown styles + const listStyles = css({ + '.ss__list': { + '.ss__list__title, .ss__list__options, .ss__list__options .ss__list__option': { + width: '100%', + color: variables?.colors?.text, + }, + '.ss__list__title': { + textTransform: 'capitalize', + }, + '.ss__list__options': { + '.ss__list__option': { + label: { + color: 'inherit', + }, + }, + '.ss__list__option--selected': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, + '.ss__list__option--unavailable': { + color: lightGray, + cursor: 'not-allowed', + textDecoration: 'line-through', + }, + }, + }, + }); + + // swatches syles + const swatchesStyles = css({ + // minWidth: '1px', + // maxWidth: '100%', + // '.ss__swatches, .ss__carousel, .swiper, .swiper-wrapper': { + // minWidth: '1px', + // maxWidth: '100%' + // } + }); + + // return variant selection styles + if (props?.type == 'list') { + return listStyles; + } else if (props?.type == 'swatches') { + return swatchesStyles; + } + { + return dropdownStyles; + } }; // VariantSelection component props @@ -15,6 +142,11 @@ export const variantSelection: ThemeComponent<'variantSelection', VariantSelecti default: { variantSelection: { themeStyleScript: variantSelectionStyleScript, + type: 'swatches', + }, + 'variantSelection dropdown icon': { + size: `${custom.sizes.icon12}px`, + icon: custom.icons.arrowDown, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts b/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts index 68b2b9b12..fabe87ac2 100644 --- a/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts +++ b/packages/snap-preact/components/src/themes/pike/components/organisms/results.ts @@ -8,7 +8,11 @@ const resultsStyleScript = (props: ResultsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - return css({}); + return css({ + '& > *': { + minWidth: '1px', + }, + }); }; // Results component props diff --git a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts index 20a2b3bd5..ff8ee83c3 100644 --- a/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts +++ b/packages/snap-preact/components/src/themes/pike/components/templates/autocompleteSlideout.ts @@ -8,103 +8,40 @@ import { custom } from '../../custom'; const autocompleteSlideoutStyleScript = (props: AutocompleteSlideoutProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - // const tabletBp = variables?.breakpoints?.tablet || 1024; - // const mobileBp = variables?.breakpoints?.mobile || 767; return css({ - // '.ss__modal': { - // '&, .ss__modal__content': { - // height: '100%', - // }, - // '.ss__modal__content': { - // backgroundColor: 'transparent', - // '&, .ss__autocomplete-modal__inner': { - // position: 'static', - // display: 'flex', - // flexFlow: 'column nowrap', - // justifyContent: 'center', - // }, - // '.ss__autocomplete-modal__inner': { - // width: props?.width, - // maxHeight: 'none', - // height: '80vh', - // overflow: 'hidden', - // '& > .ss__search-input, .ss__autocomplete': { - // minHeight: '1px', - // minWidth: '1px', - // }, - // '& > .ss__search-input': { - // flex: '0 1 auto', - // height: '40px', - // margin: 0, - // '.ss__search-input__icons .ss__button, .ss__search-input__button--close-search-button': { - // width: '40px', - // padding: 0, - // }, - // }, - // '.ss__autocomplete': { - // flex: '1 1 0%', - // overflowY: 'auto', - // borderWidth: 0, - // '&::-webkit-scrollbar': { - // width: '8px', - // height: '8px', - // }, - // '&::-webkit-scrollbar-track': { - // backgroundColor: custom.colors.gray01, - // }, - // '&::-webkit-scrollbar-thumb': { - // backgroundColor: custom.colors.gray02, - // }, - // '.ss__autocomplete__content__results .ss__results': { - // maxHeight: 'none', - // overflow: 'visible', - // } - // }, - // }, - // }, - // }, - // [`@media (max-width: ${tabletBp}px)`]: { - // '.ss__modal': { - // '.ss__modal__content': { - // '.ss__autocomplete-modal__inner': { - // '.ss__autocomplete': { - // '& > .ss__autocomplete__row': { - // alignContent: 'flex-start', - // } - // } - // } - // } - // } - // }, - // [`@media (max-width: ${mobileBp}px)`]: { - // '.ss__modal': { - // '.ss__modal__content': { - // '.ss__autocomplete-modal__inner': { - // width: props?.width, - // height: '100%', - // } - // } - // } - // }, - // '@media (max-width: 540px)': { - // '.ss__modal': { - // '.ss__modal__content': { - // '.ss__autocomplete-modal__inner': { - // '.ss__autocomplete': { - // '.ss__autocomplete__content__results .ss__results, .ss__autocomplete__content__no-results .ss__recommendation-grid__results': { - // '& > *:nth-of-type(n+3)': { - // display: 'block', - // }, - // '& > *:nth-of-type(n+5)': { - // display: 'none', - // }, - // }, - // } - // } - // } - // } - // }, + border: 0, + padding: `${custom.spacing.x4}px`, + '&::-webkit-scrollbar': { + width: '8px', + height: '8px', + }, + '&::-webkit-scrollbar-track': { + backgroundColor: custom.colors.gray01, + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: custom.colors.gray02, + }, + '& > *': { + '& > .ss__search-input': { + height: '40px', + margin: `0 0 ${custom.spacing.x1}px 0`, + '.ss__search-input__icons .ss__button, .ss__search-input__button--close-search-button': { + width: '40px', + padding: 0, + }, + }, + '.ss__autocomplete': { + borderWidth: 0, + '& > .ss__autocomplete__row .ss__autocomplete__column': { + padding: `${custom.spacing.x4}px 0`, + }, + '.ss__autocomplete__content__results .ss__results': { + maxHeight: 'none', + overflow: 'visible', + }, + }, + }, }); }; @@ -114,35 +51,33 @@ export const autocompleteSlideout: ThemeComponent<'autocompleteSlideout', Autoco autocompleteSlideout: { ...(autocompleteSlideoutThemeComponentProps.default?.['autocompleteSlideout'] || {}), themeStyleScript: autocompleteSlideoutStyleScript, - //className: 'ss__autocomplete-slideout__slideout--slim', - layout: [['c1', 'c3']], + layout: [['c1', 'c2']], column1: { width: '100%', layout: ['termsList'], }, - column3: { + column2: { width: '100%', layout: ['content', 'button.see-more'], }, - //width: '80vw', }, 'autocompleteSlideout searchInput': { className: 'ss__secondary', }, - // 'autocompleteSlideout facet searchInput': { - // className: '', - // }, + 'autocompleteSlideout facet searchInput': { + className: '', + }, 'autocompleteSlideout termsList': { retainHistory: true, retainTrending: true, }, - // 'autocompleteSlideout facetPaletteOptions': { - // gridSize: '38px', - // hideLabel: false, - // }, - // 'autocompleteSlideout facetGridOptions': { - // gridSize: '38px', - // }, + 'autocompleteSlideout facetPaletteOptions': { + gridSize: '38px', + hideLabel: false, + }, + 'autocompleteSlideout facetGridOptions': { + gridSize: '38px', + }, 'autocompleteSlideout results': { rows: 2, columns: 3, @@ -159,12 +94,12 @@ export const autocompleteSlideout: ThemeComponent<'autocompleteSlideout', Autoco mobile: { ...autocompleteSlideoutThemeComponentProps.mobile, autocompleteSlideout: { - layout: [['c1', 'c3']], + layout: [['c1', 'c2']], column1: { width: '100%', layout: ['termsList'], }, - column3: { + column2: { width: '100%', layout: ['content', 'button.see-more'], }, @@ -203,12 +138,12 @@ export const autocompleteSlideout: ThemeComponent<'autocompleteSlideout', Autoco desktop: { ...autocompleteSlideoutThemeComponentProps.desktop, autocompleteSlideout: { - layout: [['c1', 'c3']], + layout: [['c1', 'c2']], column1: { width: '100%', layout: ['termsList'], }, - column3: { + column2: { width: '100%', layout: ['content', 'button.see-more'], }, From b8b102b5bc0e9c3949fa22e5a1ed8dea35fb1e10 Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 13 Aug 2025 07:45:05 -0600 Subject: [PATCH 039/118] swatches --- .../components/molecules/facetGridOptions.ts | 2 +- .../molecules/facetPaletteOptions.ts | 98 ++++----- .../themes/pike/components/molecules/grid.ts | 83 +++++++- .../pike/components/molecules/swatches.ts | 188 +++++++++-------- .../components/molecules/variantSelection.ts | 197 +++++++++--------- 5 files changed, 329 insertions(+), 239 deletions(-) diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 16b4f18dc..302972bc0 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -28,6 +28,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { color: variables?.colors?.text, '&, &:before, .ss__facet-grid-options__option__value': { display: 'block', + boxSizing: 'border-box', }, '&:before, .ss__facet-grid-options__option__value': { position: 'absolute', @@ -39,7 +40,6 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { width: '100%', height: '100%', border: `1px solid ${custom.colors.gray02}`, - boxSizing: 'border-box', }, '.ss__facet-grid-options__option__value': { top: '50%', diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index 0effc07db..b36ef3e68 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -16,15 +16,14 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { const paletteRadius = radius ? `${radius}${radiusUnit}` : `0`; // light colors selector - const palettePrefix = '.ss__facet-palette-options__option__palette'; + const palettePrefix = '&.ss__facet-palette-options__option__palette'; const lightColors = `${palettePrefix}--white, ${palettePrefix}--ivory, ${palettePrefix}--clear, ${palettePrefix}--transparent`; - // light border styles styles - const lightBorderStyles = css({ - borderColor: custom.colors.gray02, - opacity: 1, - visibility: 'visible', - }); + // determine inner border width + let innerBorder = 5; + if (props?.layout == 'list') { + innerBorder = hasCheckbox ? 2 : 3; + } // shared palette styles const sharedStyles = css({ @@ -39,42 +38,37 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { }, }, '.ss__facet-palette-options__option__wrapper': { - '&:before, .ss__facet-palette-options__option__palette, .ss__facet-palette-options__option__palette:before': { - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - margin: 'auto', - borderRadius: paletteRadius, - }, - '&:before, .ss__facet-palette-options__option__palette:before': { - content: '""', - display: 'block', - opacity: 0, - visibility: 'hidden', - }, - '&:before': { - zIndex: 2, - border: `1px solid ${custom.colors.black}`, - }, '.ss__facet-palette-options__option__palette': { - zIndex: 1, border: 0, padding: 0, - '&:before': { - border: `1px solid ${custom.colors.white}`, - }, - '&:not([style]):before': { - ...lightBorderStyles, + '&, &:before, &:after': { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + borderRadius: paletteRadius, + boxSizing: 'border-box', }, - '.ss__icon': { - display: 'none', + '&:before, &:after': { + content: '""', + display: 'block', + opacity: 0, + visibility: 'hidden', }, - }, - [`${lightColors}`]: { '&:before': { - ...lightBorderStyles, + border: `1px solid ${custom.colors.black}`, + }, + '&:after': { + border: `${innerBorder}px solid ${custom.colors.white}`, + margin: '1px', + }, + [`&:not([style]), ${lightColors}`]: { + '&:before': { + borderColor: custom.colors.gray02, + opacity: 1, + visibility: 'visible', + }, }, }, }, @@ -90,26 +84,24 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { fontWeight: custom.fonts.weight01, color: variables?.colors?.primary, '.ss__facet-palette-options__option__wrapper': { - '&:before, .ss__facet-palette-options__option__palette:before': { - visibility: 'visible', - }, - '&:before': { - opacity: 0.3, - }, '.ss__facet-palette-options__option__palette': { + '&:before, &:after': { + visibility: 'visible', + }, '&:before': { - opacity: 1, - margin: '1px', - borderWidth: props?.layout == 'list' ? `${hasCheckbox ? 2 : 3}px` : `5px`, - borderRadius: radius ? `calc(${paletteRadius} - 1px)` : '', + opacity: 0.3, }, - '&:not([style]):before': { - borderColor: custom.colors.gray01, + '&:after': { + opacity: 1, }, - }, - [`${lightColors}`]: { - '&:before': { - borderColor: custom.colors.gray01, + [`&:not([style]), ${lightColors}`]: { + '&:before': { + borderColor: custom.colors.black, + opacity: 0.3, + }, + '&:after': { + borderColor: custom.colors.gray01, + }, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts index b051f0216..52e3859fe 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts @@ -1,13 +1,89 @@ import { css } from '@emotion/react'; import type { GridProps } from '../../../../components/Molecules/Grid'; import { ThemeComponent } from '../../../../providers'; +import { custom } from '../../custom'; +import Color from 'color'; // CSS in JS style script for the Grid component const gridStyleScript = (props: Partial) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const activeColor = new Color(variables?.colors?.primary); + const fontColor = activeColor.isDark() || activeColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); + const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); - return css({}); + return css({ + '.ss__grid__title': { + margin: `0 0 ${custom.spacing.x1}px 0`, + fontSize: custom.utils.convertPxToEm(14), + fontWeight: custom.fonts.weight02, + lineHeight: 1, + }, + '.ss__grid__options': { + gridTemplateColumns: 'repeat(auto-fill, minmax(52px, 1fr))', + gap: props?.gapSize ? props.gapSize : custom.spacing.x1, + alignItems: 'center', + '&:after': { + display: 'none', + }, + '.ss__grid__option': { + position: 'relative', + height: 0, + paddingBottom: '100%', + color: variables?.colors?.text, + '&, &.ss__grid__option--selected': { + border: 0, + }, + '&, &:after, .ss__grid__option__label': { + display: 'block', + boxSizing: 'border-box', + }, + '&:after, .ss__grid__option__label': { + position: 'absolute', + margin: 'auto', + }, + '&:after': { + content: '""', + display: 'block', + width: '100%', + height: '100%', + border: `1px solid ${custom.colors.gray02}`, + }, + '.ss__grid__option__label': { + top: '50%', + left: 0, + right: 0, + zIndex: 2, + transform: 'translateY(-50%)', + maxWidth: `calc(100% - ${custom.spacing.x2}px)`, + maxHeight: `calc(100% - ${custom.spacing.x2}px)`, + overflow: 'hidden', + textAlign: 'center', + '&, &.ss__grid__option__label--smaller': { + fontSize: custom.utils.convertPxToEm(12), + }, + }, + '&:not([style])': { + '&:after': { + backgroundColor: custom.colors.gray01, + }, + }, + }, + '.ss__grid__option--selected': { + fontWeight: custom.fonts.weight01, + '&:after': { + borderColor: darkGray, + }, + '&:not([style])': { + color: fontColor.hex(), + '&:after': { + backgroundColor: activeColor.hex(), + borderColor: activeColor.hex(), + }, + }, + }, + }, + }); }; // Grid component props @@ -15,6 +91,11 @@ export const grid: ThemeComponent<'grid', GridProps> = { default: { grid: { themeStyleScript: gridStyleScript, + //gridSize: '52px', + gapSize: `${custom.spacing.x1}px`, + titleText: 'this is title', + hideLabels: false, + hideShowLess: false, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts index 2de69796e..a02c8d0b8 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts @@ -1,123 +1,117 @@ import { css } from '@emotion/react'; import type { SwatchesProps } from '../../../../components/Molecules/Swatches'; import { ThemeComponent } from '../../../../providers'; -//import { custom } from '../../custom'; +import { custom } from '../../custom'; + +// Swatch carousel sizes and spacing +const swatchSize = 30; +const swatchSpacing = custom.spacing.x1; // CSS in JS style script for the Swatches component const swatchesStyleScript = (props: SwatchesProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; - const buttonSize = 20; // light colors selector - // const swatchesPrefix = '&.ss__swatches__carousel__swatch'; - // const lightColors = `${swatchesPrefix}--white, ${swatchesPrefix}--ivory, ${swatchesPrefix}--clear, ${swatchesPrefix}--transparent`; + const swatchesPrefix = '&.ss__swatches__carousel__swatch'; + const lightColors = `${swatchesPrefix}--white, ${swatchesPrefix}--ivory, ${swatchesPrefix}--clear, ${swatchesPrefix}--transparent`; // carousel styles const carouselStyles = css({ margin: 0, '.ss__carousel': { + '& > div': { + minWidth: '1px', + flex: '0 1 auto', + }, '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { + position: 'static', bottom: 0, - width: `${buttonSize}px`, - height: 'auto', - '& > div': { - backgroundColor: 'transparent', - color: variables?.colors?.text, - }, + width: `${swatchSize}px`, + height: `${swatchSize}px`, }, '.ss__carousel__prev-wrapper': { - '& > div': { - alignItems: 'flex-start', - }, + margin: `0 ${swatchSpacing}px 0 0`, }, '.ss__carousel__next-wrapper': { - '& > div': { - alignItems: 'flex-end', - }, + margin: `0 0 0 ${swatchSpacing}px`, }, '.swiper-container': { - margin: `0 ${buttonSize + 5}px`, - '& > .swiper-wrapper': { - '& > .swiper-slide': { - // '& > *, .ss__result': { - // padding: 0, - // margin: 0, - // width: 'auto', - // height: '100%', - // }, - }, - }, + maxWidth: `calc(100% - ${swatchSize * 2 + swatchSpacing * 2}px)`, + padding: '0 2px 0 0', }, - '.ss__swatches__carousel__swatch': { + '.swiper > .swiper-wrapper > .swiper-slide > *, .ss__swatches__carousel__swatch': { + width: '100%', + height: `${swatchSize}px`, + lineHeight: 0, border: 0, + }, + '.ss__swatches__carousel__swatch': { position: 'relative', - '&.ss__swatches__carousel__swatch--selected': { - // '&:before': { - // borderColor: custom.colors.black, - // opacity: 0.3, - // }, - // '&:after': { - // visibility: 'visible', - // opacity: 1, - // }, - // '&[style*="background"]': { - // '&:before': { - // opacity: 0.3, - // } - // }, - // [lightColors]: { - // '&:before': { - // borderColor: custom.colors.black, - // opacity: 0.3, - // }, - // '&:after': { - // } - // }, + '&, &:before, &:after': { + boxSizing: 'border-box', + }, + '&:before, &:after': { + content: '""', + display: 'block', + width: 'auto', + height: 'auto', + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + transform: 'none', + opacity: 0, + visibility: 'hidden', + }, + '&:before': { + border: `1px solid ${custom.colors.black}`, + }, + '&:after': { + border: `3px solid ${custom.colors.white}`, + margin: '1px', + }, + [`&:not([style]), ${lightColors}`]: { + '&:before': { + borderColor: custom.colors.gray02, + opacity: 1, + visibility: 'visible', + }, + }, + }, + '.ss__swatches__carousel__swatch--selected': { + '&:before, &:after': { + visibility: 'visible', + }, + '&:before': { + opacity: 0.3, + }, + '&:after': { + opacity: 1, + }, + [`&:not([style]), ${lightColors}`]: { + '&:before': { + borderColor: custom.colors.black, + opacity: 0.3, + }, + '&:after': { + borderColor: custom.colors.gray01, + }, }, - // '&[style*="background"]': { - // '&:before': { - // borderColor: custom.colors.black, - // visibility: 'hidden', - // opacity: 0, - // } - // }, - // [lightColors]: { - // '&:before': { - // borderColor: custom.colors.gray02, - // visibility: 'visible', - // opacity: 1, - // } - // }, - // '&, &:before, &:after': { - // boxSizing: 'border-box' - // }, - // '&:before, &:after': { - // content: '""', - // display: 'block', - // width: 'auto', - // height: 'auto', - // position: 'absolute', - // top: 0, - // bottom: 0, - // left: 0, - // right: 0, - // transform: 'none', - // }, - // '&:before': { - // border: `1px solid ${custom.colors.gray02}` - // }, - // '&:after': { - // border: `3px solid ${custom.colors.white}`, - // margin: '1px', - // visibility: 'hidden', - // opacity: 0, - // } }, }, }); - return carouselStyles; + // grid styles + const gridStyles = css({}); + + // return variant selection styles + if (props?.type == 'grid') { + return gridStyles; + } else { + return carouselStyles; + } }; // Swatches component props @@ -125,7 +119,21 @@ export const swatches: ThemeComponent<'swatches', SwatchesProps> = { default: { swatches: { themeStyleScript: swatchesStyleScript, - type: 'carousel', // carousel, grid, image + type: 'grid', // carousel, grid, image + }, + 'swatches carousel': { + autoAdjustSlides: false, + centerInsufficientSlides: false, + slidesPerView: 4, + slidesPerGroup: 4, + spaceBetween: `${swatchSpacing}px`, + }, + }, + desktop: { + 'swatches carousel': { + slidesPerView: 3, + slidesPerGroup: 3, + spaceBetween: `${swatchSpacing}px`, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts index f5dbb9643..8233bb1c4 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts @@ -10,129 +10,138 @@ const variantSelectionStyleScript = (props: VariantSelectionProps) => { const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); // shared styles for variant menus - // const sharedStyles = css({ - // border: `1px solid ${custom.colors.gray02}`, - // color: variables?.colors?.text, - // backgroundColor: custom.colors.gray01, - // }); + const sharedStyles = css({ + margin: `0 0 ${custom.spacing.x2}px 0`, + '&:last-of-type': { + marginBottom: 0, + }, + }); // dropdown styles - const dropdownStyles = css({ - '.ss__dropdown': { - '.ss__dropdown__button, .ss__dropdown__content': { - border: `1px solid ${custom.colors.gray02}`, - color: variables?.colors?.text, - backgroundColor: custom.colors.gray01, - }, - '.ss__dropdown__button': { - width: 'auto', - display: 'flex', - padding: `0 ${custom.spacing.x2}px`, - textAlign: 'left', - height: `${custom.sizes.height}px`, - lineHeight: `${custom.sizes.height}px`, - '& > *': { - minWidth: '1px', - flex: '0 1 auto', + const dropdownStyles = css([ + sharedStyles, + { + '.ss__dropdown': { + '.ss__dropdown__button, .ss__dropdown__content': { + border: `1px solid ${custom.colors.gray02}`, + color: variables?.colors?.text, + backgroundColor: custom.colors.gray01, }, - '.ss__dropdown__button-wrapper': { - flex: '1 1 0%', - gap: `${custom.spacing.x1}px`, - paddingRight: `${custom.spacing.x1}px`, - fontWeight: 'normal', - '.ss__dropdown__button-wrapper__label': { - fontWeight: custom?.fonts?.weight01, - textTransform: 'capitalize', + '.ss__dropdown__button': { + width: 'auto', + display: 'flex', + padding: `0 ${custom.spacing.x2}px`, + textAlign: 'left', + height: `${custom.sizes.height}px`, + lineHeight: `${custom.sizes.height}px`, + '& > *': { + minWidth: '1px', + flex: '0 1 auto', }, - }, - '.ss__variant-selection__icon': { - transition: 'transform ease .5s', - }, - }, - '.ss__dropdown__content': { - marginTop: `-1px`, - padding: `${custom.spacing.x2}px`, - '.ss__variant-selection__options': { - '&, .ss__variant-selection__option': { - listStyle: 'none', - padding: 0, - margin: 0, - }, - '.ss__variant-selection__option': { - margin: `0 0 ${custom.spacing.x1}px 0`, - '&:last-of-type': { - marginBottom: '0', - }, - '&:hover': { - fontWeight: 'normal', + '.ss__dropdown__button-wrapper': { + flex: '1 1 0%', + gap: `${custom.spacing.x1}px`, + paddingRight: `${custom.spacing.x1}px`, + fontWeight: 'normal', + '.ss__dropdown__button-wrapper__label': { + fontWeight: custom?.fonts?.weight01, + textTransform: 'capitalize', }, }, - '.ss__variant-selection__option--selected': { - fontWeight: custom.fonts.weight01, - color: variables?.colors?.primary, + '.ss__variant-selection__icon': { + transition: 'transform ease .5s', }, - '.ss__variant-selection__option--unavailable': { - color: lightGray, - cursor: 'not-allowed', + }, + '.ss__dropdown__content': { + marginTop: `-1px`, + padding: `${custom.spacing.x2}px`, + '.ss__variant-selection__options': { + '&, .ss__variant-selection__option': { + listStyle: 'none', + padding: 0, + margin: 0, + }, + '.ss__variant-selection__option': { + margin: `0 0 ${custom.spacing.x1}px 0`, + '&:last-of-type': { + marginBottom: '0', + }, + '&:hover': { + fontWeight: 'normal', + }, + }, + '.ss__variant-selection__option--selected': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, + '.ss__variant-selection__option--unavailable': { + color: lightGray, + cursor: 'not-allowed', + }, }, }, }, - }, - '.ss__dropdown--open': { - '.ss__dropdown__button': { - '.ss__variant-selection__icon': { - transform: 'rotate(180deg)', + '.ss__dropdown--open': { + '.ss__dropdown__button': { + '.ss__variant-selection__icon': { + transform: 'rotate(180deg)', + }, }, }, }, - }); + ]); // dropdown styles - const listStyles = css({ - '.ss__list': { - '.ss__list__title, .ss__list__options, .ss__list__options .ss__list__option': { - width: '100%', - color: variables?.colors?.text, - }, - '.ss__list__title': { - textTransform: 'capitalize', - }, - '.ss__list__options': { - '.ss__list__option': { - label: { - color: 'inherit', - }, + const listStyles = css([ + sharedStyles, + { + '.ss__list': { + '.ss__list__title, .ss__list__options, .ss__list__options .ss__list__option': { + width: '100%', + color: variables?.colors?.text, }, - '.ss__list__option--selected': { - fontWeight: custom.fonts.weight01, - color: variables?.colors?.primary, + '.ss__list__title': { + textTransform: 'capitalize', }, - '.ss__list__option--unavailable': { - color: lightGray, - cursor: 'not-allowed', - textDecoration: 'line-through', + '.ss__list__options': { + '.ss__list__option': { + label: { + color: 'inherit', + }, + }, + '.ss__list__option--selected': { + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, + }, + '.ss__list__option--unavailable': { + color: lightGray, + cursor: 'not-allowed', + textDecoration: 'line-through', + }, }, }, }, - }); + ]); // swatches syles - const swatchesStyles = css({ - // minWidth: '1px', - // maxWidth: '100%', - // '.ss__swatches, .ss__carousel, .swiper, .swiper-wrapper': { - // minWidth: '1px', - // maxWidth: '100%' + const swatchesStyles = css([ + sharedStyles, + // { + // // minWidth: '1px', + // // maxWidth: '100%', + // // '.ss__swatches, .ss__carousel, .swiper, .swiper-wrapper': { + // // minWidth: '1px', + // // maxWidth: '100%' + // // } // } - }); + ]); // return variant selection styles if (props?.type == 'list') { return listStyles; } else if (props?.type == 'swatches') { return swatchesStyles; - } - { + } else { return dropdownStyles; } }; From 8d7b97a41d77c8c97651b9af420982c2c96bf8c0 Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 13 Aug 2025 17:27:48 -0600 Subject: [PATCH 040/118] variant selections --- .../snap-preact-demo/templates/src/index.ts | 4 +- .../components/molecules/facetGridOptions.ts | 41 ++-- .../molecules/facetPaletteOptions.ts | 20 +- .../themes/pike/components/molecules/grid.ts | 105 +++++++-- .../pike/components/molecules/swatches.ts | 200 +++++++++++------- .../components/molecules/variantSelection.ts | 15 +- 6 files changed, 252 insertions(+), 133 deletions(-) diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index d4b136547..b8353c664 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -6,7 +6,7 @@ import type { SnapTemplatesConfig } from '@searchspring/snap-preact'; let config: SnapTemplatesConfig = { config: { - siteId: 'c52bzz', // prvb79 // 8uyt2m + siteId: '8uyt2m', // prvb79 // 8uyt2m // c52bzz (for variant options) language: 'en', currency: 'usd', platform: 'other', @@ -18,7 +18,7 @@ let config: SnapTemplatesConfig = { }, theme: { extends: 'pike', - resultComponent: 'CustomResult', + //resultComponent: 'CustomResult', variables: { breakpoints: { mobile: 767, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts index 302972bc0..4f4d6a815 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetGridOptions.ts @@ -11,46 +11,41 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { const isSecondary = props?.className?.includes('secondary') ? true : false; const activeColor = new Color(variables?.colors?.primary); const fontColor = activeColor.isDark() || activeColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); + const gridSize = props?.gridSize ? props.gridSize : '52px'; // shared grid styles const sharedStyles = css({ - gridTemplateColumns: `repeat(auto-fill, minmax(${props?.gridSize ? props.gridSize : '52px'}, 1fr))`, + gridTemplateColumns: `repeat(auto-fill, minmax(${gridSize}, 1fr))`, gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', - '&:before': { - display: 'none', - }, '.ss__facet-grid-options__option': { position: 'relative', - height: 0, - paddingBottom: '100%', + height: '100%', + aspectRatio: 1, border: 0, color: variables?.colors?.text, - '&, &:before, .ss__facet-grid-options__option__value': { - display: 'block', + '&, &:after, .ss__facet-grid-options__option__value': { boxSizing: 'border-box', }, - '&:before, .ss__facet-grid-options__option__value': { - position: 'absolute', - margin: 'auto', + '&:after, .ss__facet-grid-options__option__value': { + display: 'block', }, - '&:before': { + '&:after': { content: '""', - display: 'block', - width: '100%', - height: '100%', + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + zIndex: 1, border: `1px solid ${custom.colors.gray02}`, }, '.ss__facet-grid-options__option__value': { - top: '50%', - left: 0, - right: 0, + position: 'relative', zIndex: 2, - transform: 'translateY(-50%)', maxWidth: `calc(100% - ${custom.spacing.x2}px)`, maxHeight: `calc(100% - ${custom.spacing.x2}px)`, overflow: 'hidden', - textAlign: 'center', '&, &.ss__facet-grid-options__option__value--smaller': { fontSize: custom.utils.convertPxToEm(12), }, @@ -59,7 +54,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { '.ss__facet-grid-options__option.ss__facet-grid-options__option--filtered': { fontWeight: custom.fonts.weight01, color: fontColor.hex(), - '&:before': { + '&:after': { backgroundColor: activeColor.hex(), borderColor: activeColor.hex(), }, @@ -71,7 +66,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { sharedStyles, { '.ss__facet-grid-options__option': { - '&:before': { + '&:after': { backgroundColor: custom.colors.gray01, }, }, @@ -83,7 +78,7 @@ const facetGridOptionsStyleScript = (props: FacetGridOptionsProps) => { sharedStyles, { '.ss__facet-grid-options__option': { - '&:before': { + '&:after': { backgroundColor: custom.colors.white, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts index b36ef3e68..21bdd1def 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/facetPaletteOptions.ts @@ -7,6 +7,7 @@ import { custom } from '../../custom'; const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); const hasCheckbox = !props?.hideCheckbox ? true : false; @@ -38,6 +39,7 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { }, }, '.ss__facet-palette-options__option__wrapper': { + overflow: 'hidden', '.ss__facet-palette-options__option__palette': { border: 0, padding: 0, @@ -70,6 +72,15 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { visibility: 'visible', }, }, + '&[style*="url"]': { + backgroundRepeat: 'no-repeat !important', + backgroundSize: 'cover !important', + backgroundPosition: 'center !important', + transition: 'transform 0.5s ease-in-out', + '&:hover': { + transform: 'scale(1.5)', + }, + }, }, }, '.ss__facet-palette-options__option__value__count': { @@ -96,13 +107,18 @@ const facetPaletteStyleScript = (props: FacetPaletteOptionsProps) => { }, [`&:not([style]), ${lightColors}`]: { '&:before': { - borderColor: custom.colors.black, - opacity: 0.3, + borderColor: darkGray, + opacity: 1, }, '&:after': { borderColor: custom.colors.gray01, }, }, + '&[style*="url"]': { + '&:hover': { + transform: 'none', + }, + }, }, }, }, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts index 52e3859fe..3f3750a9b 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/grid.ts @@ -4,6 +4,9 @@ import { ThemeComponent } from '../../../../providers'; import { custom } from '../../custom'; import Color from 'color'; +// grid size +const gridSize = 52; + // CSS in JS style script for the Grid component const gridStyleScript = (props: Partial) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -12,6 +15,10 @@ const gridStyleScript = (props: Partial) => { const fontColor = activeColor.isDark() || activeColor.hex().toLowerCase() == '#00aeef' ? Color(custom.colors.white) : Color(custom.colors.black); const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); + // light colors selector + const gridPrefix = '&.ss__grid__option-value'; + const lightColors = `${gridPrefix}--white, ${gridPrefix}--ivory, ${gridPrefix}--clear, ${gridPrefix}--transparent`; + return css({ '.ss__grid__title': { margin: `0 0 ${custom.spacing.x1}px 0`, @@ -20,7 +27,7 @@ const gridStyleScript = (props: Partial) => { lineHeight: 1, }, '.ss__grid__options': { - gridTemplateColumns: 'repeat(auto-fill, minmax(52px, 1fr))', + gridTemplateColumns: `repeat(auto-fill, minmax(${gridSize}px, 1fr))`, gap: props?.gapSize ? props.gapSize : custom.spacing.x1, alignItems: 'center', '&:after': { @@ -28,59 +35,116 @@ const gridStyleScript = (props: Partial) => { }, '.ss__grid__option': { position: 'relative', - height: 0, - paddingBottom: '100%', + height: '100%', + aspectRatio: 1, color: variables?.colors?.text, + overflow: 'hidden', '&, &.ss__grid__option--selected': { border: 0, }, - '&, &:after, .ss__grid__option__label': { - display: 'block', + '&, &:after, .ss__grid__option__label, .ss__grid__show-more': { boxSizing: 'border-box', }, - '&:after, .ss__grid__option__label': { - position: 'absolute', - margin: 'auto', + '&:after, .ss__grid__option__label, .ss__grid__show-more': { + display: 'block', }, '&:after': { content: '""', - display: 'block', - width: '100%', - height: '100%', - border: `1px solid ${custom.colors.gray02}`, - }, - '.ss__grid__option__label': { - top: '50%', + position: 'absolute', + top: 0, + bottom: 0, left: 0, right: 0, + zIndex: 1, + border: `1px solid ${custom.colors.gray02}`, + opacity: 0, + }, + '.ss__grid__option__label, .ss__grid__show-more': { + position: 'relative', zIndex: 2, - transform: 'translateY(-50%)', maxWidth: `calc(100% - ${custom.spacing.x2}px)`, maxHeight: `calc(100% - ${custom.spacing.x2}px)`, overflow: 'hidden', - textAlign: 'center', '&, &.ss__grid__option__label--smaller': { fontSize: custom.utils.convertPxToEm(12), }, }, + [`&:not([style]), ${lightColors}`]: { + '&:after': { + opacity: 1, + }, + }, '&:not([style])': { '&:after': { backgroundColor: custom.colors.gray01, }, }, + '&[style*="url"]': { + backgroundRepeat: 'no-repeat !important', + backgroundSize: 'cover !important', + backgroundPosition: 'center !important', + transition: 'transform 0.5s ease-in-out', + '&:hover': { + transform: 'scale(1.5)', + }, + }, }, '.ss__grid__option--selected': { fontWeight: custom.fonts.weight01, + color: fontColor.hex(), '&:after': { - borderColor: darkGray, + borderColor: custom.colors.black, + opacity: 0.3, }, '&:not([style])': { - color: fontColor.hex(), '&:after': { backgroundColor: activeColor.hex(), borderColor: activeColor.hex(), }, }, + [lightColors]: { + color: variables?.colors?.text, + '&:after': { + borderColor: darkGray, + }, + }, + '&[style*="url"]': { + '&:hover': { + transform: 'none', + }, + }, + }, + '.ss__grid__option.ss__grid__option--unavailable': { + opacity: 1, + cursor: 'not-allowed', + pointerEvents: 'none', + '&:before': { + top: 0, + bottom: 0, + zIndex: 3, + margin: 'auto', + borderColor: darkGray, + outline: 0, + }, + }, + '.ss__grid__show-more-wrapper': { + alignItems: 'flex-start', + '&:after': { + display: 'none', + }, + }, + }, + '.ss__grid__show-more-wrapper': { + '&:not(.ss__grid__option)': { + margin: `${custom.spacing.x2}px 0 0 0`, + }, + '&, .ss__grid__show-more': { + cursor: 'pointer', + }, + '.ss__grid__show-more': { + fontSize: custom.utils.convertPxToEm(12), + fontWeight: custom.fonts.weight01, + color: variables?.colors?.primary, }, }, }); @@ -91,11 +155,8 @@ export const grid: ThemeComponent<'grid', GridProps> = { default: { grid: { themeStyleScript: gridStyleScript, - //gridSize: '52px', gapSize: `${custom.spacing.x1}px`, - titleText: 'this is title', hideLabels: false, - hideShowLess: false, }, }, }; diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts index a02c8d0b8..de2d207d5 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/swatches.ts @@ -11,102 +11,160 @@ const swatchSpacing = custom.spacing.x1; const swatchesStyleScript = (props: SwatchesProps) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const variables = props?.theme?.variables; + const darkGray = custom.utils.darkenColor(custom.colors.gray02, 0.075); // light colors selector const swatchesPrefix = '&.ss__swatches__carousel__swatch'; const lightColors = `${swatchesPrefix}--white, ${swatchesPrefix}--ivory, ${swatchesPrefix}--clear, ${swatchesPrefix}--transparent`; - // carousel styles - const carouselStyles = css({ + // shared styles for swatches + const sharedStyles = css({ margin: 0, - '.ss__carousel': { - '& > div': { - minWidth: '1px', - flex: '0 1 auto', - }, - '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { - position: 'static', - bottom: 0, - width: `${swatchSize}px`, - height: `${swatchSize}px`, - }, - '.ss__carousel__prev-wrapper': { - margin: `0 ${swatchSpacing}px 0 0`, - }, - '.ss__carousel__next-wrapper': { - margin: `0 0 0 ${swatchSpacing}px`, - }, - '.swiper-container': { - maxWidth: `calc(100% - ${swatchSize * 2 + swatchSpacing * 2}px)`, - padding: '0 2px 0 0', - }, - '.swiper > .swiper-wrapper > .swiper-slide > *, .ss__swatches__carousel__swatch': { - width: '100%', - height: `${swatchSize}px`, - lineHeight: 0, - border: 0, - }, - '.ss__swatches__carousel__swatch': { - position: 'relative', - '&, &:before, &:after': { - boxSizing: 'border-box', + }); + + // carousel styles + const carouselStyles = css([ + sharedStyles, + { + margin: 0, + '.ss__carousel': { + '& > div': { + minWidth: '1px', + flex: '0 1 auto', }, - '&:before, &:after': { - content: '""', - display: 'block', - width: 'auto', - height: 'auto', - position: 'absolute', - top: 0, + '.ss__carousel__prev-wrapper, .ss__carousel__next-wrapper': { + position: 'static', bottom: 0, - left: 0, - right: 0, - transform: 'none', - opacity: 0, - visibility: 'hidden', + width: `${swatchSize}px`, + height: `${swatchSize}px`, }, - '&:before': { - border: `1px solid ${custom.colors.black}`, + '.ss__carousel__prev-wrapper': { + margin: `0 ${swatchSpacing}px 0 0`, }, - '&:after': { - border: `3px solid ${custom.colors.white}`, - margin: '1px', + '.ss__carousel__next-wrapper': { + margin: `0 0 0 ${swatchSpacing}px`, }, - [`&:not([style]), ${lightColors}`]: { - '&:before': { - borderColor: custom.colors.gray02, - opacity: 1, - visibility: 'visible', + '.swiper-container': { + maxWidth: `calc(100% - ${swatchSize * 2 + swatchSpacing * 2}px)`, + padding: '0 2px 0 0', + '& > .swiper-wrapper': { + '& > .swiper-slide': { + overflow: 'hidden', + '&:has(.ss__swatches__carousel__swatch.ss__swatches__carousel__swatch--unavailable)': { + '&:before': { + content: '""', + display: 'block', + position: 'absolute', + top: 0, + bottom: 0, + margin: 'auto', + width: '90%', + height: '1px', + borderTop: `3px solid ${darkGray}`, + transform: 'rotate(-45deg)', + }, + }, + }, }, }, - }, - '.ss__swatches__carousel__swatch--selected': { - '&:before, &:after': { - visibility: 'visible', + '.swiper-container > .swiper-wrapper > .swiper-slide > *, .ss__swatches__carousel__swatch': { + height: `${swatchSize}px`, + lineHeight: 0, + border: 0, }, - '&:before': { - opacity: 0.3, - }, - '&:after': { - opacity: 1, + '.ss__swatches__carousel__swatch': { + position: 'relative', + '&, &:before, &:after': { + boxSizing: 'border-box', + }, + '&:before, &:after': { + content: '""', + display: 'block', + width: 'auto', + height: 'auto', + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + transform: 'none', + opacity: 0, + visibility: 'hidden', + }, + '&:before': { + border: `1px solid ${custom.colors.black}`, + }, + '&:after': { + border: `3px solid ${custom.colors.white}`, + margin: '1px', + }, + [`&:not([style]), ${lightColors}`]: { + '&:before': { + borderColor: custom.colors.gray02, + opacity: 1, + visibility: 'visible', + }, + }, + '&[style*="url"]': { + backgroundRepeat: 'no-repeat !important', + backgroundSize: 'cover !important', + backgroundPosition: 'center !important', + transition: 'transform 0.5s ease-in-out', + '&:hover': { + transform: 'scale(1.5)', + }, + }, }, - [`&:not([style]), ${lightColors}`]: { + '.ss__swatches__carousel__swatch--selected': { + '&:before, &:after': { + visibility: 'visible', + }, '&:before': { - borderColor: custom.colors.black, opacity: 0.3, }, '&:after': { - borderColor: custom.colors.gray01, + opacity: 1, + }, + [`&:not([style]), ${lightColors}`]: { + '&:before': { + borderColor: darkGray, + opacity: 1, + }, + '&:after': { + borderColor: custom.colors.gray01, + }, + }, + '&[style*="url"]': { + '&:hover': { + transform: 'none', + }, }, }, + '.ss__swatches__carousel__swatch.ss__swatches__carousel__swatch--unavailable': { + opacity: 1, + cursor: 'not-allowed', + pointerEvents: 'none', + }, }, }, - }); + ]); // grid styles - const gridStyles = css({}); + const gridStyles = css([ + sharedStyles, + { + '.ss__grid': { + '.ss__grid__options': { + gridTemplateColumns: `repeat(auto-fill, minmax(${swatchSize}px, 1fr))`, + '.ss__grid__option': { + maxHeight: `${swatchSize}px`, + }, + }, + }, + }, + ]); - // return variant selection styles + // return swatch styles if (props?.type == 'grid') { return gridStyles; } else { @@ -119,7 +177,7 @@ export const swatches: ThemeComponent<'swatches', SwatchesProps> = { default: { swatches: { themeStyleScript: swatchesStyleScript, - type: 'grid', // carousel, grid, image + type: 'grid', }, 'swatches carousel': { autoAdjustSlides: false, diff --git a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts index 8233bb1c4..f679a6edd 100644 --- a/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts +++ b/packages/snap-preact/components/src/themes/pike/components/molecules/variantSelection.ts @@ -9,7 +9,7 @@ const variantSelectionStyleScript = (props: VariantSelectionProps) => { const variables = props?.theme?.variables; const lightGray = custom.utils.lightenColor(variables?.colors?.text, 0.65); - // shared styles for variant menus + // shared styles for variant selections const sharedStyles = css({ margin: `0 0 ${custom.spacing.x2}px 0`, '&:last-of-type': { @@ -124,17 +124,7 @@ const variantSelectionStyleScript = (props: VariantSelectionProps) => { ]); // swatches syles - const swatchesStyles = css([ - sharedStyles, - // { - // // minWidth: '1px', - // // maxWidth: '100%', - // // '.ss__swatches, .ss__carousel, .swiper, .swiper-wrapper': { - // // minWidth: '1px', - // // maxWidth: '100%' - // // } - // } - ]); + const swatchesStyles = css([sharedStyles]); // return variant selection styles if (props?.type == 'list') { @@ -151,7 +141,6 @@ export const variantSelection: ThemeComponent<'variantSelection', VariantSelecti default: { variantSelection: { themeStyleScript: variantSelectionStyleScript, - type: 'swatches', }, 'variantSelection dropdown icon': { size: `${custom.sizes.icon12}px`, From ab9ba83621a3edc1b67cf6126be33ece9c122b47 Mon Sep 17 00:00:00 2001 From: adria Date: Wed, 13 Aug 2025 21:27:04 -0600 Subject: [PATCH 041/118] bundle templates --- .../public/templates/bundle.html | 2 +- .../templates/recommendationBundle.ts | 20 ++- .../templates/recommendationBundleEasyAdd.ts | 119 +++++++++++- .../templates/recommendationBundleList.ts | 170 ++++++++++++++++-- .../templates/recommendationBundleVertical.ts | 144 ++++++++++++++- 5 files changed, 427 insertions(+), 28 deletions(-) diff --git a/packages/snap-preact-demo/public/templates/bundle.html b/packages/snap-preact-demo/public/templates/bundle.html index d491774e3..3a8d95ef7 100644 --- a/packages/snap-preact-demo/public/templates/bundle.html +++ b/packages/snap-preact-demo/public/templates/bundle.html @@ -172,7 +172,7 @@

Stripe Out Off-The-Shoulder Dress

-
stuff...
+
stuff...
+ + + + + Snap Documentation + + + + + + + +
+ + + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 2943d6f68..c887cad15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,44 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0](https://github.com/searchspring/snap-1.0/compare/v1.10.1...v1.11.0) (2026-01-15) + +### Bug Fixes + +- add noBeacon param to recommend api, update beacon and snapi-types version ([89e185c](https://github.com/searchspring/snap-1.0/commit/89e185c1b06a49274103c50d3bf410e2effa1e3a)) +- **autocomplete:** prevent impressing results when subsequent queries are the same ([2a154b5](https://github.com/searchspring/snap-1.0/commit/2a154b5fdecd3d56fbcd122205e16a9edf50b5ec)) +- **controller/search:** adding 'brand' and 'manufacturer' to the list of known background filters ([273e5e4](https://github.com/searchspring/snap-1.0/commit/273e5e482078a75796ac430e87b9aa30a83c1d9a)) +- **controller/search:** changing when 'lastStringyParams' is cleared and ensuring persisted pageshow ([7148731](https://github.com/searchspring/snap-1.0/commit/71487319cb130af204b833d0ab097694914dd100)) +- **controller/search:** quick fix on setting name for hierarchy ([9f336ea](https://github.com/searchspring/snap-1.0/commit/9f336eaf08d6eb5f6bfe764fcd57f1bd24c4cf30)) +- fix inline banners in last position and index matches totalResults ([c2da409](https://github.com/searchspring/snap-1.0/commit/c2da4095721a5be8cb4467542c5579a0dce2ac95)) +- **image:** remove visibility style when value is not hidden ([e1e33df](https://github.com/searchspring/snap-1.0/commit/e1e33dff124582ac58a88f92cd0ee1be91bd7a71)) +- **networkcache:** bugfix to prevent memoryCache from growing too large, and delete expired entries ([0534b6c](https://github.com/searchspring/snap-1.0/commit/0534b6c4ccb62389669c19f8f9f87cbd098fa6a5)) +- **preact-components/recommendationbundle:** removing default prop values for title and description ([4ea121d](https://github.com/searchspring/snap-1.0/commit/4ea121d788312dc0a138e3243089e3867b75841f)) +- **preact-components/withtracking:** updating HOC to not send impressions if the product is a seed ([6c30f34](https://github.com/searchspring/snap-1.0/commit/6c30f34ab2a86293d0b6d03a04fa93de9f1ad7b9)) +- **preact/components/facet:** adjusting new facet input addition ([9823f76](https://github.com/searchspring/snap-1.0/commit/9823f7604db63fd894f9960b01fc94330bdb3626)) +- **preact/components/recommendationbundle:** fixing impression tracking in bundles ([e42fa30](https://github.com/searchspring/snap-1.0/commit/e42fa3076869b6f742ed9f50fb5f2df2c0699daa)) +- **preact/withtracking:** refactoring to useEffect and switching to capture for click event handler ([f862978](https://github.com/searchspring/snap-1.0/commit/f86297861da17394caa2c0a83886461b3938d959)) +- **recommmendationbundle:** fixing merge conflicts, and bugfix around cta slot not working with lang ([4dc4759](https://github.com/searchspring/snap-1.0/commit/4dc4759fb32d6216826b5d71e27683e5b593b8c3)) +- **searchresultstore:** bugfix for disabled variant selection logic ([0346817](https://github.com/searchspring/snap-1.0/commit/03468178b8c49d38cabaae74e3f14569c0186c1e)) +- **snap-preact:** adding additional checks around all cookie/storage usage to ensure things work ([0f6d8e7](https://github.com/searchspring/snap-1.0/commit/0f6d8e735c3d3c7eea7cfba6cf59509fafb819bb)) +- **storybook:** update all broken stories that use old siteId to new athos siteid ([ec1ac5f](https://github.com/searchspring/snap-1.0/commit/ec1ac5fd97726a36e82f17086e449d1672576c79)) +- **toolbox/domtargeter:** error handling was supressing errors due to await outside of async ([340f8c2](https://github.com/searchspring/snap-1.0/commit/340f8c2c97f625a3784eeea58b59ff23d11864cc)) +- **toolbox/domtargeter:** switching to for..of loop ([dac4096](https://github.com/searchspring/snap-1.0/commit/dac40968b36b1d69210842a2811448b660f5dba0)) +- **useintersectionadvanced:** prevent non-visible elements from being impressed ([74455cf](https://github.com/searchspring/snap-1.0/commit/74455cfe340067278b5bcf88b5e0c2415d814510)) + +### Features + +- **client:** adding support to have separate subdomain in api - using this for recommend requests ([ceb6d65](https://github.com/searchspring/snap-1.0/commit/ceb6d6500c2c8a57df0356535d0f74d8d0464fae)) +- **networkcache:** get function no longer accounts for personalization params when backforward nav ([de52d7e](https://github.com/searchspring/snap-1.0/commit/de52d7e02c7ed70488a31a4f92bd26820a8f3823)) +- **preact/components/facet:** adding range facet inputs and the ability to use filterFormatValue for filters ([ef16169](https://github.com/searchspring/snap-1.0/commit/ef16169123f749d45cf4ab4c7d29375c06ab16da)) +- **preact/components/facetslider:** adding 'separateHandles' prop and adjusting onChange ([dc2f7fc](https://github.com/searchspring/snap-1.0/commit/dc2f7fcb1a9a85876d3a235cbf3e83694a5e6a93)) +- **preact/components/recommendationbundle:** api bundleSeed and refactor recommendationBundle ([28a32ce](https://github.com/searchspring/snap-1.0/commit/28a32ce34b9bf9f9a120572b006502a6c1ecbec6)) +- **preact/components/recommendations:** adding new description prop to both recs and bundles ([46888c7](https://github.com/searchspring/snap-1.0/commit/46888c75851b0156f202cb165229b90581f0a684)) +- **preact/components/variantselection:** adding new onSelect prop to variantSelection ([8bf77c7](https://github.com/searchspring/snap-1.0/commit/8bf77c7b898c6f6d8e2addcd3b5f6d053841170d)) +- **searchcontroller:** adding the ability to show hierarchy filters in the filterSummary ([43c3890](https://github.com/searchspring/snap-1.0/commit/43c3890db4f6e6b2ec5edc7f45fe2069a6f4ce20)) +- **searchresultstore:** added config setting to showDisabledSelections for the variants ([918dee6](https://github.com/searchspring/snap-1.0/commit/918dee65f4ee323a1dcdd0d7aee34f7d5b0163db)) +- **snap.tsx:** adding support for configurable initiator ([f286c5c](https://github.com/searchspring/snap-1.0/commit/f286c5c5f416ddad5771002bf00a0dc2e570922f)) + ## [1.10.1](https://github.com/searchspring/snap-1.0/compare/v1.10.0...v1.10.1) (2025-10-06) ### Bug Fixes @@ -345,6 +383,96 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **snap-layout/theming:** work towards theming a snap layout using styleScripts ([b6be05e](https://github.com/searchspring/snap-1.0/commit/b6be05eee8fc0a06c9e853a591aa0d380158fc0c)) - support template themes in snap-preact ([df9d905](https://github.com/searchspring/snap-1.0/commit/df9d9057a1554d7eb62830002c3db990e0f8f272)) - theme reactivity initial interface ([c017aad](https://github.com/searchspring/snap-1.0/commit/c017aadd4add7591bd98fcfbdb8db8985bf2d498)) +## [0.72.1](https://github.com/searchspring/snap/compare/v0.72.0...v0.72.1) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap + +# [0.72.0](https://github.com/searchspring/snap/compare/v0.71.0...v0.72.0) (2026-01-14) + +### Bug Fixes + +- base url ([4f7926a](https://github.com/searchspring/snap/commit/4f7926aa5995c9bd5df594cf3fd304e92f572c2e)) +- change content width and legend breakpoint ([d16e865](https://github.com/searchspring/snap/commit/d16e865986dfb6e6306271a81df3d475c9c38d7c)) +- legend links ([b753591](https://github.com/searchspring/snap/commit/b753591cb63e07b80311aa0529310cde38a7879d)) +- **networkcache:** bugfix to prevent memoryCache from growing too large, and delete expired entries ([0534b6c](https://github.com/searchspring/snap/commit/0534b6c4ccb62389669c19f8f9f87cbd098fa6a5)) +- route base path for non-localhost ([511529a](https://github.com/searchspring/snap/commit/511529ac6194535d8049e667de3eee8b38a8b50f)) +- **searchresultstore:** adding support for option element to be used in realtime variants ([167f867](https://github.com/searchspring/snap/commit/167f867f12b06a6fb0f8f84f4553a9d9e6b2d884)) + +### Features + +- **client:** adding support to have separate subdomain in api - using this for recommend requests ([ceb6d65](https://github.com/searchspring/snap/commit/ceb6d6500c2c8a57df0356535d0f74d8d0464fae)) +- **networkcache:** get function no longer accounts for personalization params when backforward nav ([de52d7e](https://github.com/searchspring/snap/commit/de52d7e02c7ed70488a31a4f92bd26820a8f3823)) +- **snap.tsx:** adding support for configurable initiator ([f286c5c](https://github.com/searchspring/snap/commit/f286c5c5f416ddad5771002bf00a0dc2e570922f)) + +# [0.71.0](https://github.com/searchspring/snap/compare/v0.70.1...v0.71.0) (2025-11-26) + +### Bug Fixes + +- **autocomplete:** prevent impressing results when subsequent queries are the same ([2a154b5](https://github.com/searchspring/snap/commit/2a154b5fdecd3d56fbcd122205e16a9edf50b5ec)) +- **controller/search:** changing when 'lastStringyParams' is cleared and ensuring persisted pageshow ([7148731](https://github.com/searchspring/snap/commit/71487319cb130af204b833d0ab097694914dd100)) +- **image:** remove visibility style when value is not hidden ([e1e33df](https://github.com/searchspring/snap/commit/e1e33dff124582ac58a88f92cd0ee1be91bd7a71)) +- **preact/withtracking:** refactoring to useEffect and switching to capture for click event handler ([f862978](https://github.com/searchspring/snap/commit/f86297861da17394caa2c0a83886461b3938d959)) +- **useintersectionadvanced:** prevent non-visible elements from being impressed ([74455cf](https://github.com/searchspring/snap/commit/74455cfe340067278b5bcf88b5e0c2415d814510)) + +### Features + +- **preact/components/facetslider:** adding 'separateHandles' prop and adjusting onChange ([dc2f7fc](https://github.com/searchspring/snap/commit/dc2f7fcb1a9a85876d3a235cbf3e83694a5e6a93)) + +## [0.70.1](https://github.com/searchspring/snap/compare/v0.70.0...v0.70.1) (2025-11-17) + +### Bug Fixes + +- **preact-components/withtracking:** updating HOC to not send impressions if the product is a seed ([6c30f34](https://github.com/searchspring/snap/commit/6c30f34ab2a86293d0b6d03a04fa93de9f1ad7b9)) + +# [0.70.0](https://github.com/searchspring/snap/compare/v0.69.2...v0.70.0) (2025-11-13) + +### Bug Fixes + +- **preact-components/recommendationbundle:** removing default prop values for title and description ([4ea121d](https://github.com/searchspring/snap/commit/4ea121d788312dc0a138e3243089e3867b75841f)) +- **preact/components/recommendationbundle:** fixing impression tracking in bundles ([e42fa30](https://github.com/searchspring/snap/commit/e42fa3076869b6f742ed9f50fb5f2df2c0699daa)) +- **searchresultstore:** bugfix for disabled variant selection logic ([0346817](https://github.com/searchspring/snap/commit/03468178b8c49d38cabaae74e3f14569c0186c1e)) + +### Features + +- **preact/components/recommendationbundle:** api bundleSeed and refactor recommendationBundle ([28a32ce](https://github.com/searchspring/snap/commit/28a32ce34b9bf9f9a120572b006502a6c1ecbec6)) +- **preact/components/recommendations:** adding new description prop to both recs and bundles ([46888c7](https://github.com/searchspring/snap/commit/46888c75851b0156f202cb165229b90581f0a684)) +- **searchresultstore:** added config setting to showDisabledSelections for the variants ([918dee6](https://github.com/searchspring/snap/commit/918dee65f4ee323a1dcdd0d7aee34f7d5b0163db)) + +## [0.69.2](https://github.com/searchspring/snap/compare/v0.69.1...v0.69.2) (2025-11-04) + +**Note:** Version bump only for package @searchspring/snap + +## [0.69.1](https://github.com/searchspring/snap/compare/v0.69.0...v0.69.1) (2025-10-23) + +**Note:** Version bump only for package @searchspring/snap + +# [0.69.0](https://github.com/searchspring/snap/compare/v0.68.0...v0.69.0) (2025-10-16) + +### Bug Fixes + +- **controller/search:** adding 'brand' and 'manufacturer' to the list of known background filters ([273e5e4](https://github.com/searchspring/snap/commit/273e5e482078a75796ac430e87b9aa30a83c1d9a)) +- **snap-preact:** adding additional checks around all cookie/storage usage to ensure things work ([0f6d8e7](https://github.com/searchspring/snap/commit/0f6d8e735c3d3c7eea7cfba6cf59509fafb819bb)) +- **toolbox/domtargeter:** error handling was supressing errors due to await outside of async ([340f8c2](https://github.com/searchspring/snap/commit/340f8c2c97f625a3784eeea58b59ff23d11864cc)) +- **toolbox/domtargeter:** switching to for..of loop ([dac4096](https://github.com/searchspring/snap/commit/dac40968b36b1d69210842a2811448b660f5dba0)) + +### Features + +- **searchcontroller:** adding the ability to show hierarchy filters in the filterSummary ([43c3890](https://github.com/searchspring/snap/commit/43c3890db4f6e6b2ec5edc7f45fe2069a6f4ce20)) + +# [0.68.0](https://github.com/searchspring/snap/compare/v0.67.5...v0.68.0) (2025-08-18) + +### Bug Fixes + +- add noBeacon param to recommend api, update beacon and snapi-types version ([89e185c](https://github.com/searchspring/snap/commit/89e185c1b06a49274103c50d3bf410e2effa1e3a)) +- fix inline banners in last position and index matches totalResults ([c2da409](https://github.com/searchspring/snap/commit/c2da4095721a5be8cb4467542c5579a0dce2ac95)) + +### Features + +- **preact/components/variantselection:** adding new onSelect prop to variantSelection ([8bf77c7](https://github.com/searchspring/snap/commit/8bf77c7b898c6f6d8e2addcd3b5f6d053841170d)) + +## [0.67.5](https://github.com/searchspring/snap/compare/v0.67.4...v0.67.5) (2025-08-11) + +**Note:** Version bump only for package @searchspring/snap ## [0.67.4](https://github.com/searchspring/snap/compare/v0.67.3...v0.67.4) (2025-07-29) diff --git a/assets/hljs.css b/assets/hljs.css index ea0a0f939..bc63e355a 100644 --- a/assets/hljs.css +++ b/assets/hljs.css @@ -89,4 +89,85 @@ pre { .hljs-strong { font-weight: bold; +} + +/* Dark mode overrides - ensure accessible colors on dark backgrounds */ +body.dark-mode { + pre { + color: #e0e0e0; + } + + .hljs-comment, + .hljs-quote { + color: #9ca3af; + font-style: italic; + } + + .hljs-keyword, + .hljs-selector-tag, + .hljs-subst { + color: #60a5fa; + font-weight: bold; + } + + .hljs-number, + .hljs-literal, + .hljs-variable, + .hljs-template-variable, + .hljs-tag .hljs-attr { + color: #34d399; + } + + .hljs-string, + .hljs-doctag { + color: #fbbf24; + } + + .hljs-title, + .hljs-section, + .hljs-selector-id { + color: #60a5fa; + font-weight: bold; + } + + .hljs-type, + .hljs-class .hljs-title { + color: #a78bfa; + font-weight: bold; + } + + .hljs-tag, + .hljs-name, + .hljs-attribute { + color: #60a5fa; + font-weight: normal; + } + + .hljs-regexp, + .hljs-link { + color: #34d399; + } + + .hljs-symbol, + .hljs-bullet { + color: #f472b6; + } + + .hljs-built_in, + .hljs-builtin-name { + color: #38bdf8; + } + + .hljs-meta { + color: #9ca3af; + font-weight: bold; + } + + .hljs-deletion { + background: rgba(239, 68, 68, 0.2); + } + + .hljs-addition { + background: rgba(34, 197, 94, 0.2); + } } \ No newline at end of file diff --git a/assets/styles.css b/assets/styles.css index 875c1aa51..a8f88e00d 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -1,12 +1,35 @@ +:root { + --athos-primary: #1D4990; + --athos-secondary: #00AEEF; + --athos-secondary-AA: #007EAD; + --athos-secondary-AAA: #005F82; + --navigation-width: 250px; + --legend-width: 250px; + + /* Light mode colors */ + --bg-primary: #ffffff; + --bg-secondary: #fafafa; + --bg-tertiary: #fafafa; + --text-primary: #222; + --text-secondary: #777; + --text-inverse: #ffffff; + --border-color: #eee; + --shadow-color: rgb(0 0 0 / 35%); + --code-bg: rgba(27,31,35,0.05); + --pre-bg: #fafafa; + --table-header-bg: var(--athos-secondary-AA); + --table-header-text: #ffffff; + --table-row-even: #fafafa; +} + html { box-sizing: border-box; - color: #222; + color: var(--text-primary); } body { margin: 0; - background: #fafafa; - font-family: 'Ubuntu', sans-serif; + font-family: 'Inter', sans-serif; -webkit-font-smoothing: antialiased; } @@ -16,45 +39,34 @@ body { box-sizing: inherit; } -#search-container { - color: hsl(247deg 67% 33%); - padding: 0px 20px 0px 20px; - margin: 0; - margin-bottom: 20px; -} - a { text-decoration: none; - color: #4c3ce2; + color: var(--athos-secondary-AA); } a:hover { text-decoration: underline; } -p, table { - font-family: 'Roboto', sans-serif; -} - table { border-collapse: collapse; - background-color: #ffffff; + background-color: var(--bg-primary); border-radius: 3px; width: 100%; - box-shadow: 0 0 1px rgb(0 0 0 / 35%); margin-bottom: 1em; + box-shadow: 0 0 1px var(--shadow-color); } table thead { - background-color: #00cee1; - color: #ffffff; + background-color: var(--table-header-bg); + color: var(--table-header-text); text-transform: uppercase; font-size: 0.9em; font-weight: bold; } table tr:nth-child(even) { - background-color: #fafafa; + background-color: var(--table-row-even); } table th { @@ -71,6 +83,10 @@ table td { padding: 10px; } + +#app { + display: flex; +} #header { display: flex; margin: 10px 0 20px 0; @@ -84,17 +100,18 @@ table td { } .logo-container img { - width: 250px; + width: var(--navigation-width); } #navigation-wrapper { - width: 250px; height: 100vh; + width: var(--navigation-width); + z-index: 1; position: fixed; top: 0; left: 0; - background: #fff; - box-shadow: 0 0 5px rgb(0 0 0 / 35%); + background: var(--bg-primary); + box-shadow: 0 0 2px var(--shadow-color); } #navigation-wrapper .collapseNav { @@ -104,6 +121,9 @@ table td { #navigation { overflow-y: auto; max-height: calc(100% - 170px); + display: flex; + flex-direction: column; + height: inherit; } #navigation ul { @@ -116,65 +136,132 @@ table td { color: inherit; display: inline-block; width: calc(100% - 20px); - padding: 5px 10px; + padding: 3px 10px; font-size: 0.9em; } #navigation ul li a .fas { - color: #777; + color: var(--text-secondary); float: right; font-size: 10px; } #navigation ul li a.active { font-weight: bold; - color: #4937d7; -} - -#navigation .sublinks li a.active { - color: hsl(185deg 97% 35%); + box-shadow: -3px 0px 0 0 var(--athos-secondary); } #navigation h3 { - color: hsl(247deg 67% 33%); + color: var(--athos-primary); padding: 0px 20px 0px 20px; margin: 0; } +#content-wrapper { + position: fixed; + top: 0; + right: 0; + width: calc(100% - var(--navigation-width)); + overflow: auto; + height: 100%; +} + #content { - width: calc(100% - 250px); - height: 100vh; - float: right; + display: flex; + flex-direction: row-reverse; + margin: 0 auto; +} + +#content.markdown { + max-width: 1400px; +} + +#content .permalink { + position: absolute; + top: -1em; + color: var(--athos-secondary-AA); + font-size: 0.8rem; +} +#content .scrolled-to { + box-shadow: 0 3px 0 0px var(--athos-secondary); +} + +#content h2, +#content h3, +#content h4, +#content h5, +#content h6 { + cursor: pointer; + position: relative; + background: transparent; + transition: background 0.5s ease; +} + +#content h2::before, +#content h3::before, +#content h4::before, +#content h5::before, +#content h6::before { + content: '\f0c1'; + font-family: 'Font Awesome 5 Free', 'Font Awesome 6 Free', 'FontAwesome'; + font-weight: 900; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + position: absolute; + left: -1.2em; + opacity: 0; + transition: opacity 0.25s ease; + color: var(--athos-secondary); +} + +#content h2:hover::before, +#content h3:hover::before, +#content h4:hover::before, +#content h5:hover::before, +#content h6:hover::before { + opacity: 1; + transition: opacity 0.25s ease; } #markdown { - padding: 50px 100px; - max-width: 1500px; - margin: 50px auto; + padding: 3% 5%; line-height: 1.5; - background: white; - border-top-left-radius: 20px; - border-top-right-radius: 20px; - border: 1px solid #eee; + background: var(--bg-primary); + overflow: auto; + width: 100%; } #markdown h2 { - color: #4937d7; + color: var(--athos-primary); font-size: 1.5em; margin-top: 2em; } #markdown h2:first-of-type:not(h1 ~ h2), #markdown h1 { - color: #4937d7; - border-bottom: 3px solid #4937d7; + color: var(--athos-primary); + border-bottom: 3px solid var(--athos-primary); padding-bottom: 5px; font-size: 2em; margin-top: 0px; } +.theme-toggle { + position: absolute; + right: 0; + padding: 1em; +} + +.theme-toggle button { + border: none; + background: transparent; +} + .markdown-alert { - padding: .5em 1em; + padding: 1em; margin: 1em 0; color: inherit; border-left: .25em solid #ccc; @@ -193,6 +280,11 @@ table td { font-weight: bold; align-items: center; line-height: 1; + gap: 0.5em; +} + +.markdown-alert .markdown-alert-title svg { + fill: currentColor; } .markdown-alert.markdown-alert-note { @@ -252,29 +344,29 @@ table td { border: none; } +.section:last-child { + margin-top: auto; +} + .section .links { margin: 5px 0 20px; } .section .links .sublinks { font-size: 90%; - margin: 0 10px; } pre { - font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace; - padding: 16px; overflow: auto; - line-height: 1.2; - background-color: #fafafa; + background-color: var(--pre-bg); border-radius: 0px; - border: 1px solid #eee; + border: 1px solid var(--border-color); } code:not([class]) { padding: 0.2em 0.4em; margin: 0; - background-color: rgba(27,31,35,0.05); + background-color: var(--code-bg); border-radius: 3px; } @@ -288,38 +380,88 @@ code:not([class]) { height: 100%; } -@media only screen and (max-width: 1750px) { +.legend { + padding: 3em 1em; + border-radius: 5px; + font-size: 80%; + flex: 1 1 auto; + max-width: var(--legend-width); + width: 100%; + box-sizing: content-box; +} +.legend-container { + position: fixed; + max-width: var(--legend-width); + overflow-y: auto; + max-height: calc(100vh - 3em - 3em); + word-break: break-word; +} +.legend-title { + font-size: 1.2em; + margin-bottom: 1em; +} +.legend ul { + list-style: none; + padding: 0; + margin: 0; +} +.legend ul li { + padding: 0.2em 0; +} +.legend ul li.h3 { + margin-left: 1em; +} +.legend ul li.h4 { + margin-left: 2em; +} +.legend ul li.h5 { + margin-left: 3em; +} +.legend ul li.h6 { + margin-left: 4em; +} +.legend ul li a.active { + font-weight: bold; + border-left: 3px solid var(--athos-secondary); +} +.legend ul li a { + color: inherit; + border-left: 3px solid transparent; + padding-left: 3px; + display: flex; +} + +@media only screen and (max-width: 1400px) { #markdown { margin: 0px auto; border-radius: 0px; } -} -@media only screen and (max-width: 767px) { - #header { - margin: 0; + #content { + display: unset; + flex-direction: unset; + } - #navigation { + .legend { display: none; } +} - #navigation.visible { - display: unset; - } - +@media only screen and (max-width: 767px) { #navigation-wrapper { width: 100%; position: relative; height: auto; - padding: 10px 0; } - #navigation-wrapper img { - width: 100%; max-height: 80px; } + #app { + flex-direction: column; + } + #navigation-wrapper .collapseNav { display: unset; color: rgb(58, 35, 173); @@ -329,14 +471,123 @@ code:not([class]) { top: 0; right: 0; } + #navigation { + display: none; + } + #navigation.visible { + display: unset; + } - #content { - width: 100%; - height: auto; - float: unset; + #content-wrapper { + position: unset; + width: unset; } + +} - #markdown { - padding: 10px 30px; +/* Dark mode */ +@media (max-width: 767px) { + .theme-toggle { + display: none; } +} + +body.dark-mode { + background-color: var(--bg-primary); + color: var(--text-primary); + + --bg-primary: #1a1a1a; + --bg-secondary: #2d2d2d; + --bg-tertiary: #252525; + --text-primary: #e0e0e0; + --text-secondary: #a0a0a0; + --text-inverse: #1a1a1a; + --border-color: #404040; + --shadow-color: rgb(0 0 0 / 60%); + --code-bg: rgba(0, 0, 0, 0.3); + --pre-bg: #252525; + --pre-text: #e0e0e0; + --table-header-bg: var(--athos-secondary-AA); + --table-header-text: #ffffff; + --table-row-even: #252525; + --heading-color: #6ba3d8; + --link-color: #4db8e8; +} + +.dark-mode .theme-toggle button { + color: var(--text-primary); +} + +/* Fix headings - use lighter blue for better contrast */ +.dark-mode #markdown h2, +.dark-mode #markdown h1, +.dark-mode #navigation h3 { + color: var(--heading-color); +} + +/* Fix links - use brighter blue for better contrast */ +.dark-mode a { + color: var(--link-color); +} + +.dark-mode #content .permalink { + color: var(--link-color); +} + +/* Fix pre blocks - ensure text is visible (override hljs.css) */ +.dark-mode pre { + color: var(--pre-text) !important; +} + +/* Fix code blocks - ensure better contrast */ +.dark-mode code:not([class]) { + color: var(--text-primary) !important; + background-color: var(--code-bg) !important; +} + +/* Fix code blocks inside pre elements */ +.dark-mode pre code { + color: var(--pre-text) !important; + background-color: transparent !important; +} + +/* Dark mode markdown alerts */ +.dark-mode .markdown-alert.markdown-alert-note { + border-left-color: #8F6CF6; + background-color: rgba(143, 108, 246, 0.15); +} +.dark-mode .markdown-alert.markdown-alert-note .markdown-alert-title { + color: #b399ff; +} + +.dark-mode .markdown-alert.markdown-alert-tip { + border-left-color: #00CEE1; + background-color: rgba(0, 206, 225, 0.15); +} +.dark-mode .markdown-alert.markdown-alert-tip .markdown-alert-title { + color: #4dd9e7; +} + +.dark-mode .markdown-alert.markdown-alert-important { + border-left-color: #a68bff; + background-color: rgba(166, 139, 255, 0.15); +} +.dark-mode .markdown-alert.markdown-alert-important .markdown-alert-title { + color: #c9b3ff; +} + +.dark-mode .markdown-alert.markdown-alert-warning { + border-left-color: #e09f00; + background-color: rgba(224, 159, 0, 0.15); +} +.dark-mode .markdown-alert.markdown-alert-warning .markdown-alert-title { + color: #ffb732; +} + +.dark-mode .markdown-alert.markdown-alert-caution { + border-left-color: #ff4757; + background-color: rgba(255, 71, 87, 0.15); +} +.dark-mode .markdown-alert.markdown-alert-caution .markdown-alert-title { + color: #ff6b7a; } \ No newline at end of file diff --git a/athos_favicon.svg b/athos_favicon.svg new file mode 100644 index 000000000..d63edfd86 --- /dev/null +++ b/athos_favicon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/doc-app.js b/doc-app.js new file mode 100644 index 000000000..b5f0c921a --- /dev/null +++ b/doc-app.js @@ -0,0 +1,532 @@ +function flattenDocumentLinks(docs) { + const flattened = []; + function traverse(links) { + if (!Array.isArray(links)) return; + links.forEach((link) => { + flattened.push(link); + if (link.links && Array.isArray(link.links)) { + traverse(link.links); + } + }); + } + docs.forEach((doc) => { + if (doc.links && Array.isArray(doc.links)) { + traverse(doc.links); + } + }); + return flattened; +} + +marked.use(markedAlert()); +import('./docs/documents.js').then(function (_) { + const documents = _.default; + const replaces = flattenDocumentLinks(documents) + .filter((link) => link.type === 'markdown') + .map((link) => { + return { a: `(https://searchspring.github.io/snap${link.route}`, b: `(.${link.route}` }; + }) + .flat() + .concat([ + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Search)', b: '(./reference-store-search)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Autocomplete)', b: '(./reference-store-autocomplete)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Recommendation)', b: '(./reference-store-recommendation)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Finder)', b: '(./reference-store-finder)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Storage)', b: '(./reference-store-storage)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Abstract)', b: '(./reference-store-abstract)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Cart)', b: '(./reference-store-cart)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Meta)', b: '(./reference-store-meta)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Search)', b: '(./reference-controller-search)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Autocomplete)', b: '(./reference-controller-autocomplete)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Finder)', b: '(./reference-controller-finder)' }, + { + a: '(https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Recommendation)', + b: '(./reference-controller-recommendation)', + }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-url-manager)', b: '(./reference-url-manager)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-event-manager)', b: '(./reference-event-manager)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-logger)', b: '(./reference-logger)' }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-tracker)', b: '(./reference-tracker)' }, + { a: '(https://github.com/searchspring/snap/tree/main/docs/SNAP_TRACKING.md)', b: '(./snap-tracking)' }, + { a: '(https://github.com/searchspring/snap/tree/main/docs/SNAP_TRACKING.md#', b: '(./snap-tracking#' }, + { + a: '(https://github.com/searchspring/snap/tree/main/docs/SNAP_TRACKING.md#cart-attribute-tracking)', + b: '(./snap-tracking#cart-attribute-tracking)', + }, + { a: '(https://github.com/searchspring/snap/tree/main/packages/snap-client)', b: '(./snap-client)' }, + { + a: '(https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url)', + b: '(./reference-snap-preact-url-translator)', + }, + { + a: '(https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/QueryString)', + b: '(./reference-url-manager-query-string-translator)', + }, + { + a: '(https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/linkers/react)', + b: '(./reference-url-manager-react-linker)', + }, + { a: '(https://github.com/searchspring/snap/tree/main/docs/REFERENCE_CONFIGURATION_MIDDLEWARE.md)', b: '(./reference-snap-preact-middleware)' }, + { a: '(https://github.com/searchspring/snap/tree/main/docs/REFERENCE_VARIANTS.md)', b: '(./reference-variants)' }, + { a: '(https://searchspring.github.io/snap/preact-components)', b: '(./preact-components)' }, + { a: '(https://searchspring.github.io/snap/preact-components?params=', b: '(./preact-components?params=' }, + ]); + + const modifyLinks = (markdown) => { + replaces.forEach(function (replace) { + markdown = markdown.replaceAll(replace.a, replace.b); + }); + return markdown; + }; + const App = { + data() { + return { + documents, + darkMode: localStorage.getItem('darkMode') === 'true', + }; + }, + mounted() { + if (this.darkMode) { + document.body.classList.add('dark-mode'); + } + }, + methods: { + toggleDarkMode() { + this.darkMode = !this.darkMode; + localStorage.setItem('darkMode', this.darkMode); + if (this.darkMode) { + document.body.classList.add('dark-mode'); + } else { + document.body.classList.remove('dark-mode'); + } + }, + }, + computed: { + routes() { + const routeMap = this.documents.reduce((acc, section) => { + section?.links?.forEach((link) => { + if (link.route) { + acc[link.route] = link; + } + + link?.links?.forEach((sublink) => { + if (sublink.route) { + acc[sublink.route] = sublink; + } + if (sublink?.links) { + sublink.links.forEach((subsublink) => { + if (subsublink.route) { + acc[subsublink.route] = subsublink; + } + }); + } + }); + }); + + return acc; + }, {}); + + return routeMap; + }, + }, + template: ` + + +
+ +
+
+ +
+
+ `, + }; + + const app = Vue.createApp(App); + + app.component('Content', { + props: ['routes'], + template: ` +
+ + +
+
+ `, + computed: { + currentRoute() { + return this.$route.path; + }, + routeData() { + const params = this.$route.query.params || ''; + + if (params && this.routes[this.currentRoute] && this.routes[this.currentRoute].url && this.routes[this.currentRoute].type === 'iframe') { + const currentiFrameSrc = document.querySelector('iframe')?.src; + let url = currentiFrameSrc || `${this.routes[this.currentRoute].url}${params}`; + if (params.includes('&ac')) { + url = `${this.routes[this.currentRoute].url}${params}`; + } + if (url.startsWith('http') && url.includes('/packages/')) { + url = `./packages/${url.split('/packages/')[1]}`; + } + return { type: 'iframe', url }; + } + + return this.routes[this.currentRoute] || { type: 'markdown', url: './docs/404.md' }; + }, + }, + + methods: { + onLoad() { + const mutations = []; + // Select the node that will be observed for mutations + const targetNode = document.getElementById('frame').contentWindow.document.querySelector('title'); + if (!targetNode) return; + + // Options for the observer (which mutations to observe) + const config = { characterData: true, attributes: true, childList: true, subtree: true }; + + // Callback function to execute when mutations are observed + const callback = function (mutationsList) { + mutations.push(mutationsList[0]); + if (mutationsList.length && mutations.length > 1) { + const url = mutationsList[0].target.baseURI; + const params = encodeURIComponent(`?${url.split('?')[1]}`); + const currentPath = window.location.pathname; // '/components-preact' + const newRoute = `${currentPath}?params=${params}`; + window.history.pushState({}, '', newRoute); + } + }; + + // Create an observer instance linked to the callback function + const observer = new MutationObserver(callback); + + // Start observing the target node for configured mutations + observer.observe(targetNode, config); + }, + }, + }); + + app.component('Markdown', { + props: ['src'], + template: ` +
+ `, + data() { + return { + markdown: '', + }; + }, + watch: { + src() { + this.getMarkdown(this.src); + }, + }, + computed: { + markedHTML() { + return marked(this.markdown).replace(/ { + // external links should open in a new tab + return match.replace('
{ + const response = await fetch(file); + let text = await response.text(); + return modifyLinks(text); + }) + ); + this.markdown = markdowns.join('\n'); + } else { + this.markdown = ''; + const response = await fetch(file); + let text = await response.text(); + this.iframe = ''; + this.markdown = modifyLinks(text); + } + }, + }, + }); + + app.component('Link', { + props: ['link', 'active'], + template: ` + + {{link.label}} + + + + {{link.label}} + + + `, + }); + + app.component('Navigation', { + props: ['documents'], + components: ['Link'], + data() { + return { + navVisible: window.innerWidth < 768 ? false : true, + }; + }, + computed: { + currentRoute() { + return this.$route.path; + }, + }, + methods: { + toggleNav() { + this.navVisible = !this.navVisible; + }, + inRoute(link) { + if (link?.route) { + const includedRoutes = [link.route]; + + link.links?.forEach((sublink) => { + includedRoutes.push(sublink.route); + if (sublink?.links) { + sublink.links.forEach((subsublink) => includedRoutes.push(subsublink.route)); + } + }); + + return includedRoutes.includes(this.currentRoute); + } + }, + }, + template: ` + + `, + }); + + const routes = [ + { path: '/', component: app.component('Content') }, + { path: '/:path', component: app.component('Content') }, + { path: '/:pathMatch(.*)', component: app.component('Content') }, + ]; + + const router = VueRouter.createRouter({ + history: VueRouter.createWebHistory(window.location.hostname !== 'localhost' ? `/${window.location.pathname.split('/')[1]}/` : undefined), + routes, + }); + router.afterEach((to, from) => { + if (to.path !== from.path) { + // close navigation on mobile upon changing routes (not applicable to initial load) + if (window.innerWidth < 768 && document.getElementById('navigation')?.classList.contains('visible')) { + document.querySelector('.collapseNav')?.click(); + } + + // invoked here to handle removal when iframe is rendered + document.querySelector('.legend')?.remove(); + } + }); + + app.use(router); + + app.mount('#app'); + + const SCROLL_TO_HEADING_DELAY = 2000; + const DEBOUNCE_DELAY = 100; + window.postRenderModifications = function () { + let headingIdsInView = []; + let lastScrolledUp = true; + let lastScrollY = window.scrollY; + let hashId = window.location.hash.split('#')[1]; + let preventLegendUpdate = Boolean(hashId); // if there is a hash id, prevent the legend from updating while scrolling + if (hashId) { + // scroll to heading if it exists in the url + const heading = document.getElementById(hashId); + if (heading && createHeadingId(heading) === hashId) { + heading.scrollIntoView({ behavior: 'smooth' }); + heading.classList.add('scrolled-to'); + window.setTimeout(function () { + heading.classList.remove('scrolled-to'); + preventLegendUpdate = false; + }, SCROLL_TO_HEADING_DELAY); + } else { + // handle redirects of old routes + if (hashId === '/') { + router.replace('/'); + } else if (hashId.match(/^\/components-preact/)) { + router.replace(hashId.replace(/^\/components-preact/, '/preact-components')); + } else if (hashId.match(/^\/integration-recommendations/)) { + router.replace('/snap-recommendations-integration'); + } else if (hashId.match(/^\/snap-recommendations-legacy/)) { + router.replace('/snap-recommendations-legacy'); + } else if (hashId.match(/^\/start-preact/)) { + router.replace('/getting-started'); + } else if (hashId.match(/^\/start-preact-events/)) { + router.replace('/reference-snap-preact-middleware'); + } else if (hashId.match(/^\/start-github/)) { + router.replace('/build-deploy'); + } else if (hashId.match(/^\/start-setup/)) { + router.replace('/snap-setup'); + } + } + } + + // highlight code blocks + document.querySelectorAll('pre code').forEach((block) => { + hljs.highlightElement(block); + }); + + const handleScroll = debounce(() => { + if (window.scrollY > lastScrollY) { + lastScrolledUp = false; + } else { + lastScrolledUp = true; + } + lastScrollY = window.scrollY; + }, DEBOUNCE_DELAY); + + window.addEventListener('scroll', handleScroll); + + function updateLegend(id) { + if (preventLegendUpdate && !id) return; + + document.querySelectorAll(`.legend a`).forEach((item) => { + item.classList.remove('active'); + }); + const activeHeadingId = id || headingIdsInView[lastScrolledUp ? 0 : headingIdsInView.length - 1]; + const legendItem = document.querySelector(`.legend a[data-id="${activeHeadingId}"]`); + if (legendItem) { + legendItem.classList.add('active'); + } + } + + // update active legend item when clicked + // setTimeout is needed to prevent the legend from updating if observer fires + window.updateLegend = (id) => { + router.push({ hash: '#' + id }); + document.getElementById(id).scrollIntoView({ behavior: 'instant' }); + setTimeout(() => updateLegend(id), 1); + }; + + // adds ids to headings for permalinks and handles clicks to copy to clipboard + const headingsRaw = Array.from(document.querySelectorAll('#content h2, #content h3')); + const headings = headingsRaw.map((heading) => { + heading.role = 'link'; + const id = createHeadingId(heading); + heading.id = id; + heading.addEventListener('click', () => { + const url = window.location.origin + window.location.pathname + '#' + id; + navigator.clipboard.writeText(url); + router.push({ hash: '#' + id }); + const span = document.createElement('span'); + span.textContent = 'Permalink copied!'; + span.classList.add('permalink'); + heading.prepend(span); + window.setTimeout(function () { + span.remove(); + }, SCROLL_TO_HEADING_DELAY); + }); + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + headingIdsInView.push(entry.target.id); + } else { + headingIdsInView = headingIdsInView.filter((item) => item !== entry.target.id); + } + headingIdsInView.sort((a, b) => { + const aIndex = headingsRaw.findIndex((h) => h.id === a); + const bIndex = headingsRaw.findIndex((h) => h.id === b); + return aIndex - bIndex; + }); + updateLegend(); + }); + }); + observer.observe(heading); + return heading; + }); + + // adds a legend to the top of the page with links to the headings + if (headings.length) { + const legend = document.createElement('div'); + legend.classList.add('legend'); + const title = document.querySelector('#content h1')?.textContent; + legend.innerHTML = + `
` + + (title ? `
${title}
` : '') + + ` +
    + ${headings + .map((h) => { + const id = h.id; + const text = h.textContent; + const level = h.tagName.toLowerCase(); + return `
  • ${text}
  • `; + }) + .join('')} +
+
`; + document.getElementById('content').prepend(legend); + // scroll active legend item into view + const activeLegendItem = document.querySelector('.legend a.active'); + if (activeLegendItem) { + activeLegendItem.scrollIntoView({ behavior: 'instant' }); + } + } + }; + + function debounce(func, wait) { + let timeout; + return function executedFunction(...args) { + const later = () => { + clearTimeout(timeout); + func(...args); + }; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + } + + function createHeadingId(heading) { + return heading.id || 'id-' + heading.textContent.toLowerCase().replace(/ /g, '-'); + } +}); diff --git a/docs/404.md b/docs/404.md index 50ba99565..883d8cf10 100644 --- a/docs/404.md +++ b/docs/404.md @@ -1,4 +1,4 @@
- 404 - This link is broken... sorry. + This link may no longer exist. Please find the content you are looking for in the side navigation or attempt to search for it.
\ No newline at end of file diff --git a/docs/ABOUT.md b/docs/ABOUT.md index 926994f77..00dd1118b 100644 --- a/docs/ABOUT.md +++ b/docs/ABOUT.md @@ -1,15 +1,39 @@ -## What is Snap? +# Snap Documentation -Snap is not an acronym! Snap is Searchspring's open source SDK for integrating into front end web apps. It replaces the previous version - v3. +Snap is not an acronym! Snap is an open source SDK for building e-commerce experiences powered by Searchspring. -Snap supports Searchspring's latest features and API release for lightning fast performance. +The SDK includes multiple core packages published to npm that in combination with each other, provide the complete front-end tooling for building e-commerce experiences with Searchspring. However to simplify usage, the `@searchspring/snap-preact` package is an abstraction that combines all core packages into a single dependency in combination with Preact to render the UI. This documentation is primarily focused on the usage of this package. -Snap also supports integration with modern front end frameworks such as React and Vue. In addition, we've also released a full [React component library](https://searchspring.github.io/snap/#/components-preact) to get you up and running quickly. +## Getting Started +This documentation is organized into two sections: Snap Integration and API Integration. Depending on how you're going to integrate Snap, you'll want to reference the correct section to get started. -## Getting Started + + +Additionally, the Reference section contains the most comprehensive and technical material that is common between all integration types and is linked to from other sections. + +### Snap Integration + +A "Snap Integration" is a project that uses the `Snap` export from the `@searchspring/snap-preact` package to build a storefront integration. It provides the ability to create multiple controllers, custom plugins, and full custom markup to match the storefront markup and inherit styles. It is the most flexible and powerful way to integrate Searchspring into your storefront. + +An example Snap Integration project can be found [here](https://github.com/searchspring-implementations/demo.shopify). + +Continue by referencing the [Snap Setup](https://searchspring.github.io/snap/snap-setup) section. + +### Snap Templates Integration + +A "Snap Templates Integration" is a project that uses the `SnapTemplates` export from the `@searchspring/snap-preact` package to build a storefront integration. It is an abstraction of the `Snap` integration that limits the available configuration and does not provide access to the entire project markup. + +Instead, it is based on choosing an optimimzed and prebuilt template and theme while only customizing slight layout changes, theme variables, result card markup, and general style declarations. This integration type allows for a rapid integration of Searchspring to your storefront. + +Continue by referencing the [Snap Templates Integration](https://searchspring.github.io/snap/templates-about) documentation. + +### API Integration + +An "API Integration" is a project that utilizes the Searchspring APIs directly to integrate into your custom storefront project. Although not required, we recommend using just the `@searchspring/snap-client` package to fetch data from Searchspring APIs. + +Continue by referencing the [API Integration](https://searchspring.github.io/snap/snap-client) section. -Check out the [Getting Started](https://searchspring.github.io/snap/#/start-setup) guide to jump right in! ## Contributing @@ -18,8 +42,3 @@ Snap is open source! The repository can be found on [Github](https://github.com/ We invite your participation through Github issues, discussions and pull requests! Please reference Searchspring's [Community Contribution Guidelines](https://github.com/searchspring/community/blob/main/CONTRIBUTING.md) before contributing. - - -## Documentation - -For all Snap documentation, see the [Snap Docs](https://searchspring.github.io/snap/). diff --git a/docs/ADVANCED.md b/docs/ADVANCED.md deleted file mode 100644 index aaae46d58..000000000 --- a/docs/ADVANCED.md +++ /dev/null @@ -1,8 +0,0 @@ -## Advanced Snap Usage - -If a controller or service is needed with configuration outside of what is permited by the Snap abstraction layer, one can be created by following the docs in this section. - -It is recommended to use the Snap abstraction layer packages whenever possible - these packages quickly create Snap controllers using a config based interface. Underneath the hood the Snap abstraction packages utilize all of the core Snap packages that will be outlined here. - -### Snap abstraction Packages -[@searchspring/snap-preact](https://github.com/searchspring/snap/tree/main/packages/snap-preact) \ No newline at end of file diff --git a/docs/ADVANCED_AUTOCOMPLETE.md b/docs/ADVANCED_AUTOCOMPLETE.md deleted file mode 100644 index 9eee74495..000000000 --- a/docs/ADVANCED_AUTOCOMPLETE.md +++ /dev/null @@ -1,102 +0,0 @@ -## Autocomplete -To set up Autocomplete using Snap, we'll need to create a `AutocompleteController` instance, which requires `AutocompleteControllerConfig` and `ControllerServices` objects to instantiate. For more details see the [`AutocompleteController docs`](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Autocomplete). - -### Config (AutocompleteControllerConfig) -Let's define an `AutocompleteControllerConfig` object: - -```typescript -const autocompleteConfig = { - id: 'autocomplete', - selector: '#search_query', - globals: { - suggestions: { - count: 4, - }, - pagination: { - pageSize: 6, - }, - }, - settings: { - initializeFromUrl: true, - syncInputs: false, - facets: { - trim: true - }, - }, -} -``` - -### Autocomplete Controller Services -The `ControllerServices` object contains all of the controller's dependencies. Note the difference between SearchController's ControllerServices is the different store. Here we are using `AutocompleteStore` - -Note that the `UrlManager` is utilizing the `UrlTranslator` which will use `'q'` as the URL query parameter. This can be overwritten to use `'search_query'` by providing a by providing a `parameters.core.query` [config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) such as in this example: - -```typescript -const autocompleteUrlManager = new UrlManager(new UrlTranslator({ parameters: core: { query: { name: 'search_query' } } }), reactLinker).detach(); -const autocompleteControllerServices = { - client, - store: new AutocompleteStore(autocompleteConfig, { urlManager: autocompleteUrlManager }), - urlManager: new UrlManager(new UrlTranslator({ queryParameter: 'search_query' }), reactLinker), - eventManager: new EventManager(), - profiler: new Profiler(), - logger: new Logger(), - tracker -} -``` - -The translator type should be the same between Autocomplete and Search Controllers in order for compatible URLs to be generated. - -Note: `client` and `tracker` are shared services and are defined in previous steps. - -### Instantiation -With the `AutocompleteControllerConfig` and `ControllerServices` defined, we can now create an instance of a `AutocompleteController`. - -```typescript -const autocompleteController = new AutocompleteController(autocompleteConfig, autocompleteControllerServices); -``` - -### Middleware -Autocomplete supports middleware to hook into various events using `plugin` and `on` methods. See [Search Middlewear](https://github.com/searchspring/snap/blob/main/docs/SEARCH.md) for usage - - -### DomTargeter -Similar to Search DomTargeter, the following example shows how to use the DomTargeter with an `AutocompleteController` and passing that controller as a prop to a `Autocomplete` component (not shown). After the target has been found it injects a new element ('.ss-ac-target') and then uses the Preact render function to render the component into the newly created element. - -For further usage and documentation, see [DomTargeter](https://github.com/searchspring/snap/tree/main/packages/snap-toolbox/src/DomTargeter). - -```typescript -new DomTargeter( - [ - { - selector: autocompleteController.config.selector, // input element that we are binding to - inject: { - action: 'after', // injecting an element after the input - element: (target, origElement) => { - const acContainer = document.createElement('div'); - acContainer.id = 'ss-ac-target'; - acContainer.addEventListener('click', (e) => { - e.stopPropagation(); - }); - return acContainer; - }, - }, - }, - ], - (target, injectedElem, inputElem) => { - // bind to config selector - autocompleteController.bind(); - render(, injectedElem); - } -); -``` - -### Initialize -Optionally initialize the controller by invoking its `init` method. This will subscribe to any `UrlManager` state changes and fire the `init` event and any attached middleware. This will happen automatically when invoking the `search` method for the first time. - -```typescript -autocompleteController.init(); -``` - -### Bind inputs - -Invoking the AutocompleteController `bind` method will bind the controller instance to all selector elements found. In this example, this occurs in the DomTargeter callback method (`onTarget`) when a selector is found. diff --git a/docs/ADVANCED_CLIENT.md b/docs/ADVANCED_CLIENT.md deleted file mode 100644 index 553b40ae9..000000000 --- a/docs/ADVANCED_CLIENT.md +++ /dev/null @@ -1,19 +0,0 @@ -## Snap Client -Next, let's define Snap Client as it is required for all Snap controller instances. Typically the Client is shared across all Snap controllers. - -```typescript -const client = new Client(globals); -``` - -The Snap Client requires `ClientGlobals` for instantiation. - -### Global Config -The `ClientGlobals` object can be used to set client search parameters that will apply to all requests made with the client (and subsequently any controllers using the client as a service). Typically only the `siteId` will be set here, but could be used for setting globally utilized background filters and/or sorts as well. - -You can find your Searchspring `siteId` in the [Searchspring Management Console](https://manage.searchspring.net) and define it directly: - -```typescript -const globals = { - siteId: 'a1b2c3', -}; -``` diff --git a/docs/ADVANCED_FINDER.md b/docs/ADVANCED_FINDER.md deleted file mode 100644 index 84d3eb6cf..000000000 --- a/docs/ADVANCED_FINDER.md +++ /dev/null @@ -1,123 +0,0 @@ -## Finder -To set up a product Finder using Snap, we'll need to create a `FinderController` instance, which requires `FinderControllerConfig` and `ControllerServices` objects to instantiate. For more details see the [`FinderController docs`](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Finder). - -### Config (FinderControllerConfig) -There are two types of Finder configurations, a Hierarchy and Non-Hierarchy. The difference is the type of field being used and how it is configured in the Searchspring Management Console. - -#### Hierarchy Config -To use a Hierarchy configuration, ensure that the config's `fields` array contain a single entry, and that the field is of type `hierarchy` in the Searchspring Management Console. Here is an example of a Hierarchy `FinderControllerConfig` object: - -```typescript -const finderConfig = { - id: 'finder', - url: '/search' - fields: [{ - field: 'ss_tire', - label: 'Wheel Finder', - levels: ['Year', 'Make', 'Model', 'Wheel Size'] - }] -} -``` - -#### Non-Hierarchy Config -To use a Non-Hierarchy configuration, multiple `fields` are specified. All fields must have a `type` or `value` and NOT `hierarchy`. Facet types can be configured in the Searchspring Management Console. Here is an example of a Non-Hierarchy `FinderControllerConfig` object: - -```typescript -const finderConfig = { - id: 'finder', - url: '/search', - fields: [ - { - field: 'custom_wheel_size', - label: 'Size' - }, - { - field: 'custom_wheel_width', - label: 'Width' - }, - { - field: 'custom_wheel_bolt_pattern', - label: 'Bolt Pattern' - }, - { - field: 'custom_color', - label: 'Color' - } - ] -}; -``` - -Note: When using fields that are not of hierarchy type, `levels` are not required. - -### ControllerServices -The `ControllerServices` object contains all of the controller's dependencies. - -Note that the `UrlManager` is created separately because it is a shared dependency; it is also a service needed for the `FinderStore`. The `UrlManager` is utilizing the `UrlTranslator` which will use `'q'` as the default URL query parameter. This can be overwritten to use `'search_query'` by providing a `parameters.core.query` [config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) such as in this example: - -```typescript -const finderUrlManager = new UrlManager(new UrlTranslator({ parameters: core: { query: { name: 'search_query' } } }), reactLinker).detach(0); -const finderControllerServices = { - client, - store: new FinderStore(finderConfig, { urlManager: finderUrlManager }), - urlManager: finderUrlManager, - eventManager: new EventManager(), - profiler: new Profiler(), - logger: new Logger(), - tracker -} -``` - -Note: `client` and `tracker` are shared services and are defined in previous steps. - -### Instantiation -With the `FinderControllerConfig` and `ControllerServices` defined, we can now create an instance of a `FinderController`. - -```typescript -const finderController = new FinderController(finderConfig, finderControllerServices); -``` - -### Middleware -Finders support middleware to hook into various events using `plugin` and `on` methods. See [Search Middlewear](https://github.com/searchspring/snap/blob/main/docs/SEARCH.md) for usage. - - -### DomTargeter -Similar to Search DomTargeter, the following example shows how to use the DomTargeter with a `FinderController` and passing that controller as a prop to a `Finder` UI component (not shown). It uses the Preact render function to render the component after the target has been found. - -For further usage and documentation, see [DomTargeter](https://github.com/searchspring/snap/tree/main/packages/snap-toolbox/src/DomTargeter). - -```typescript -const finderTarget = new DomTargeter( - [ - { - selector: '#finder-target-selector', // CSS selector for element to render component into - }, - ], - async (target, elem) => { - // await search after target element is found - await finderController.search(); - - render(, elem); - } -); -``` - -### Initialize -Optionally initialize the controller by invoking its `init` method. This will subscribe to any `UrlManager` state changes and fire the `init` event and any attached middleware. This will happen automatically when invoking the `search` method for the first time. - -```typescript -finderController.init(); -``` - -### Search -After the controller has been initialized, the search method must be invoked for the finder to fetch its initial data. In the example above, this is being invoked in the DomTargeter. - -```typescript -finderController.search(); -``` - -### Find -After selection(s) have been made, the user will click on a 'Find' button. This click event should invoke the `find` method of the Finder controller which will redirect to the specified `url` in the config with it's selection parameters attached. - -```typescript -finderController.find(); -``` diff --git a/docs/ADVANCED_INSTALLATION.md b/docs/ADVANCED_INSTALLATION.md deleted file mode 100644 index 28df5332e..000000000 --- a/docs/ADVANCED_INSTALLATION.md +++ /dev/null @@ -1,17 +0,0 @@ -## Installation - -```shell -npm install --save @searchspring/snap-client @searchspring/snap-url-manager @searchspring/snap-event-manager @searchspring/snap-profiler @searchspring/snap-logger @searchspring/snap-tracker @searchspring/snap-toolbox @searchspring/snap-controller @searchspring/snap-store-mobx -``` - -```typescript -import { Client } from '@searchspring/snap-client'; -import { UrlManager, UrlTranslator, reactLinker } from '@searchspring/snap-url-manager'; -import { EventManager } from '@searchspring/snap-event-manager'; -import { Profiler } from '@searchspring/snap-profiler'; -import { Logger } from '@searchspring/snap-logger'; -import { Tracker } from '@searchspring/snap-tracker'; -import { DomTargeter } from '@searchspring/snap-toolbox'; -import { SearchController, AutocompleteController, FinderController } from '@searchspring/snap-controller'; -import { SearchStore, AutocompleteStore, FinderStore } from '@searchspring/snap-store-mobx'; -``` diff --git a/docs/ADVANCED_SEARCH.md b/docs/ADVANCED_SEARCH.md deleted file mode 100644 index 6477a8eda..000000000 --- a/docs/ADVANCED_SEARCH.md +++ /dev/null @@ -1,150 +0,0 @@ -

Search

- -To set up Search using Snap, we'll need to create a `SearchController` instance, which requires `SearchControllerConfig` and `ControllerServices` objects to instantiate. For more details see the [`SearchController docs`](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Search). - -### Config (SearchControllerConfig) -Let's define a `SearchControllerConfig` object: -```typescript -const searchConfig = { - id: 'search', - globals: { - filters: [], - }, - settings: { - redirects: { - merchandising: true, - singleResult: true, - }, - facets: { - trim: true, - } - }, -}; -``` - - -### Category Pages / Background Filters -Optionally, apply filters from the page's content to the SearchControllerConfig `globals.filters` property. The controller globals are similar to the client globals in that all search requests will include the parameters specified. This can be used to configure category/brand pages, or other special filtering to apply to the current page's search requests. - -For example, if a global variable `snapConfig` exists on the page (must be defined prior to our Snap script): - -```html - -``` - -```typescript -if (snapConfig?.category) { - searchConfig.globals.filters.push({ - type: 'value', - background: true, - field: 'categories_hierarchy', - value: snapConfig.category.value, - }); -} -``` - - -### ControllerServices -The `ControllerServices` object contains all of the controller's dependencies. - -Note that the `UrlManager` is created separately because it is a shared dependency; it is also a service needed for the `SearchStore`. The `UrlManager` is utilizing the `UrlTranslator` which will use `'q'` as the default URL query parameter. This can be overwritten to use `'search_query'` by providing a `parameters.core.query` [config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) such as in this example: - -```typescript -const searchUrlManager = new UrlManager(new UrlTranslator({ parameters: core: { query: { name: 'search_query' } } }), reactLinker); -const searchControllerServices = { - client, - store: new SearchStore(searchConfig, { urlManager, searchUrlManager }), - urlManager: searchUrlManager, - eventManager: new EventManager(), - profiler: new Profiler(), - logger: new Logger(), - tracker -} -``` - -Note: `client` and `tracker` are shared services and are defined in previous steps. - -### Instantiation -With the `SearchControllerConfig` and `ControllerServices` defined, we can now create an instance of a `SearchController`. - -```typescript -const searchController = new SearchController(searchConfig, searchControllerServices); -``` - -

Middleware

- -Now that our `SearchController` is instantiated (using `searchController` variable), we can optionally attach middleware to hook into various events. There are two ways of doing this, using the Controller's `on` or `plugin` methods. - -#### via `on` method: - -```typescript -searchController.on('afterStore', async ({ controller }, next) => { - controller.log.debug('store', controller.store.toJSON()); - await next(); -}) -``` - -#### via `plugin` method (to attach groups of middleware): - -```typescript -const middleware = (controller) => { - controller.on('init', async({ controller }, next) => { - controller.log.imageText({ - url: 'https://searchspring.com/wp-content/themes/SearchSpring-Theme/dist/images/favicons/favicon.svg', - text: 'snap integration initialized', - style: `color: ${controller.log.colors.indigo}; font-weight: bold;`, - }); - - await next(); - }); - // log the store - controller.on('afterStore', async({ controller }, next) => { - controller.log.debug('store', controller.store.toJSON()); - await next(); - }); -}; - -searchController.plugin(middleware); -``` - -

DomTargeter

- -`DomTargeter` is a utility for rending components in specified DOM targets. The following example shows how to use the DomTargeter with a `SearchController` and passing that controller as a prop to a `Content` component (not shown). It uses the Preact render function to render the component after the target has been found. - -For further usage and documentation, see [DomTargeter](https://github.com/searchspring/snap/tree/main/packages/snap-toolbox/src/DomTargeter). - -```typescript -const contentTarget = new DomTargeter( - [ - { - selector: '#searchspring-content', // CSS selector for element to render component into - }, - ], - (target, elem) => { - // run search after finding target - controller.search(); - render(, elem); - } -); -``` - -### Initialize -Optionally initialize the controller by invoking its `init` method. This will subscribe to any `UrlManager` state changes and fire the `init` event and any attached middleware. This will happen automatically when invoking the `search` method for the first time. - -```typescript -searchController.init(); -``` - -### Perform Search - -Invoking the SearchController `search` method will perform a search. In this example, this is being done after the Domtargeter finds the element selector `#searchspring-content`. Simultaneously the `Content` component will be rendered into the element selector. diff --git a/docs/ADVANCED_TRACKER.md b/docs/ADVANCED_TRACKER.md deleted file mode 100644 index b20116e7a..000000000 --- a/docs/ADVANCED_TRACKER.md +++ /dev/null @@ -1,19 +0,0 @@ -## Snap Tracker -The Snap Tracker, like the Client, is required for all Snap controller instances and is shared across all Snap controllers. - -```typescript -const tracker = new Tracker(globals); -``` - -The Snap Tracker requires `TrackerGlobals` for instantiation. - -### Global Config -The `TrackerGlobals` object requires only a `siteId`. - -You can find your Searchspring `siteId` in the [Searchspring Management Console](https://manage.searchspring.net) and define it directly: - -```typescript -const globals = { - siteId: 'a1b2c3', -}; -``` \ No newline at end of file diff --git a/docs/BUILD_DEPLOY.md b/docs/BUILD_DEPLOY.md new file mode 100644 index 000000000..099d97bb7 --- /dev/null +++ b/docs/BUILD_DEPLOY.md @@ -0,0 +1,189 @@ +# Building + +To build the project, run the following command: + +```sh +npm run build +``` + +The build output files are placed in the `dist` directory. Each project is configured to use Webpack to build the project files and will build two sets of files: A modern build and a universal build. + +## Deploy + +If you are managing the project and repository (also referred to as "Self-Snap"), you will need handle the deployment of the build files to your CDN or hosting provider. (ie. Shopify, BigCommerce, Magento, AWS S3, etc..) + +If the URL above is used it will result in a 403 Error. + +To host your own build files follow the below steps in your project. + +1. Ensure you have changed directories so that you are in the root of the project directory +2. In your terminal run the command `npm run build`, will output build files to `./dist` +3. Navigate to `./dist` and copy the generated build files +4. Go to the codebase of your E-commerce platform (Shopify, BigCommerce, Magento, etc.) and copy/paste the generated build files in a directory (most platforms have an ***assets*** directory) +5. On the frontend of the site, add a script block as outlined in the [integration](https://searchspring.github.io/snap/build-deploy-integration) section - be sure to change the `src` attribute to point to the `bundle.js` file and align the URL with your self-hosted build files (eg: /assets/bundle.js) + + + + +## Deploy to Searchspring CDN + +**Deploying to Searchspring CDN is only possible if the repository is managed by the Searchspring [Github organization](https://github.com/searchspring-implementations)**. Repositories in this organization are typically managed by the Searchspring professional services team and deployed via a CI/CD pipeline using the [snap-action](https://github.com/searchspring/snap-action) Github Action. An invitation can be requested for collaboration. + +Github action runs triggered on the default branch `production` will build and deploy bundle files to this URL: + +`https://snapui.searchspring.io/[your_site_id]/bundle.js` + +Builds on different branch names will be deployed to: + +`https://snapui.searchspring.io/[your_site_id]/[branch]/bundle.js` + +### Github Repository Requirements + +- Repository must be managed by the Searchspring [Github organization](https://github.com/searchspring-implementations) +- Repository must have a default branch named `production` +- Repository must have repository secrets for each siteId in the repository. Found at `https://github.com/[owner]/[repository]/settings/secrets/actions` + - Secret Key Name: `WEBSITE_SECRET_KEY_[SITEID]` where `[SITEID]` should be replaced with the 6 character alphanumeric siteId found in the [Searchspring Management Console](https://manage.searchspring.net). For example: `WEBSITE_SECRET_KEY_ABC123` + - Value: `secretKey` located adjacent to the siteId in the [Searchspring Management Console](https://manage.searchspring.net) +- Repository must have a `snap-action` workflow file in the `.github/workflows` directory. See section below. +- Repository must have a `package.json` file that contains all siteIds associated with this project. See section below. + +#### Github Action + +The [snap-action](https://github.com/searchspring/snap-action/) can be used by creating a new github workflow file (ie. `.github/workflows/deploy.yml`) + +The Snap Action requires additional parameters not shown in the example below. See [snap-action](https://github.com/searchspring/snap-action/) for additional documentation. + +```yml +on: [push, pull_request] + +jobs: + Publish: + runs-on: ubuntu-latest + name: Snap Action + steps: + - name: Checkout action + uses: actions/checkout@v2 + with: + repository: searchspring/snap-action + - name: Run @searchspring/snap-action + uses: ./ +``` + +#### Project package.json configuration + +The package.json file must contain all siteIds associated with this project. This data is also used by the Snapfu CLI for various features. + + + +Single siteId example: + +```json +{ + ... + "searchspring": { + "siteId": { + "abc123": { + "name": "site1.com" + } + }, + } +} +``` + +Multi siteId example: + +```json +{ + ... + "searchspring": { + "siteId": { + "abc123": { + "name": "site1.com" + }, + "def456": { + "name": "site1.com.au" + } + }, + } +} +``` + +### Branch Overrides + +This functionality is only currently possible with Searchspring managed Snap repositories (https://github.com/searchspring-implementations). + +While browsing a page that contains a Snap integration, appending the `?searchspring-preview=[branchname]` query parameter to the URL will stop the execution of the existing script, and load the build from the `[branchname]` branch `https://snapui.searchspring.io/[siteid]/[branchname]/bundle.js` + +You will see an interface overlay on the bottom right of the viewport indicating if successful and details of the build. + + + +This will also be persisted across page navigation. To stop previewing a branch build, you must click the `Stop Preview` button in the interface or clear the `ssBranch` cookie. The interface can also be minimized. + +## Modern vs Universal Builds + +If your project is hosted in Searchspring's Github organization, we'll automatically handle serving the appropriate version of the build files for you. However if you are hosting your own build files, you can either: + +- only serve the modern build to all users, however this will break functionallity for IE11. +- change the build targets, although this may impact core web vitals negatively. +- serve the appropriate bundle via a check of the userAgent. More information below. + +Always serve the build to modern browsers and the universal build only to legacy browsers. Serving universal builds to modern browsers negatively impacts Core Web Vitals: + +```html + + + + + +``` + +The modern build is used for projects targeting the latest browsers supporting the latest JavaScript features (ES6 and above). Example modern build files: `bundle.js` & `bundle.chunk.[hash].js` + +A browser is considered modern based on the [@searchspring/browserslist-config-snap modern](https://github.com/searchspring/browserslist-config-snap/blob/main/modern/index.js) rules and is included in the preconfigured scaffold. + + +The universal build is used for projects targeting legacy browsers and will transpile any usage of modern JavaScript to ES5 as well as polyfill any missing browser features. If you are not targeting legacy browsers, you can omit deploying the universal built files that are prefixed with `universal.` - Example: `universal.bundle.js` and `universal.bundle.chunk.[hash].js` + +A browser is considered legacy based on the [@searchspring/browserslist-config-snap universal](https://github.com/searchspring/browserslist-config-snap/blob/main/universal/index.js) rules and is included in the preconfigured scaffold. + +However, if you are targeting legacy browsers, it is not recommended to always serve the universal build files to all browsers—including modern browsers—as this will impact Core Web Vitals and performance negatively. + +Therefore, you will require a method for switching the front-end script `src` to point to the appropriate version of the build files depending on whether the browser is modern or legacy. This can be done in many ways, including: + +- Server-side checking the userAgent and serving the appropriate version of the build files. +- Front-end checking the userAgent and serving the appropriate version of the build files. +- Lambda function serving the appropriate version of the build files based on the userAgent. + +The following is an example of a regex that would match the versions of the `browserlist-config-snap@1.0.7` rules: + +```js +module.exports = /((CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS)[ +]+(14|(1[5-9]|[2-9]\d|\d{3,})|15|(1[6-9]|[2-9]\d|\d{3,}))[_.]\d+(?:[_.]\d+)?)|((?:Chrome).*OPR\/(77|(7[8-9]|[8-9]\d|\d{3,}))\.\d+\.\d+)|(Edge\/(91|(9[2-9]|\d{3,}))(?:\.\d+)?)|((Chromium|Chrome)\/(91|(9[2-9]|\d{3,}))\.\d+(?:\.\d+)?)|(Version\/(14|(1[5-9]|[2-9]\d|\d{3,})|15|(1[6-9]|[2-9]\d|\d{3,}))\.\d+(?:\.\d+)? Safari\/)|(Firefox\/(74|(7[5-9]|[8-9]\d|\d{3,}))\.\d+\.\d+)|(Firefox\/(74|(7[5-9]|[8-9]\d|\d{3,}))\.\d+(pre|[ab]\d+[a-z]*)?)/; +``` +(regex generated using [browserslist-useragent-regexp](https://www.npmjs.com/package/browserslist-useragent-regexp)) + + + +## Build Tools + +Webpack is the default choice of build tooling that all Snapfu templates include and will be preconfigured. + +If you are integrating `@searchspring/snap-preact` using other build tools, you may require certain plugins to ensure preact compatibility. + +We hope to maintain this page with the most common build tools and their respective plugins as we discover them. + + +### Vite + +[@preact/preset-vite](https://github.com/preactjs/presets/tree/main/packages/preset-vite) is a plugin for Vite that allows you to use Preact in your Vite project. + +```js +// vite.config.js +import { defineConfig } from 'vite' +import preact from '@preact/preset-vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [preact()], +}) +``` \ No newline at end of file diff --git a/docs/BUILD_DEPLOY_CHECKLIST.md b/docs/BUILD_DEPLOY_CHECKLIST.md new file mode 100644 index 000000000..204f3e4b9 --- /dev/null +++ b/docs/BUILD_DEPLOY_CHECKLIST.md @@ -0,0 +1,35 @@ +# QA Checklist + +## Integration Code Checklist + +- [Background Filters](./snap-background-filters) are being read from script context and applied as client globals to handle Category pages. + +- (If applicable) [Foreground Filters](./snap-background-filters) are added as client globals to handle custom filtering (ie. only show results in stock) + +- Merchandising [Banner](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-banner--footer) components have been added for all banner locations: `header`, `banner`, `footer`, `left`. See [Search > Search Store Merchandising](./snap-search#searchcontrollerstoremerchandising) + +- [InlineBanner](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-inlinebanner--default) component is conditionally rendered in your Results component. See [Search > Search Store Results](./snap-search#searchcontrollerstoreresults) + +- [Result impression tracking](./snap-tracking#impressions) has been added. Confirm via browser dev tools network tab that beacon impression events are being sent for results in Search, Category, Autocomplete, and Recommendations + +- [Result click tracking](./snap-tracking#product-click) has been added. (not required if using `withTracking` or `ResultTracker` as this functionallity is handled by those components) + +- [Result add to cart tracking](./snap-tracking#product-add-to-cart) has been added. Confirm via browser dev tools network tab that beacon add to cart events are being sent for results in Search, Category, Autocomplete, and Recommendations + +- (If enabled) Self-configured [Badges](./snap-badges) are added to your Result component + +- No Results component text has been personalized for your store (ie. contains support contact and storefront address information) + +## Platform Integration Checklist + +- [Shopper Login tracking](./snap-tracking#shopper-login) has been added. Confirm via browser dev tools network tab that beacon shopper login events are being sent only when a shopper is logged in + +- [Currency tracking](./snap-tracking#currency) has been added. Confirm via browser dev tools network tab that beacon events contain currency code in data payload for any event. If using a single currency, this is not required. + +- [Product view tracking](./snap-tracking#product-view). Confirm via browser dev tools that last viewed products cookie is being updated when a product is viewed. + +- [Order transaction tracking](./snap-tracking#order-transaction) has been added. Confirm via browser dev tools network tab that beacon order transaction events are being sent only when an order is completed. + +- [Cart contents tracking](./snap-tracking#cart-contents) has been added. Confirm via browser dev tools network tab that beacon cart add and remove events are being sent when cart contents change. + +- (If applicable) Realtime recommendations requires [Cart Attribute Tracking](./snap-tracking#cart-attribute-tracking) diff --git a/docs/INTEGRATION_CONTEXT.md b/docs/BUILD_DEPLOY_INTEGRATION.md similarity index 62% rename from docs/INTEGRATION_CONTEXT.md rename to docs/BUILD_DEPLOY_INTEGRATION.md index a747c49a6..2aadc0a09 100644 --- a/docs/INTEGRATION_CONTEXT.md +++ b/docs/BUILD_DEPLOY_INTEGRATION.md @@ -1,14 +1,47 @@ +# Integration + +After building the project and uploading the build files to your CDN or hosting provider, you will need to add a script tag to your storefront. + + +```html + +``` + +The bundle should be included in the tag, ideally near the top of the node, and should not have a 'defer' or 'async' attribute. This location is important in order to start fetching results and as soon as possible. This placement prior to any body elements also serves to allow for the hiding of targeted elements that contain content - this preventing a flash when the contents change upon injection. + + +```html + + + + + + + Snap Integration Example + + + + +
+ + +``` + ## Context Variables -Context variables are conditionally rendered within the `bundle.js` script's innerHTML via server side code or template logic. They provide various context variables that can be utilized by the Snap integration. Typically these variables are used to specify category page details (for [background filtering](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_BACKGROUND_FILTERS.md)), shopper details (for personalization), merchandising segmentation, or any other custom variables needed for the integration. +Context variables are conditionally rendered within the `bundle.js` script's innerHTML via server side code or template logic. They provide various context variables that can be utilized by the Snap integration. Typically these variables are used to specify category page details (for [background filtering](https://searchspring.github.io/snap/snap-background-filters)), shopper details (for personalization), merchandising segmentation, or any other custom variables needed for the integration. -The innerHTML of the script MUST only contain variable assignments without `var`, `let`, or `const`. Each declaration should end with a semi-colon to ensure minification does not impact the functions ability to parse the innerHTML. These variables are retrieved using the [getContext](https://github.com/searchspring/snap/tree/main/packages/snap-toolbox/src/getContext) function at run time. +The innerHTML of the script MUST only contain variable assignments without `var`, `let`, or `const`. Each declaration should end with a semi-colon to ensure minification does not impact the functions ability to parse the innerHTML. These variables are retrieved using the [getContext](https://searchspring.github.io/snap/reference-toolbox-getcontext) function at run time. There are a few core context variables utilized by Snap, `shopper`, `merchandising` and `config` - these are reserved context variable names and should not be utilized for custom context functionality. | Option | Value | Page | Description | |---|---|:---:|---| -| shopper.id | logged in user unique identifier | all | required for personalization functionallity | +| shopper.id | logged in user unique identifier | all | required for personalization functionality | | shopper.cart | array of cart objects, each object in the array can contain `uid` (required), `childUid`, `sku`, `childSku`, `price`, `qty` | all | current cart contents, required if checkout process does not contain a dedicated cart page (ie. slideout cart) | | currency.code | currency code string, ie. 'EUR' (ISO 4217) | all | currency code of the shopper's cart contents or order confirmation. Used for beacon events containing pricing data | | merchandising.segments | array of strings used for merchandising | any | segmented merchandising allows for custom control over products returned on search requests and must also be setup within the Searchspring Management Console (SMC) | @@ -20,7 +53,7 @@ There are a few core context variables utilized by Snap, `shopper`, `merchandisi The custom variable example below shows a custom context being added for 'page'. The value would typically be assigned server side using template logic. This would be used to possibly toggle the siteId utilized by the client (to fetch different catalog data) or to modify text or currency displays. ```html - ``` @@ -28,9 +61,9 @@ The custom variable example below shows a custom context being added for 'page'. When used, shopper context should always include at least an `id`; the `cart` contents can optionally be provided to ensure personalization is applied on every page. Standard Snap integrations will automatically take this context data and apply it for personalization. ```html - -``` \ No newline at end of file +``` + +## Content Security Policy + +If your site requires a strict [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP), an entry of `https://*.searchspring.io` should be added to your CSP configuration to ensure Searchspring is functional. + diff --git a/docs/BUILD_DEPLOY_INTEGRATION_BIGCOMMERCE.md b/docs/BUILD_DEPLOY_INTEGRATION_BIGCOMMERCE.md new file mode 100644 index 000000000..951ae7e06 --- /dev/null +++ b/docs/BUILD_DEPLOY_INTEGRATION_BIGCOMMERCE.md @@ -0,0 +1,461 @@ +# BigCommerce Integration + +## Searchspring Management Console Actions + +### Update Field Settings + +On the [Field Settings page](https://manage.searchspring.net/management/field-settings/display-fields), make sure the following fields are updated: + +| Field | Type | Multi-Valued | Display | +|---|---|:---:|:---:| +| categories_hierarchy | Text | | ✓ | +| brand | Text | No | ✓ | + +If settings are changed, perform an [Update Index](https://manage.searchspring.net/management/index/status). + +## Add IntelliSuggest Tracking + +See [IntelliSuggest Tracking for BigCommerce Stencil](https://searchspring.zendesk.com/hc/en-us/articles/360056186252-IntelliSuggest-Tracking-for-BigCommerce-Stencil). + +## Create a Category Search Page + +> [!WARNING] +> Before creating this category page, ensure that the url does not already exist by going to `https://[domain]/shop`. If the url is active, alternative paths would be `search` or `ssearch`. + +- Products > Product Categories and click "Create a Category". +- Set the category details to the following: + +| Option | Value | +|---|---| +| Name | Searchspring | +| URL | /searchspring/ | +| Parent Category | -- No Parent Category -- | +| Template Layout File | Default (aka category.html) | +| Search Engine Optimization > Page Title | (leave blank) | + +- On the "Product Categories" listing, set the "Searchspring" category to Visible in Menu > No ("x" icon). +- On the "Product Categories" listing, click Actions > New Sub Category to the right of the "Searchspring" row. +- Set the category details to the following: + +| Option | Value | +|---|---| +| Name | Search Results | +| URL | /shop/ | +| Parent Category | Searchspring | +| Template Layout File | Default (aka category.html) | +| Search Engine Optimization > Page Title | Search Results | + +- Leave the "shop" page as Visible in Menu > Yes ("check" icon). +- You do not need to add products to these categories. +- You can preview the category by going to `https://[domain]/shop`. + +> [!CAUTION] +> If you are not seeing your category page, it may be because the page is locked down (by default) to certain customer groups. Go to Customers > Customer Groups > "..." under Actions > Edit > Group Details > Group Access. + +## Theme Integration + +Next we'll integrate Searchspring into the theme. + +- Create a copy of the current theme to integrate on. It is recommended to do so rather than integrating directly on the live theme initially to allow for testing prior to going live. +- Storefront > My Themes > Live theme > Advanced > Make a Copy. +- Rename the duplicated theme to something like "Theme Name - Searchspring". +- Previewing an unpublished theme copy is not particularly easy with BigCommerce as there is no preview theme link. First, right click on "My Themes" in the left navigation and open this in a new tab. An extra tab is needed so you can go back through the BigCommerce admin and edit code. On either tab, next to the copy of your theme there should be an icon that looks like three dots "...". Click this then "Customize". If asked which theme style you want to view, pick the option that looks like the site (typically the first) and "Continue". This tab will now be used to preview the theme. + +### Base.html Edits + +- Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > layout > base.html. +- Before the closing `` tag, add the integration snippet. + +#### Search Only + +> [!NOTE] +> If you are only integrating Search page functionality, you can use the following snippet. Otherwise skip this section and continue below to install both search, category, and brand functionality. + +- Update `REPLACE_WITH_YOUR_SITE_ID` with the correct site id. +- If your search page url was not `/shop/`, update this as needed. + +```handlebars +{{!-- START: Searchspring Integration code --}} + +{{!-- define initial variables --}} +{{assignVar 'ss_site_id' 'REPLACE_WITH_YOUR_SITE_ID'}} +{{assignVar 'ss_search_url' '/shop/'}} +{{assignVar 'ss_page_type' 'other'}} + +{{!-- check if on search page --}} +{{#if category}} + {{#contains category.url (getVar 'ss_search_url')}}{{assignVar 'ss_page_type' 'search'}}{{else}}{{assignVar 'ss_page_type' 'category'}}{{/contains}} +{{else if brand}} + {{assignVar 'ss_page_type' 'brand'}} +{{/if}} + +{{!-- create integration script --}} + + +{{!-- END: Searchspring Integration code --}} +``` + +#### Search, Category, and Brand + +- Update `REPLACE_WITH_YOUR_SITE_ID` with the correct site id. +- If your search page url was not `/shop/`, update this as needed. +- Replace the `>` character in the breadcrumb trail if the data is using a different delimiter. + +```handlebars +{{!-- START: Searchspring Integration code --}} + +{{!-- define initial variables --}} +{{assignVar 'ss_site_id' 'REPLACE_WITH_YOUR_SITE_ID'}} +{{assignVar 'ss_search_url' '/shop/'}} +{{assignVar 'ss_page_type' 'other'}} +{{assignVar 'ss_is_loaded' 'false'}} + +{{!-- check if results should load --}} +{{#or category brand}} + {{assignVar 'ss_is_loaded' 'true'}} +{{/or}} + +{{!-- check if on search page --}} +{{#if category}} + {{#contains category.url (getVar 'ss_search_url')}}{{assignVar 'ss_page_type' 'search'}}{{else}}{{assignVar 'ss_page_type' 'category'}}{{/contains}} +{{else if brand}} + {{assignVar 'ss_page_type' 'brand'}} +{{/if}} + +{{!-- create integration script --}} + + +{{!-- END: Searchspring Integration code --}} +``` + +### Body Class Name + +(Optional) There may be occasions where you need to add a class name to the `body` element on the search page for styling purposes. + +If `body` tag has no `class` attribute: + +```handlebars + +``` + +If `body` tag has a `class` attribute, ensure to keep the existing class names and append the `ss-shop` class name to the existing list of class names: + +```handlebars + +``` + +## Category and Brand Page Edits + +Next we'll add our target element(s) to the category and brand pages. This is where the Searchspring elements will be injected into, typically two elements are added for a two-column layout: one for content, and one for facets. + +Targets are defined in your Snap configuration and will only be injected into if they exist on the page. + +### Category Page Edits + +- Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > pages > category.html. +- `category.html` is a standard BigCommerce template, but this may not be the file to edit. Look for includes which will tell you where to go, for example: `{{> thing/parent/child}}`. This says that there's additional code for the category page located in another file that has the name "child". +- Once the correct file is found, ensure that all of your search controller targets are added to the category template. Use theme comments to hide the store's default product grid, thus speeding up load time for Searchspring. + +```handlebars +
+{{!-- + +--}} + +
+{{!-- + +--}} +``` + +- Sidebar elements may be within a separate file. Typically this is found in Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > components > category > sidebar.html, which is the sidebar layout specifically for category pages. +- Some category templates will only display sidebar or results content if products are assigned to the category. You can make these elements show on the category search page with additional checks where required. + +Show an element only when on the `shop` category: + +- If your search page url was not `/shop/`, update this as needed. + +```handlebars +{{#contains category.url '/shop/'}} + +{{/contains}} +``` + +### Brand Page Edits + +- Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > pages > brand.html. +- Make sure you are within `brand.html` as `brands.html` file contains the listing of all brands. +- `brand.html` is a standard BigCommerce template, but this may not be the file to edit. Look for includes which will tell you where to go, for example: `{{> thing/parent/child}}`. This says that there's additional code for the brand page located in another file that has the name "child". +- Once the correct file is found, ensure that all of your search controller targets are added to the brand template. Use theme comments to hide the store's default product grid, thus speeding up load time for Searchspring. + +```handlebars +
+{{!-- + +--}} + +
+{{!-- + +--}} +``` + +- Sidebar elements may be within a separate file. Typically this is found in Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > templates > components > brand > sidebar.html, which is the sidebar layout specifically for brand pages. + +## Search Form Updates + +Next we'll update the search form to submit to the search results category page (ie. `/shop/`) we created. + +- Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > components > common > header.html. +- If you do not see the search form in the header, it might be in another snippet. For example: Often in the header file you'll find the following line of code `{{> components/common/quick-search}}`. This means the search form is in quick-search.html. +- Alternate locations: + - Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > components > common > quick-search.html + - Storefront > My Themes > [theme name] > "..." icon > Edit Theme Files > components > common > search-box.html +- Create a copy of the form which you will edit and comment out the old form. This will allow us to retain BigCommerce's default functionality. + +```handlebars +{{!-- + +--}} + +``` + +In your form copy, update the following details: + +| Element | Attribute | Value | +|---|---|---| +| form | method | get | +| form | action | /shop/ | +| input | | remove any `data-search-quick` attribute | +| input[type="hidden"] | | remove any hidden inputs | +| div | | remove any `quickSearchResults` div | + +## Integration Code + +Up until this point, we've added the Searchspring integration to the theme and category/brand pages. + +Now we'll ensure our integration code captures the context variables and sets up the necessary configuration. + +### Check Shopper and Site ID Code + +For tracking shopper data used in personalization features, we need to include code in `src/index.js`. Shopper code should be in `index.js` by default, but if not, follow the below steps. `siteId` context should also be included, but adding it will allow for the implementation of multi-site integrations later. + +```js +// src/index.js +/* searchspring imports */ +import { Snap } from '@searchspring/snap-preact'; +import { getContext } from '@searchspring/snap-toolbox'; + +/* context from script tag */ +const context = getContext(['shopper', 'siteId']); +``` + +### Add Site and Page Objects + +Still within `index.js`, add objects to setup a `site` and `page` config. This may already be present depending on what Snap templates you are using. + +> [!NOTE] +> Update `REPLACE_WITH_YOUR_SITE_ID` with the correct, default site id. + +> [!NOTE] +> If the default query parameter is something other than "search_query", update the parameter. + +> [!WARNING] +> Due to certain issues with BigCommerce integrations, these sites should always change the pagination parameter to not match the default. "p" as the page parameter is usually our default for this platform. + +```js +// src/index.js + +/* set up site details config */ +let site = { + id: context?.siteId ? context.siteId : 'REPLACE_WITH_YOUR_SITE_ID', + currency: 'usd', + lang: 'en', + parameters: { + query: 'search_query', + page: 'p', + }, + features: { + integratedSpellCorrection: { + enabled: true, + }, + }, +}; + +/* set up page details config */ +let isSearch = window.location.href.includes('/shop') || window.location.href.includes('/mockup') || window.location.href.includes('/lighthouse') ? true : false; +let page = { + id: isSearch ? 'shop' : 'other', + title: isSearch ? 'Search Results' : 'Other Page', + type: isSearch ? 'search' : 'other', +}; +``` + +Pass these configs into a plugin and set on `store.custom`: + +```js +// src/plugins/sharedPlugin.js +export const sharedPlugin = (controller, site, page) => { + // set initial custom settings for project + controller.store.custom = { ...controller.store.custom, site: site, page: page }; +}; +``` + +For a fuller example, please see the template repo [here](https://github.com/searchspring/snapfu-template-preact-implementations/tree/production/src). + +### Category and Brand Integration (Optional) + +If integrating on category and brand pages, follow these steps. If not, skip this section. + +Back in `src/index.js`, adjust context to grab additional values for category and brand integration. + +```js +// src/index.js +/* context from script tag */ +const context = getContext(['category', 'brand', 'shopper', 'siteId']); +``` + +Below this, add code to support background filters on a `search` controller. As a best practice, for category pages we use `categories_hierarchy` as it contains a unique, hierarchal path for the category. On brand, the `brand` data should contain the brand name. + +```js +// src/index.js + +/* background filters */ +let backgroundFilters = []; + +if (context.category?.path) { + const categoryPath = replaceCharacters(context.category.path); + const categoryName = replaceCharacters(context.category.name); + + // update page details when on category + page = { + id: categoryPath, + title: categoryName, + type: 'category', + }; + + // set category background filter + backgroundFilters.push({ + field: 'categories_hierarchy', + value: categoryPath, + type: 'value', + background: true, + }); +} else if (context.brand?.name) { + const brandName = replaceCharacters(context.brand.name); + + // update page details when on brand + page = { + id: brandName, + title: brandName, + type: 'brand', + }; + + // set brand background filter + backgroundFilters.push({ + field: 'brand', + value: brandName, + type: 'value', + background: true, + }); +} + +/* replace special characters in values */ +function replaceCharacters(value) { + if (value) { + return value.replace(/\&\;/g, '&').replace(/\<\;/g, '<').replace(/\>\;/g, '>').replace(/\"\;/g, '"').replace(/\'\;/g, "'").replace(/\'\;/g, "'").trim(); + } else { + return ''; + } +} +``` + +To attach the `backgroundFilters` to a `search` controller, they need to be added into a `globals` config. + +```js +// src/index.js +import { sharedPlugin } from './plugins/sharedPlugin'; +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + plugins: [[sharedPlugin, site, page], [contentPlugin]], + settings: { + redirects: { + singleResult: true, + }, + facets: { + pinFiltered: true, + }, + }, + globals: { + filters: backgroundFilters, + }, + }, + targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content/Content')).Content; + }, + }, + ], + }, + ], + }, +}); +``` + +> [!NOTE] +> Typically, you should complete the brand integration even if brand pages are not visible. This gives the client the option to show brand integration later. You can find brand pages by going to `https://[domain]/brands` then click on one of the brands in the listing. + +## Additional Targets (Optional) + +In addition to having two targets for a two-column layout, you may want to inject content into other sections of the page such above the content and sidebar to display information such as the search query. Note the addition of `ss-shop` class before `searchspring-header` to ensure the content is only injected on the search page. + +```js +// src/index.js +targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./content/content/Content')).Content; + }, + hideTarget: true, + }, + { + selector: '#searchspring-sidebar', + component: async () => { + return (await import('./sidebar/sidebar/Sidebar')).Sidebar; + }, + hideTarget: true, + }, + { + selector: '.ss-shop #searchspring-header', + component: async () => { + return (await import('./content/header/Header')).Header; + }, + hideTarget: true, + }, +], +``` diff --git a/docs/BUILD_DEPLOY_INTEGRATION_MAGENTO2.md b/docs/BUILD_DEPLOY_INTEGRATION_MAGENTO2.md new file mode 100644 index 000000000..19a7570d1 --- /dev/null +++ b/docs/BUILD_DEPLOY_INTEGRATION_MAGENTO2.md @@ -0,0 +1,520 @@ +# Magento2 Integration + +## Searchspring Management Console Actions + +### Update Field Settings + +On the [Field Settings page](https://manage.searchspring.net/management/field-settings/display-fields), make sure the following fields are updated: + +**For Search Only Integration:** + +| Field | Type | Multi-Valued | Display | +|---|---|:---:|:---:| +| visibility | Text | , | ✓ | + +**For Search and Category Integration:** + +| Field | Type | Multi-Valued | Display | +|---|---|:---:|:---:| +| visibility | Text | , | ✓ | +| category_hierarchy | Text | \| | ✓ | + +If settings are changed, perform an [Update Index](https://manage.searchspring.net/management/index/status). + +## Add IntelliSuggest Tracking + +Have the client install the following IntelliSuggest tracking module: [github.com/SearchSpring/magento2-searchspring-tracking](https://github.com/SearchSpring/magento2-searchspring-tracking). + +Notify the client which site id to use for the section "How to Install", step 4, point iii. + +## Create a Category Search Page + +> [!WARNING] +> Before creating this category page, ensure that the url does not already exist by going to `https://[domain]/shop.html`. If the url is active, alternative paths would be `search` or `ssearch`. + +> [!NOTE] +> If this is a re-mockup (and sometimes even a re-platform), it is recommended to keep the same search page url (and query parameter) so customers do not need to create redirects. If the site was previously integrated using CMS page rather than a category search page, you should be able to proceed with the below methods. This is because CMS and category page urls can be formatted the same way. + +- Magento 2 Admin > Left Navigation > Catalog > Categories > Add Subcategory. +- The category should be created wherever most categories are in the current view, which means usually you wouldn't use the "Add Root Category" button. Set the details to the following: + +| Section | Option | Value | +|---|---|---| +| Currently Active | Enable Category | Yes | +| Include in Menu | No | | +| Category Name | Search Results | | +| Search Engine Optimization | Url Key | shop | +| Search Engine Optimization | Meta Title | Search Results | +| Design | Layout | 2 columns with left bar or 2 columns with right bar (but could be full width) | + +- You do not need to add products to this category. +- You can preview the category by going to `https://[domain]/shop.html`. +- Go back to Magento 2 Admin > Left Navigation > Catalog > Categories. Click on the search category link to bring up the details. At the top of the page, it should say "Search Results (ID: 000000)". Note the id number as we will use it later. + +## Theme Integration + +Next we'll integrate Searchspring into the theme. + +> [!NOTE] +> If possible, have the site owner enable Template Path Hints. This will make it easier to find which files need to be changed. + +### Root.phtml Edits + +- FTP > app > design > frontend > [theme vendor] > [theme name] > Magento_Theme > templates > root.phtml. + +> [!CAUTION] +> If this file doesn't exist, find the root file of the same, make a copy, and upload it to the path above. Try looking in: vendor > magento > magento-theme > view > frontend > templates > root.phtml. Do not edit the root files directly because if the client updates Magento, this could erase the changes and break the integration. + +- Add the below code at the end of the file. + +#### Search Only + +> [!NOTE] +> If you are only integrating Search page functionality, you can use the following snippet. Otherwise skip this section and continue below to install both search and category functionality. + +- Replace `REPLACE_WITH_YOUR_SITE_ID` with the correct site id. +- Replace `000000` with the search category id that was noted when creating the category page. + +```php +get('Magento\Framework\Registry')->registry('current_category'); + + // Update defer if on search category + if (isset($ss_category) && $ss_category->getId() == 000000) { + $ss_defer_config = ''; + } +?> + + + +``` + +#### Search and Category + +- Replace `REPLACE_WITH_YOUR_SITE_ID` with the correct site id. +- Replace `000000` with the search category id that was noted when creating the category page. +- (Optional) If the category path attribute has a parent level you want to exclude, add to the `$ss_category_exclude` array. + +```php +get('Magento\Framework\Registry')->registry('current_category'); + + // Update details if on category + if (isset($ss_category)) { + $ss_category_id = $ss_category->getId(); + $ss_category_name = str_replace($ss_find, $ss_replace, $ss_category->getName()); + $ss_category_copy = $ss_category; + $ss_category_array = array(); + $ss_category_exclude = array('Default Category', 'Root Catalog'); + + // Build category path and trim characters + while (!in_array($ss_category_copy->getName(), $ss_category_exclude)) { + $ss_category_array[] = trim(htmlspecialchars($ss_category_copy->getName())); + $ss_category_copy = $ss_category_copy->getParentCategory(); + } + $ss_category_join = implode('>', array_reverse($ss_category_array)); + $ss_category_path = str_replace($ss_find, $ss_replace, $ss_category_join); + + // Update defer if on category + $ss_defer_config = ''; + + // Add category filter (but not on search category) + if ($ss_category_id != 000000) { + $ss_category_config = 'category = { id : "' . $ss_category_id . '", name : "' . $ss_category_name . '", path : "' . $ss_category_path . '" };'; + } + } +?> + + + +``` + +### Body Class Name + +Magento automatically adds a class based on the name of the page to the body, so there is no additional code needed to generate `category-shop` class. This class can be used for styling purposes or to target elements specifically on the search page. + +## Category Page Edits + +Next we'll add our target element(s) to the category page. This is where the Searchspring elements will be injected into, typically two elements are added for a two-column layout: one for content, and one for facets. + +Targets are defined in your Snap configuration and will only be injected into if they exist on the page. + +### Category Listing + +- FTP > app > design > frontend > [theme vendor] > [theme name] > Magento_Catalog > templates > product > list.phtml. + +> [!CAUTION] +> If this file doesn't exist, find the root file of the same, make a copy, and upload it to the path above. Try looking in: vendor > magento > magento-catalog > view > frontend > templates > list.phtml. Do not edit the root files directly because if the client updates Magento, this could erase the changes and break the integration. + +- Once the correct file is found, ensure that all of your search controller targets are added to the category listing. +- Some category templates will only display sidebar or results content if products are assigned to the category. Look for conditions that may be checking for product count and adjust as needed (likely altering the product count conditional). + +```php +
+``` + +### Category Sidebar + +- FTP > app > design > frontend > [theme vendor] > [theme name] > Magento_LayeredNavigation > templates > layer > view.phtml. + +> [!CAUTION] +> If this file doesn't exist, find the root file of the same, make a copy, and upload it to the path above. Try looking in: vendor > magento > magento-layerednavigation > view > frontend > templates > view.phtml. Do not edit the root files directly because if the client updates Magento, this could erase the changes and break the integration. + +- Once the correct file is found, ensure that all of your search controller targets are added to the category sidebar. + +```php +
+``` + +## Search Form Updates + +Next we'll update the search form to submit to the search results category page (ie. `/shop.html`) we created. + +- FTP > app > design > frontend > [theme vendor] > [theme name] > Magento_Search > templates > form.mini.phtml. + +> [!CAUTION] +> If this file doesn't exist, find the root file of the same, make a copy, and upload it to the path above. Try looking in: vendor > magento > magento-search > view > frontend > templates > form.mini.phtml. Do not edit the root files directly because if the client updates Magento, this could erase the changes and break the integration. + +- Above the form, add a php flag to enable and disable Searchspring: + +```php + +``` + +- Create a copy of the form which you will edit and comment out the old form. This will allow us to retain Magento's default functionality for when Searchspring is disabled. + +```php + + + + + +``` + +In your form copy, update the following details: + +| Element | Attribute | Value | +|---|---|---| +| form | method | get | +| form | action | /shop.html | +| input[type="text"] or input[type="search"] | data-mage-init | remove attribute | +| input[type="hidden"] | | remove any hidden inputs | +| search_autocomplete | | remove element | + +## Integration Code + +Up until this point, we've added the Searchspring integration to the theme and category page. + +Now we'll ensure our integration code captures the context variables and sets up the necessary configuration. + +### Create Magento2 Plugin + +In the `src/scripts` folder, create a plugin called `magento2.js` and add the below code. This snippet will provide tracking for shopper id; setting form key and uenc; and a few common pieces of Magento 2 logic used in results display. + +```js +// src/scripts/magento2.js +import { cookies } from '@searchspring/snap-toolbox'; + +export const magento2 = (controller) => { + // get shopper id from magento cache + const mageCacheStorage = JSON.parse(localStorage.getItem('mage-cache-storage')); + let shopperId = (mageCacheStorage?.customer?.data_id) ? mageCacheStorage.customer.data_id : false; + + // track shopperId + if (shopperId) { + controller.tracker.track.shopper.login({ + id: shopperId + }); + } + + // magento2 configs + controller.store.custom.m2 = { + domain: window.location.hostname, + formKey: cookies.get('form_key'), + uenc: typeof btoa == 'function' ? btoa(window.location.href) : '', + }; + + controller.on('afterStore', async ({ controller }, next) => { + const store = controller.store; + const { pagination, results } = store; + const customM2 = store.custom.m2; + customM2.uenc = typeof btoa == 'function' ? btoa(window.location.href) : ''; + + if (pagination.totalResults) { + results.forEach((result) => { + const core = result.mappings.core; + const custom = result.custom; + + // shared magento2 action config + const sharedData = { + data: { + product: core.uid, + uenc: customM2.uenc, + }, + }; + + // magento2 wishlist action + let wishlistData = sharedData; + wishlistData.action = '//' + customM2.domain + '/wishlist/index/add/'; + custom.wishlist = JSON.stringify(wishlistData).replace(/\//g, '\\/'); + + // magento2 compare action + let compareData = sharedData; + compareData.action = '//' + customM2.domain + '/catalog/product_compare/add/'; + custom.compare = JSON.stringify(compareData).replace(/\//g, '\\/'); + + // magento2 Add to Cart action + custom.addToCart = '//' + customM2.domain + '/checkout/cart/add/uenc/' + customM2.uenc + '/product/' + core.uid + '/'; + }); + } + + await next(); + }); +}; +``` + +### Search Only Integration + +If you are only integrating Search page functionality, follow these steps. Otherwise skip this section and continue below to install both search and category functionality. + +Add code to set a visibility filter on search pages: + +```js +// src/index.js + +/* visibility filters */ +let visibilityFilters = []; +visibilityFilters.push({ + field: 'visibility', + value: 'Search', + type: 'value', + background: true, +}); +``` + +In the search controller config object, add the magento2 plugin and filters: + +```js +// src/index.js +import { magento2 } from './scripts/magento2'; +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + plugins: [[magento2]], + settings: { + redirects: { + singleResult: true, + }, + }, + globals: { + filters: visibilityFilters, + }, + }, + targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content')).Content; + }, + }, + ], + }, + ], + autocomplete: [ + { + config: { + id: 'autocomplete', + plugins: [[magento2]], + globals: { + filters: visibilityFilters, + search: { + query: { + spellCorrection: true, + }, + }, + pagination: { + pageSize: 6, + }, + }, + }, + }, + ], + }, +}); +``` + +### Search and Category Integration + +If integrating on search and category pages, follow these steps. + +Back in `src/index.js`, adjust context to grab additional values for category integration: + +```js +// src/index.js +/* context from script tag */ +import { getContext } from '@searchspring/snap-toolbox'; +const context = getContext(['category']); +``` + +Below this, add code to support background filters on a `search` controller. As a best practice, we use `category_hierarchy` as it contains a unique, hierarchal path for the category. + +> [!CAUTION] +> The background filter will almost always be `category_hierarchy`, but there are times when we create a custom field such as `ss_category` or `ss_category_hierarchy`. The background filter should match the category facet if there is one set. If you do need to update, be sure to change the below code to set the background filter on that field instead. + +```js +// src/index.js + +/* background filters */ +let backgroundFilters = []; +let visibilityFilters = []; + +if (context.category?.path) { + // set background filter + backgroundFilters.push({ + field: 'category_hierarchy', + value: context.category.path.replace(/\"\;/g, '"'), + type: 'value', + background: true, + }); + + // add visibility filters for category + visibilityFilters.push({ + field: 'visibility', + value: 'Catalog', + type: 'value', + background: true, + }); +} else { + // add visibility filters for search + visibilityFilters.push({ + field: 'visibility', + value: 'Search', + type: 'value', + background: true, + }); +} +``` + +To attach the `backgroundFilters` and `visibilityFilters` to a `search` controller, they need to be added into a `globals` config: + +```js +// src/index.js +import { magento2 } from './scripts/magento2'; +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + plugins: [[magento2]], + settings: { + redirects: { + singleResult: true, + }, + }, + globals: { + filters: visibilityFilters.concat(backgroundFilters), + }, + }, + targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content')).Content; + }, + }, + ], + }, + ], + autocomplete: [ + { + config: { + id: 'autocomplete', + plugins: [[magento2]], + globals: { + filters: visibilityFilters, + search: { + query: { + spellCorrection: true, + }, + }, + pagination: { + pageSize: 6, + }, + }, + }, + }, + ], + }, +}); +``` + +> [!NOTE] +> In the above code, we concat `visibilityFilters` and `backgroundFilters` to the search controller. The autocomplete controller only needs a background filter on visibility. + +## Additional Targets (Optional) + +In addition to having two targets for a two-column layout, you may want to inject content into other sections of the page such above the content and sidebar to display information such as the search query. Note the addition of `category-shop` class before `searchspring-header` to ensure the content is only injected on the search page. + +```js +// src/index.js +targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content')).Content; + }, + hideTarget: true, + }, + { + selector: '#searchspring-sidebar', + component: async () => { + return (await import('./components/Sidebar')).Sidebar; + }, + hideTarget: true, + }, + { + selector: '.category-shop #searchspring-header', + component: async () => { + return (await import('./content/header/Header')).Header; + }, + hideTarget: true, + }, +], +``` diff --git a/docs/BUILD_DEPLOY_INTEGRATION_SHOPIFY.md b/docs/BUILD_DEPLOY_INTEGRATION_SHOPIFY.md new file mode 100644 index 000000000..66635cac0 --- /dev/null +++ b/docs/BUILD_DEPLOY_INTEGRATION_SHOPIFY.md @@ -0,0 +1,465 @@ +# Shopify Integration + + +## Searchspring Management Console Actions + +Log into the Searchspring Management Console (SMC) and perform the following actions: + +### Add IntelliSuggest Tracking + +Shopify sites now add IntelliSuggest tracking through "Web Pixel Tracking". In the SMC, check the [Data Feed page](https://manage.searchspring.net/management/data-feed) and ensure this feature is enabled. If it's not, speak with the Solutions team member who setup the account. + +### Update Field Settings + +On the [Field Settings page](https://manage.searchspring.net/management/field-settings/display-fields), make sure the following fields are updated: + +| Field | Type | Multi-Valued | Display | Display In Recs | +|---|---|:---:|:---:|:---:| +| collection_handle | Text | | ✓ | | +| handle | Text | No | ✓ | ✓ | +| tags | Text | , | ✓ | | +| vendor | Text | No | ✓ | | +| product_type | Text | No | ✓ | | + +If settings are changed, perform an [Update Index](https://manage.searchspring.net/management/index/status). + +### "All" Collection Page (Optional) + +Most if not all Shopify sites have an "All" collection page located at `[domain]/collections/all`. Typically, products are automatically assigned to this collection and then fed back into our data feed. There have been occasions where this is not true and we don't have the `collection_handle` > `all` assignment in our data feed. If this happens, please inquire with Searchspring support to have this added. + +## Create a Collection Search Page + +To add a search results page, we'll need to create a new collection in Shopify. + +> [!WARNING] +> Before creating this collection page, ensure that the url does not already exist by going to `https://[domain]/collections/shop`. If the url is active, an alternative path could be `search`. + +- Within the Shopify Admin, navigate to Products > Collections > Create collection. +- Set the collection details to the following: + +| Option | Value | +|---|---| +| Title | Shop | +| Collection type | Manual | + +- You do not need to add products to this collection. +- The initial title value determines the collection handle so starting with "Shop" ensures the url is `/collections/shop`. If you need to use another url path, your "Title" should be different to format the correct collection handle. +- After saving, edit this collection once more and change the "Title" to "Search Results". +- You can preview the collection by going to `https://[domain]/collections/shop`. + + +## Theme Integration + +Next we'll integrate Searchspring into the theme. + +- Create a copy of the current theme to integrate on. It is recommended to do so rather than integrating directly on the live theme initially to allow for testing prior to going live. +- Online Store > Themes > Live theme > ... > Duplicate +- To grab a link to the copied theme, click Online Store > Themes > [theme name] > ... > right click Preview > and Copy Link Address. + +### Theme Settings + +Next, we'll add the Searchspring theme settings. Theme settings are defined to provide an interface for defining variables that are used in liquid code. See [Shopify Setting Types](https://shopify.dev/themes/architecture/settings#setting-types) for more information. These theme settings are then accessed by going to Online Store > Themes > [theme name] > Customize button. Click on "Theme settings" in the lower left navigation and on the right side, there should be a "Searchspring" section. + +- Online Store > Themes > [theme name] > ... > Edit code > Config > `settings_schema.json`. +- At the end of the file, find a closing square bracket `]` on the last line. +- Before this closing bracket, add code for Searchspring theme settings. The opening comma is needed if there is another configuration before your code paste. + +```json +{ + "name": "Searchspring", + "settings": [ + { + "type": "checkbox", + "id": "ss_enable", + "label": "Enable Searchspring", + "default": true + }, + { + "type": "text", + "id": "ss_site_id", + "label": "Site ID", + "default": "REPLACE_WITH_YOUR_SITE_ID" + }, + { + "type": "text", + "id": "ss_collection_handle", + "label": "Search collection handle", + "default": "shop" + }, + { + "type": "text", + "id": "ss_branch_name", + "label": "Branch Name" + } + ] +} +``` + +- Replace `REPLACE_WITH_YOUR_SITE_ID` on line 15 with the correct siteId found in the Searchspring Management Console. +- (if applicable) Replace `shop` on line 21 with the search collection handle if it was not `shop`. + + +## Create a Liquid Template Snippet + +Liquid template snippets are used to store code that is used in multiple templates. + +Create a new snippet which will be used to store the Searchspring integration script code. + +- Online Store > Themes > [theme name] > ... > Edit code > Snippets > Add a new snippet > `ss-script`. + +### Search Only + +> [!NOTE] +> If you are only integrating Search page functionality, you can use the following snippet. Otherwise skip this section and continue below to install both search and collections functionality. + +```liquid +{%- if settings.ss_branch_name != blank -%} + {% capture ss_branch_name %}/{{ settings.ss_branch_name }}{% endcapture %} +{%- endif -%} + +{%- if customer -%} + {% capture ss_shopper_config %} + shopper = { id: "{{ customer.id }}" }; + {% endcapture %} +{%- endif -%} + +{% assign ss_defer_config = ' defer' %} +{%- if collection.handle and template contains 'collection' and collection.handle == settings.ss_collection_handle -%} + {% assign ss_defer_config = '' %} +{%- endif -%} + +{%- if template -%} + {% capture ss_template_config %} + template = "{{ template }}"; + {% endcapture -%} +{%- endif -%} + +{% capture ss_money_config %} + format = "{{ shop.money_format }}"; +{% endcapture %} + +{% comment %}Searchspring Script{% endcomment %} + +``` + +### Search and Collections + +```liquid +{%- if settings.ss_branch_name != blank -%} + {% capture ss_branch_name %}/{{ settings.ss_branch_name }}{% endcapture %} +{%- endif -%} + +{%- if customer -%} + {% capture ss_shopper_config %} + shopper = { id: "{{ customer.id }}" }; + {% endcapture %} +{%- endif -%} + +{% assign ss_defer_config = ' defer' %} +{%- if collection.handle and template contains 'collection' -%} + {% assign ss_defer_config = '' %} + {%- if collection.handle != settings.ss_collection_handle -%} + {% capture ss_collection_config %} + collection = { id: "{{ collection.id }}", name: "{{ collection.title | replace: '"', '"' }}", handle: "{{ collection.handle }}" }; + {% endcapture %} + {%- endif -%} +{%- endif -%} + +{%- if current_tags -%} + {% capture ss_tags_config %} + tags = {{ current_tags | json }}; + {% endcapture %} +{%- endif -%} + +{%- if template -%} + {% capture ss_template_config %} + template = "{{ template }}"; + {% endcapture -%} +{%- endif -%} + +{% capture ss_money_config %} + format = "{{ shop.money_format }}"; +{% endcapture %} + +{% comment %}Searchspring Script{% endcomment %} + +``` + +### Snippet Installation + +Next, we'll integrate the `ss-script` snippet into the theme. We'll have to add the snippet to the theme.liquid file such that it is included on every page. +It is recommended to install the snippet in the `head` tag so that the script is loaded as soon as possible. + +- Online Store > Themes > [theme name] > ... > Edit code > Layout > `theme.liquid`. +- Before the closing `` tag, add the following code: + +```liquid +{% if settings.ss_enable %} + {% render 'ss-script' %} +{% endif %} +``` + +### Body class name + +(Optional) There may be occasions where you need to add a class name to the `body` element on the search page for styling purposes. + +If `body` tag has no `class` attribute: + +```liquid + +``` + +If `body` tag has a `class` attribute, ensure to keep the existing class names and append the `ss-shop` class name to the existing list of class names: + +```liquid + +``` + +## Collection Page Edits + +Next we'll add our target element(s) to the collection page. This is where the Searchspring elements will be injected into, typically two elements are added for a two-column layout: one for content, and one for facets. + +Targets are defined in your Snap configuration and will only be injected into if they exist on the page. + +- Online Store > Themes > [theme name] > ... > Edit code > Templates > `collection.liquid`. +- `collection.liquid` is a standard Shopify template, but this may not be the file to edit depending on your theme. Look for includes which will tell you where to go, for example: `{% section 'collection-main' %}`. This says that there's additional code for the collection page located in the section file that has the name "collection-main". +- Once the correct file is found, ensure that all of your search controller targets are added to the collection template. By using the `ss_enable` condition, we can retain Shopify's default functionality for when Searchspring is disabled via the theme settings. + +```liquid +{% if settings.ss_enable %} +
+{% else %} + +{% endif %} + +{% if settings.ss_enable %} +
+{% else %} + +{% endif %} +``` + +## Autocomplete Form Submission + +Next we'll update the search form to submit to the search results collection page (ie. `/shop`) we created. + +- Online Store > Themes > [theme name] > ... > Edit code > Layout > `theme.liquid`. +- Check for the search form. It may not be in this file, but check for its approximate location and then look in includes such as snippets or sections for its actual location. Other possible file names might be `form-search.liquid`, `header.liquid`, etc. +- Create a copy of the form which you will edit and comment out the old form. This will allow us to retain Shopify's default functionality for when Searchspring is disabled via the theme settings. + +```liquid +{% if settings.ss_enable %} +
+ +
+{% else %} + +{% endif %} +``` + +In your form copy, update the following details: + +| Element | Attribute | Value | +|---|---|---| +| form | method | get | +| form | action | `{{ routes.collections_url }}/{{ settings.ss_collection_handle }}` | +| input[type="hidden"] | | remove any hidden inputs | + + +## Integration Code + +Up until this point, we've added the Searchspring integration to the theme and collection page. + +Now we'll ensure our integration code captures the context variables. + +```js +// src/index.js +/* context from script tag */ +const context = getContext(['collection', 'tags', 'template', 'shopper', 'siteId']); +``` + +Below this, add code to support background filters on a `search` controller. As a best practice, we use `collection_handle` as this has unique values in comparison to collection name. Certain integrations also use tags to further filter products on collections (for example: `/collections/shirts/red` where `red` is a color tag), which is why we set an additional filter on `tags`. Additionally, this code snippet updates page details so you can use conditionals matching the current page in code. + +> [!NOTE] +> Shopify has default collections to store vendors and types with urls of `/collections/vendors` and `/collections/types` respectively. To show a vendor or type, a query is applied to the url like `/collections/vendors?q=Awesome Brand`. To show results on these pages, in the code below we will take the page title from script context and apply it as a background filter with the `vendor` or `product_type` field. + +```js +// src/index.js + +/* set up page details config */ +let isSearch = Boolean(window.location.href.includes('/shop')); +let page = { + id: isSearch ? 'shop' : 'other', + title: isSearch ? 'Search Results' : 'Other Page', + type: isSearch ? 'search' : 'other', +}; + +/* background filters */ +let backgroundFilters = []; +if (context.collection?.handle) { + // replace characters on collection name + const collectionName = context.collection.name.replace(/\&\#39\;/, "'"); + + // update page details when on collection + page = { + id: context.collection.handle, + title: collectionName, + type: 'collection', + }; + + // set background filter + if (context.collection.handle == 'vendors') { + backgroundFilters.push({ + field: 'vendor', + value: collectionName, + type: 'value', + background: true, + }); + } else if (context.collection.handle == 'types') { + backgroundFilters.push({ + field: 'product_type', + value: collectionName, + type: 'value', + background: true, + }); + } else { + backgroundFilters.push({ + field: 'collection_handle', + value: context.collection.handle, + type: 'value', + background: true, + }); + } + + // handle collection tags (filters) + if (context.tags && Array.isArray(context.tags)) { + context.tags.forEach((tag) => { + backgroundFilters.push({ + field: 'tags', + value: tag, + type: 'value', + background: true, + }); + }); + } +} +``` + +To attach the `backgroundFilters` to a `search` controller, they need to be added into a `globals` config. + +```js +// src/index.js +import { sharedPlugin } from './plugins/sharedPlugin'; +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + plugins: [[sharedPlugin, page]], + globals: { + filters: backgroundFilters, + }, + }, + targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content/Content')).Content; + }, + }, + ], + }, + ], + }, +}); +``` + +## Update Product URLs + +For most Shopify sites, the product url should be updated to include the collection handle in the url path. Within a plugin that is attached to all controllers, add the below code. Adding this code to a "global" plugin makes the function reusable. For example: this function may be needed when clicking on a product swatch or size option to change the url with the collection handle. + +See example above to attach the plugin to the `search` controller. + +```js +// src/plugins/sharedPlugin.js +export const sharedPlugin = (controller, page) => { + // set initial custom settings for project + controller.store.custom = { ...controller.store.custom, page: page }; + // update Shopify product url + controller.store.custom.updateUrl = (handle) => { + const { type, store } = controller; + const page = store.custom.page; + + if (type == 'search') { + const hasRoute = typeof Shopify == 'object' && typeof Shopify.routes == 'object' && typeof Shopify.routes.root == 'string' ? true : false; + const routeShopify = hasRoute ? Shopify.routes.root : '/'; + const routeCollection = page.type == 'collection' ? `collections/${page.id}/` : ``; + return `${routeShopify}${routeCollection}products/${handle}`; + } + }; + + controller.on('afterStore', async ({ controller }, next) => { + const page = controller.store.custom.page; + const { results } = controller.store; + + if (page.type == 'collection' && results && results.length !== 0) { + results.forEach((result) => { + if (result.type != 'banner') { + result.mappings.core.url = controller.store.custom.updateUrl(result.attributes.handle); + } + }); + } + + await next(); + }); +} +``` + +> [!NOTE] +> Both code blocks above use the `page` config, so make sure that is defined. + +> [!WARNING] +> Only use this function on product results, meaning do not use it if you have content tabs with blog articles. It should also not be run on Autocomplete, as it could result in invalid urls. For example: if you are on a collection page for "shoes" and search "shirts", this would format the url as "/collections/shoes" which could result in invalid urls for the results. + + +## Additional Targets (Optional) + +In addition to having two targets for a two-column layout, you may want to inject content into other sections of the page such above the content and sidebar to display information such as the search query. Note the addition of `ss-shop` class before `searchspring-header` to ensure the content is only injected on the search page. + +```js +// src/index.js +targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./content/content/Content')).Content; + }, + hideTarget: true, + }, + { + selector: '#searchspring-sidebar', + component: async () => { + return (await import('./sidebar/sidebar/Sidebar')).Sidebar; + }, + hideTarget: true, + }, + { + selector: '.ss-shop #searchspring-header', + component: async () => { + return (await import('./content/header/Header')).Header; + }, + hideTarget: true, + }, +], +``` + diff --git a/docs/BUILD_DEPLOY_PERFORMANCE_OPTIMIZATION.md b/docs/BUILD_DEPLOY_PERFORMANCE_OPTIMIZATION.md new file mode 100644 index 000000000..ea4a140db --- /dev/null +++ b/docs/BUILD_DEPLOY_PERFORMANCE_OPTIMIZATION.md @@ -0,0 +1,166 @@ +# Performance Optimization + +## Script Loading Optimization + +### Script Placement + +Search and Category pages are critical to the initial display of content. Therefore it is recommended to install the Snap script tag in the `` so that content is fetched and displayed as soon as possible. + + +### Async/Defer Script Attributes + +On Search and Category pages, it is not recommended to use `async` or `defer` attributes on the script tag as these will prevent the Snap script from executing immediately and will further delay the initial display of content. + +For other pages that only contain Recommendations, Autocomplete, or Finders (non-critical content), the `defer` attribute can be added conditionally to improve page load performance. + +### Resource Hints + +Use link tags with `preconnect` and `dns-prefetch` attributes to establish early connections to Snap CDN and Searchspring API domains: + +```html + + + + + + + + + +``` + + +## Optimizing Largest Contentful Paint (LCP) + + +### Image Optimization + +For images that are part of the LCP element (above the fold), consider disabling lazy loading. + +The [Image](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-image--default) component has lazy loading enabled by default. + +Otherwise, if you are not using the `Image` component, ensure product images are optimized by setting the `loading` attribute to `lazy`: + +```jsx +{result.name} +``` + +### Prefetch Strategies + +Use `prefetch` sparingly and only for components that are likely to be viewed. When `prefetch` is enabled, the controller's search method is called immediately when the targeter is registered, before the target element is found in the DOM: + +```jsx +const config = { + controllers: { + search: [{ + config: { + id: 'search', + }, + targeters: [{ + selector: '#search-results', + prefetch: true // Triggers search immediately, before target is found + }] + }] + } +}; +``` + +Avoid prefetching when: +- Component is below the fold +- Component might not be needed (conditional rendering) +- Page already has heavy resource usage + +## Optimizing Interaction to Next Paint (INP) + +### Optimize Event Middleware + +Snap's event middleware system allows you to hook into controller lifecycle events. When adding middleware, ensure you're not adding synchronous work that blocks the main thread: + +```jsx +// Good: Use async middleware for non-blocking operations +controller.on('beforeSearch', async ({ controller, request }, next) => { + // Perform async work that doesn't block interaction + await someAsyncOperation(); + await next(); +}); + +// Avoid: Adding heavy synchronous operations in middleware +// This will delay INP response +controller.on('beforeSearch', ({ controller, request }, next) => { + // Heavy synchronous computation - blocks INP + heavyComputation(); + next(); +}); +``` + + +## Optimizing Cumulative Layout Shift (CLS) + +### Reserve Space for Components + +By adding a `min-height` to elements that are being targeted by Snap components, you can always reserve space for Snap components to prevent layout shifts. This is especially important for elements that are above the fold. By default, the `min-height` style will automatically be removed when the component is rendered. + +```html +
+ +
+``` + +### Use Skeleton Components + +We recommended using server-side rendered skeletons inside the target elements for the best LCP performance. If this is the case, also set the `renderAfterSearch` property to `true`. + +```html +
+
+
+
+
+
+``` + +```jsx +import { Snap } from '@searchspring/snap-preact'; + +const config = { + controllers: { + search: [{ + config: { + id: 'search', + }, + targeters: [{ + selector: '#searchspring-content', + component: () => import('./Search'), + renderAfterSearch: true, // Render the skeleton after search is ready + }] + }] + } +}; +``` + +### Set Image Dimensions + +Ensure product images have defined dimensions to prevent layout shifts: + +```css +.ss__image img { + width: 100%; + height: auto; + aspect-ratio: 1 / 1; /* Maintain aspect ratio */ +} +``` + +Or use the `style` prop on Image components: + +```jsx +{result.name} +``` + diff --git a/docs/GITHUB.md b/docs/GITHUB.md deleted file mode 100644 index 1e65edd72..000000000 --- a/docs/GITHUB.md +++ /dev/null @@ -1,83 +0,0 @@ -## Github Setup - -If you would like your final bundle and assets to reside on Searchspring's CDN (ie. `https://snapui.searchspring.io/siteid/bundle.js`), the repository must be in Searchspring's Github organization and an invitation can be requested for collaboration. - -Although it is not required to use Searchspring's organization nor Github for your version control, Snap utilizes Github Actions as part of our continuous integration and deployment. - -### Repository Configuration - -#### Default Branch - -The expected default branch name should be set to `production` instead of `main` or `master` - -#### Action Secrets - -For each Searchspring's Management Console account associated with the Snap repository, the following Action secret should be added as a repository secret. This page can be found at `https://github.com/[owner]/[repository]/settings/secrets/actions` - -- Secret Key Name: `WEBSITE_SECRET_KEY_[SITEID]` - -Where `[SITEID]` should be replaced with the siteId found in the Searchspring Management Console. For example: `WEBSITE_SECRET_KEY_ABC123` - -- Value: `secretKey` located adjacent to the siteId in the Searchspring Management Console - - -### Github Action - -The [snap-action](https://github.com/searchspring/snap-action/) can be used by creating a new github workflow file (ie. `.github/workflows/deploy.yml`) - -The Snap Action requires additional parameters not shown in the example below. See [snap-action](https://github.com/searchspring/snap-action/) for additional documentation. - -```yml -on: [push, pull_request] - -jobs: - Publish: - runs-on: ubuntu-latest - name: Snap Action - steps: - - name: Checkout action - uses: actions/checkout@v2 - with: - repository: searchspring/snap-action - - name: Run @searchspring/snap-action - uses: ./ -``` - -### Project configuration - -#### package.json - -The package.json file must contain all siteIds associated with this project. - -Single siteId example: - -```json -{ - ... - "searchspring": { - "siteId": { - "abc123": { - "name": "site1.com" - } - }, - } -} -``` - -Multi siteId example: - -```json -{ - ... - "searchspring": { - "siteId": { - "abc123": { - "name": "site1.com" - }, - "def456": { - "name": "site1.com.au" - } - }, - } -} -``` \ No newline at end of file diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md deleted file mode 100644 index 7f0a10b6d..000000000 --- a/docs/INTEGRATION.md +++ /dev/null @@ -1,33 +0,0 @@ -## Integration - -When development has concluded the bundle is ready to be placed on a development or production site. - -```html - -``` - -The bundle should be included in the `` tag, ideally near the top of the node, and should not have a 'defer' or 'async' attribute. This location is important in order to start fetching results and as soon as possible. This placement prior to any body elements also serves to allow for the hiding of targeted elements that contain content - this preventing a flash when the contents change upon injection. - -Context variables should be placed inside the script tag, see the documentation for [context variables](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_CONTEXT.md) for more details. - -```html - - - - - - - Snap Integration Example - - - - -
- - - -``` \ No newline at end of file diff --git a/docs/INTEGRATION_DEBUGGING.md b/docs/INTEGRATION_DEBUGGING.md deleted file mode 100644 index 0d2311079..000000000 --- a/docs/INTEGRATION_DEBUGGING.md +++ /dev/null @@ -1,23 +0,0 @@ -## Debugging - -## Branch Overrides - -This functionality is only currently possible with Searchspring managed Snap repositories (https://github.com/searchspring-implementations). - -While browsing a page that contains a Snap integration, appending the `?searchspring-preview=[branchname]` query parameter to the URL will stop the execution of the existing script, and load the build from the `[branchname]` branch `https://snapui.searchspring.io/[siteid]/[branchname]/bundle.js` - -You will see an interface overlay on the bottom right of the viewport indicating if successful and details of the build. - - - -This will also be persisted across page navigation. To stop previewing a branch build, you must click the `Stop Preview` button in the interface or clear the `ssBranch` cookie. The interface can also be minimized. - - -## Development Mode - -While browsing a page that contains a Snap integration, appending the `?dev` query parameter to the URL will set the controller's `environment` property to `development`. - -This is commonly used to enable visibility of development logs in the console. - -See [AbstractController](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Abstract) and [@searchspring/snap-logger](https://github.com/searchspring/snap/tree/main/packages/snap-logger) - diff --git a/docs/INTEGRATION_VARIANTS.md b/docs/INTEGRATION_VARIANTS.md index 285bb99b4..4be5ac144 100644 --- a/docs/INTEGRATION_VARIANTS.md +++ b/docs/INTEGRATION_VARIANTS.md @@ -9,48 +9,7 @@ Product variants allow you to represent different versions of the same base prod Snap's variants functionality helps manage these product variations by providing tools to configure, display and interact with variant data in your components. -Configure variants by setting the variant field in either: -- Controller config: `controllers[controller].config.settings.variants.field` -- Recommendation config: `instantiators.recommendation.config.settings.variants.field` - -Example - - -```typescript -const config = { - instantiators: { - recommendation: { - components: { - Bundle: async () => (await import('./components/Recommendations/Bundle/Bundle')).Bundle, - }, - config: { - settings: { - variants: { - field: 'ss_variants', - }, - }, - }, - }, - }, - controllers: { - search: [ - { - config: { - id: 'search', - settings: { - variants: { - field: 'ss_variants', - }, - }, - }, - }, - ], - }, -}; - -const snap = new Snap(config); -``` - -Once configured, each result that has variants in `controller.store.results` should include a `variants` object with: +The Athos API will automatically return variants data when applicable. When this happens, each result that has variants in `controller.store.results` will include a `variants` object with: **Properties:** - `active` - Currently selected variant data @@ -175,7 +134,6 @@ const config = { config: { settings: { variants: { - field: 'ss_variants', realtime: { enabled: true, }, @@ -234,7 +192,6 @@ You can filter which results update in realtime by adding filters to your config Example - ```typescript variants: { - field: 'ss_variants', realtime: { enabled: true, filters: ['first'] diff --git a/docs/PREACT.md b/docs/PREACT.md deleted file mode 100644 index 46aa55f31..000000000 --- a/docs/PREACT.md +++ /dev/null @@ -1,13 +0,0 @@ -## Snap Preact - -The [@searchspring/snap-preact](#/package-preact) package is an abstraction layer for Preact that provides a config based interface for creating a Searchspring integration quickly. Underneath the hood it utilizes all of the core Snap packages. If you wish to create a Snap integration using core packages individually (hard mode), see the Advanced section. - -If you are not using Snapfu to start with a template, you will need to start by adding Snap to your project. - -```bash -npm install --save @searchspring/snap-preact -``` - -```typescript -import { Snap } from '@searchspring/snap-preact'; -``` \ No newline at end of file diff --git a/docs/PREACT_CONFIG.md b/docs/PREACT_CONFIG.md deleted file mode 100644 index 35bfcf04f..000000000 --- a/docs/PREACT_CONFIG.md +++ /dev/null @@ -1,149 +0,0 @@ -## Configuration - -Let's define our config. The config that is provided to Snap will create and return controllers that are specified in the config. In this example, we will be creating a Search and Autocomplete controller. - -```typescript -const config = { - features: { - integratedSpellCorrection: { - enabled: true, - }, - }, - url: { - parameters: { - core: { - query: { name: 'query' } - } - } - }, - client: { - globals: { - siteId: 'xxxxxx', - }, - }, - controllers: { - search: [ - { - config: { - id: 'search', - }, - targeters: [ - { - selector: '#searchspring-content', - component: () => Content, - skeleton: () => ContentSkel, - hideTarget: true, - }, - { - selector: '#searchspring-sidebar', - component: () => Sidebar, - skeleton: () => SidebarSkel, - hideTarget: true, - }, - ], - }, - ], - autocomplete: [ - { - config: { - id: 'autocomplete', - selector: 'input.searchspring-ac', - settings: { - trending: { - limit: 5, - }, - }, - }, - targeters: [ - { - selector: 'input.searchspring-ac', - component: () => Autocomplete, - hideTarget: true, - }, - ], - }, - ], - }, -}; -``` - -Let's go over a few things. - -`config.url` is optional and contains a [`UrlTranslator` config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) object that is passed to the core [@searchspring/snap-url-manager](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager) package used by all controllers. This parameter configuration will be applied to all controllers created via Snap, but can be specified per controller for specific customization. - -`config.client` is required and contains a config object that is passed to the core [@searchspring/snap-client](https://github.com/searchspring/snap/tree/main/packages/snap-client) package. This service handles the network requests to our APIs to retrieve data to be displayed. - -`config.client.globals` specifies base query parameters to the API; these are parameters that will ALWAYS be included on every request. At the bare minimum, `siteId` is required and can be obtained in the [Searchspring Management Console](https://manage.searchspring.net/) - -`config.controllers` specifies all of the controllers that we wish to create. In this this example we are creating a Search and Autocomplete controller. In addition, Finder and Recommendation services can also be specified. - -### Search - -Lets look at the Search controller that we are creating. - -The `config` object contains all controller configurations. The most notable property here is the required `id` with a given value of `'search'`. This will be the name of the search controller that we can then interface with the return of the `new Snap()` instance via the `getController` method. In snap-preact controllers are created only as needed (typically when a targeter has found a target), their creation is an asynchronous process. The `getController` method will return a promise that will resolve to the controller object requested immediately after its creation. - -For example: - -```typescript -const snap = new Snap(config); -snap.getController('search').then((search) => { - // do things with controller -}); -``` - -If multiple controllers are needed at the same time, usage of the `getControllers` method is necessary. The `getControllers` method returns a promise that resolves to an array of controllers in the order requested by the parameters. The promise only resolves when ALL of the controllers have been created - if a controller is specified that is never created the promise will never resolve. For this reason this method should only be used when all controllers are needed simultaneously. - -```typescript -const snap = new Snap(config); -snap.getControllers('search', 'autocomplete').then(([search, autocomplete]) => { - // do things with controllers -}); -``` - -We also have a `targeters` array of DomTargeter `targeter` configuration objects. Each object defines an entry point on the page where a component will be rendered. - -`targeter.selector` specifies the DOM selector to target - -`targeter.component` specifies a function that returns a reference to the component to render at the target selector. - -`targeter.hideTarget` boolean that specifies if the target node should be hidden before the component is mounted and rendered. It is recommended to enable this to prevent flashy behaviour. - -`targeter.autoRetarget` (optional) boolean that specificies if the targeter should continuously query for the selector in the DOM until it finds it and triggers a retarget. This is useful for dynamically generated selectors that might not exist at dom ready. - -`targeter.skeleton` (optional) meant to be used as a "loading" component. This specifies a function that returns a reference to the component to render immediately at the target selector to show briefly while the data is returning and the real component is rendering. You can use any component you want for this, although `snap-preact-components` provides a `skeleton` component for you to use if preferred. - -`targeter.props` (optional) convenient way of passing additional props to the component, by default we pass `controller` - -`targeter.onTarget` (optional) callback that fires after a target is found - -`targeter.name` (optional) name to give the targeter for later reference using `controller.targeters` - -In our example, we are rendering a `` component into `
` and the `` component into `
` - - - -### Autocomplete - -We're also creating an Autocomplete controller in a similar function. - -One notable thing to mention as you may see a duplicate `selector` property in both the `config` and `targeter`. - -The `config.selector` specifies the `` element(s) to attach events to that respond to Autocomplete actions. This supports a selector that targets many elements. - -The `targeter.selector` specifies the DOM node where the `targeter.component` will be rendered into. - -However in our example, since they are both the same value, the Autocomplete component will rendered as a child DOM node below the `` element that is currently focused. - - -### Feature Flags - -`config.features` is optional and defines features to enable. - -#### Integrated Spell Correction - -Integrated spell correction is disabled by default. When disabled and a query is typed into autocomplete, a request is made to the suggest API to retrieve a list of terms. The highest scoring term is then used to query the search API for results. - -Enabling integrated spell correction `config.features.integratedSpellCorrection.enabled = true` will still retrieve terms from the suggest API to display, however the query that was entered will be used as the term sent to the search API. Spell correction will occur within the search API. The correction and original query is returned in the response and available to be render. Upon submitting the autocomplete form, a `fallbackQuery` parameter is also submitted. This contains a value of the highest scoring suggested term and will be searched for if the initial query yields 0 results. - -Note: Enabling integrated spell correction modifies [AutocompleteController](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Autocomplete)'s config by setting `config.settings.integratedSpellCorrection = true` diff --git a/docs/PREACT_CONTROLLER_PROPS.md b/docs/PREACT_CONTROLLER_PROPS.md deleted file mode 100644 index 1a1f75eab..000000000 --- a/docs/PREACT_CONTROLLER_PROPS.md +++ /dev/null @@ -1,93 +0,0 @@ -## Controller Props - -For each targeted element, the corresponding controller that created it will be passed along to the component via the `controller` prop. - -Let's go over our `Content` component. This is considered a root level component since it is being rendered onto the page using a targeter. - -We'll want to create a `ControllerProvider` such that any subcomponents can have a reference to the controller via its props (as long as it is using the cooresponding `withController` consumer). The [@searchspring/snap-preact-components](https://github.com/searchspring/snap/tree/main/packages/snap-preact-components) package contains a `ControllerProvider` that we can utilize. - - -```jsx -import { h, Fragment, Component } from 'preact'; -import { observer } from 'mobx-react-lite'; -import { ControllerProvider } from '@searchspring/snap-preact-components'; -import { Results, NoResults } from './Results'; - -@observer -export class Content extends Component { - render() { - const controller = this.props.controller; - - return ( - controller.store.loaded && ( - - { - controller.store.pagination.totalResults > 0 ? () : () - } - - ) - ); - } -} -``` - -Then from any subcomponent such as the `Result` component in this example, we'll need to add the `@withController` decorator to access our controller via props. The `@withController` decorator should be placed before any other decorators. - -```jsx -import { h, Fragment, Component } from 'preact'; -import { observer } from 'mobx-react-lite'; -import { withController, InlineBanner, Result } from '@searchspring/snap-preact-components'; - -@withController -@observer -export class Results extends Component { - render() { - const controller = this.props.controller; - const { results } = controller.store; - - return ( -
    - {results.map((result) => ( -
  • - {{ - banner: , - }[result.type] || } -
  • - ))} -
- ); - } -} -``` - - -## Reactivity - -Each controller has a `store` property. This is a MobX store created from the core [@searchspring/snap-store-mobx](https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx) package and we will be rendering its data in our components. - -The MobX store contains many observable properties that are reactive to changes such as user interaction (ie. collapsing a facet) or fetching data (ie. pagination). In order to react to these store changes the `@observer` decorator must be added to our components. - -If you are creating functional components you would use this as a function wrapping your component. - -```jsx -import { h, Fragment } from 'preact'; -import { observer } from 'mobx-react-lite'; -import { withController, InlineBanner, Result } from '@searchspring/snap-preact-components'; - -export const Results = observer(withController((props) => { - const controller = props.controller; - const { results } = controller.store; - - return ( -
    - {results.map((result) => ( -
  • - {{ - banner: , - }[result.type] || } -
  • - ))} -
- ) -})); -``` diff --git a/docs/PREACT_DISPLAYING_DATA.md b/docs/PREACT_DISPLAYING_DATA.md deleted file mode 100644 index a0b06d83d..000000000 --- a/docs/PREACT_DISPLAYING_DATA.md +++ /dev/null @@ -1,479 +0,0 @@ -## Displaying Data - -At this point you are ready to start building components that render data from the controller's store. Here are a few common store properties and suggested usage in components. If you have used Snapfu to start with a template, these component examples will already be included. - -## All Stores - -All of the following properties are available on all stores (Search, Autocomplete, Finder, & Recommendations) - -### controller.store.loaded - -The `loaded` property will be true when the store has been loaded with data and is available to be consumed. This property is recommended to conditionally render a component. - -### controller.store.loading - -The `loading` property will be true is a network request is in progress. This property is recommended to conditionally render a loading status (ie. spinning icon or loading bar) - -### controller.store.custom - -See [`custom` property](https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Abstract) - -## Search Store - -The following properties are specific to a Search Store via a Search Controller. - - -### SearchController.store.merchandising - -The `merchandising` property contains merchandising redirects and banner content. It is recommended to utlizing the `` component from `@searchspring/snap-preact-components` to display the various merchandising banners. - -The available banner types include: `header`, `banner`, `footer`, `left`, `inline` - -For inline banners, the `` component should be used instead. An example of this usage can be found in the 'store.results' section below. - -```jsx -import { Banner } from '@searchspring/snap-preact-components'; - -@observer -export class Content extends Component { - render() { - const controller = this.props.controller; - const { store } = controller; - const { pagination, merchandising } = store; - - return ( - store.loaded && ( - -
- - - - { - pagination.totalResults > 0 - ? () : - () - } - - -
-
- ) - ); - } -} -``` - -### SearchController.store.search - -The `search` property contains information about the current query, typically displayed above results and used in combination with the `store.pagination` data. - -```jsx -@withController -@observer -export class SearchHeader extends Component { - render() { - const { controller } = this.props; - const { store } = controller; - const { pagination, search } = store; - const originalQuery = search.originalQuery; - - return ( - store.loaded && ( -
- {pagination.totalResults ? ( -

- {`Showing `} - {pagination.multiplePages && {` ${pagination.begin} - ${pagination.end} of `}} - {pagination.totalResults} - {` result${pagination.totalResults == 1 ? '' : 's'}`} - {search?.query && ( - - {` for `} - "{search.query.string}" - - )} -

- ) : ( - pagination.totalResults === 0 && ( -

- {search?.query ? ( - - No results for "{search.query.string}" found. - - ) : ( - No results found. - )} -

- ) - )} - - {originalQuery && ( -
- Search instead for "{originalQuery.string}" -
- )} -
- ) - ); - } -} -``` - -### SearchController.store.pagination - -The `pagination` property is not only used for information about the current query, but also contains everything needed for handling pagination of a query that yields multiple pages. Invoking the `getPages` method will retrieve the specified number of page objects. For more about the pagination store, checkout the [Search Controller docs](#/package-controller-search). - -```jsx -@withController -@observer -export class Pagination extends Component { - render() { - const controller = this.props.controller; - const { - store: { pagination }, - } = controller; - const pages = pagination.getPages(5); - - return ( -
- {pagination.previous && ( - - - Prev - - - )} - - {pages.map((page) => ( - - {page.number} - - ))} - - {pagination.next && ( - - - Next - - - )} -
- ); - } -} -``` - -### SearchController.store.sorting - -The `sorting` property contains sorting options applicable to the current query. Typically used to render a ` { - const selectedOption = sorting.options.filter((option) => option.value == e.target.value).pop(); - selectedOption && selectedOption.url.go(); - }} - > - {sorting.options.map((option) => ( - - ))} - -
- ) - ); - } -} -``` - -### SearchController.store.results - -The `results` property contains an array of result objects for the current page. - -Each result object contains the following notable properties: - -`result.type` will be 'product' or 'banner' (inline banner) - -`result.mappings.core` core attributes configured in the [Searchspring Management Console](https://manage.searchspring.net/) - -`result.attributes` remaining attributes - -`result.mask` provides a way to temporarily modify result data without changing the underlying store data. This can be used in combination with the `result.display` for simple UI effects like showing alternate product images on hover, or more complex interactions like updating displayed prices when selecting different product variants. - -`result.mask.merge` a function to merge new mask data with the current display state. This function accepts a single object as its only parameter. - -`result.mask.set` a function to set the mask data. Overwrites the current mask data. This function accepts a single object as its only parameter. - -`result.mask.clear` a function to clear the mask data, reverting to the original display state. - -`result.display` an object used for display in result components. Containing the currently set display state from the `result.mask` combined with the underlying core data for the result. - -`result.variants` contains information about product variants like size and color options, as well as the variant selections data. (requires variants to be enabled and configured) For more variant integration information, see [Variant Integration Docs](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_VARIANTS.md) - -`result.custom` an empty object that is not modified by core Snap packages. This is available for you to modify and store custom data to be rendered. See [`custom` property](https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Abstract) - -Note: if you will be creating a custom Result component, be sure to include intellisuggest product click tracking. Available via `controller.track.product.click()` as seen in the example below. This should be invoked `onClick` or `onMouseDown` on each Result. - -```jsx -@withController -@observer -export class Results extends Component { - render() { - const controller = this.props.controller; - const { results } = controller.store; - - return ( -
    - {results.map((result) => ( -
  • - {{ - banner: , - }[result.type] || } -
  • - ))} -
- ); - } -} - -@withController -@observer -class Result extends Component { - render() { - const { result } = this.props; - const { - attributes, - mappings: { core }, - } = result.display; - const intellisuggest = (e) => controller.track.product.click(e, result); - - return ( - result && ( - - ) - ); - } -} -``` - -### SearchController.store.facets - -The `facets` property contains an array of facet objects for the current query. - -Each result object contains the following notable properties: - -`facet.collapsed` facet collapse state. Facets can be configured to start collapsed by default in the [Searchspring Management Console](https://manage.searchspring.net/) - -`facet.toggleCollapse` a method that toggles the collapse state for this facet - -`facet.clear` a method to remove the facet if it is currently active - -`facet.label` the facet label to display (ie. Price, Size, Brand) - -`facet.field` the raw facet field name - -`facet.display` the facet display type - used to conditionally render different facet components. Available display types: `list` (default), `grid`, `palette`, `hierarchy`, `slider`. Facet display types can be configured in the [Searchspring Management Console](https://manage.searchspring.net/) - -The example below displays a custom `FacetOptionsList` component for facets with a display type of `list`. - -The `@searchspring/snap-preact-components` component library includes the following components that can be imported or used as a reference: `FacetListOptions`, `FacetGridOptions`, `FacetPaletteOptions`, `FacetHierarchyOptions`, `FacetSlider` - -`facet.type` the facet type - Available facet types: `range`, `value`, `range-buckets`. - -Facets that contain a `type` value of `range` will not contain any `values` as this is typically used as a Slider. Instead, the facet will include `range.low`, `range.high`, `active.low`, and `active.high` properties. - -Facets with a `type` value of `value` or `range-buckets` will contain the following properties: - -`facet.search.input` facet search within - setting this will dynamically filter the facet `values` array to only include values that match the `facet.search.input` substring - -`facet.overflow.setLimit` method to set the number of values to display before overflow occurs - -`facet.overflow.toggle` method to toggle overflow of a facet, typically invoked `onClick` event of a facet 'show more' button - -`facet.refinedValues` facet values that have been limitied if any overflow or search within is active; this should be used to render facet values from components - -`facet.values` original facet values - it is not recommended to directly render facet values using this in your components - `facet.refinedValues` should be used instead - however, if you are using an `afterStore` event to reference facet values, `facet.values` should be used - -```jsx -@withController -@observer -export class Facets extends Component { - render() { - const { facets } = this.props.controller.store; - - return ( - facets.length !== 0 && ( -
- {facets.map((facet) => ( - - ))} -
- ) - ); - } -} - -@withController -@observer -export class Facet extends Component { - render() { - const { facet } = this.props; - - return ( - facet && ( -
-
{ - facet.toggleCollapse(); - }} - > - {facet.label} -
- -
-
- {{ - grid:
grid component
, - palette:
palette component
, - hierarchy:
hierarchy component
, - slider: , - }[facet.display] || } -
-
-
- ) - ); - } -} - -@observer -class FacetOptionsList extends Component { - render() { - const facet = this.props.facet; - const values = facet.refinedValues; - - return ( - - ); - } -} -``` - - -### SearchController.store.filters - -The `filters` property contains an array of filters that are currently applied to the query. - -Typically used to display a filter summary with options to remove filters. - -```jsx -@withController -@observer -export class FilterSummary extends Component { - render() { - const controller = this.props.controller; - const { - store: { filters }, - } = controller; - - return ( - filters.length !== 0 && ( - - ) - ); - } -} -``` - - -## Autocomplete Store - -It is recommended to utlizing the `` component from `@searchspring/snap-preact-components` to display Autocomplete. - -The following properties are specific to an Autocomplete Store via an Autocomplete Controller. - -### AutocompleteController.store.merchandising - -See `SearchController.store.merchandising` section above. - -### AutocompleteController.store.search - -The `search` property contains information about the current query. However unlike SearchController.store.search, AutocompleteController.store.search does not contain a `didYouMean` query. - - -### AutocompleteController.store.facets - -See `SearchController.store.facets` section above. - -In addition, each facet value will contain a `preview` method that should be invoked on the `onFocus` event of a facet value. This method will lock the current facets such that when the store is updated with the filtered results, the original facets do not get replaced with the new facets from the filtered query. - -### AutocompleteController.store.filters - -See `SearchController.store.filters` section above. - -### AutocompleteController.store.results - -See `SearchController.store.results` section above. - -### AutocompleteController.store.terms - -The `terms` property contains an array of autocomplete terms that are relevant to the query. Each term contains a `preview` method that should be invoked on the `onFocus` event of a term value. This method will lock the current terms and unlock the previous facets (if changing terms with a facet filter applied) such that when the store is updated with the results for the new term, the original terms do not change. - -### AutocompleteController.store.trending - -The `trending` property contains an array of trending `terms`. Trending terms are not relevant to the current query and are generated from collected reporting data. It is recommended to display trending terms as a starting point when the `` is focused and does not yet contain a value. Trending terms must be enabled via settings in the AutocompleteController config. - - -### AutocompleteController.store.pagination - -See `SearchController.store.pagination` section above. - -### AutocompleteController.store.sorting - -See `SearchController.store.sorting` section above. - -### AutocompleteController.store.history - -The `history` property contains an array of previously searched `terms`. Historical terms are not relevant to the current query and are stored in localstorage. Historical terms can be displayed in the Autocomplete component in place of or in addition to trending and suggested terms. Historical terms must be enabled via settings in the AutocompleteController config. - diff --git a/docs/PREACT_EVENTS.md b/docs/PREACT_EVENTS.md deleted file mode 100644 index 2c4ac432e..000000000 --- a/docs/PREACT_EVENTS.md +++ /dev/null @@ -1,397 +0,0 @@ -## Snap Events - -Let's look at how to tie into various Snap events by utilizing middleware and plugins. To learn more details about middleware, and execution order of it, checkout the core [@searchspring/snap-event-manager](https://github.com/searchspring/snap/tree/main/packages/snap-event-manager) package. - -The Snap instance that we create will return a `controllers` object with all of the requested controllers specified in the config. - -There are two ways we can attach events to our controllers. Using the config, or directly on the controller. - - -## Config Middleware - -On the config we can specify middleware via `on` or `plugins` attributes. - -### middleware - -The `middleware` property is an object that has event name(s) as the key and and array of the middleware functions as the values. - -The value can be a single function or an array of functions if attaching multiple middleware to a single event. - -```typescript -const initMiddleware = async(eventData, next) => { - console.log("runs on init", eventData); - await next(); -} - -const afterSearchMiddlewareOne = async(eventData, next) => { - console.log("runs on afterSearch", eventData); - await next(); -} - -const afterSearchMiddlewareTwo = async(eventData, next) => { - console.log("runs on afterSearch, after afterSearchMiddlewareOne", eventData); - await next(); -} - -const config = { - client: { - globals: { - siteId: 'xxxxxx', - }, - }, - controllers: { - search: [ - { - config: { - id: 'search', - middleware: { - init: initMiddleware, - afterSearch: [ - afterSearchMiddlewareOne, - afterSearchMiddlewareTwo - ] - } - }, - }, - ], - }, -}; -``` - - -### plugins - -The `plugins` property is an array of arrays of functions and optional function parameters that are used to attach functionality to controllers. Parameters can optionally be passed to the functions as shown with the `paramPlugin` below: - -```typescript -const plugin = (controller) => { - controller.on('init', async(eventData, next) => { - console.log("runs on init", eventData); - await next(); - }); -} - -const paramPlugin = (controller, ...params) => { - controller.on('afterStore', async(eventData, next) => { - console.log("runs on afterStore", eventData, params); - await next(); - }); -} - -const config = { - client: { - globals: { - siteId: 'xxxxxx', - }, - }, - controllers: { - search: [ - { - config: { - id: 'search', - plugins: [ - [ plugin ], - [ paramPlugin, 'param1', 'param2' ] - ] - }, - }, - ], - }, -}; -``` - -## Global Events - -We can attach global events to the `window.searchspring` object via the event-manager created by the Snap instance. These events can be listened for or fired via the `on` and `fire` methods. - -Example: -```typescript - window.searchspring.on('myEvent', (data) => { - console.log('myEvent happened!', data); - }) - - window.searchspring.fire('myEvent', data); -``` - -## Provided Global Events - -### controller/selectVariantOptions -The `controller/selectVariantOptions` event takes a payload with 2 values, `options` and `controllerIds` (optional). - -This event will loop through all available controllers on the window, find matches to the passed controllerIds (will use all controllers if no ids are provided), and then call `variants.makeSelections` with the options provided for each `product` type result in that controller store. This allows you to programmatically select variant options for results completely outside your result components. - -The `controllerIds` can be an exact controller id string match or an array of matches - - -```typescript -controllerIds: `search`, -controllerIds: [`search`, `autocomplete`] -``` - -it can also take regex for partial matches - - -```typescript -controllerIds: [/^recommend_/] -``` -Example: -```typescript -const data = { - options: { "size" : "M" }, - controllerIds: [/^recommend_/] -} - -window.searchspring.fire('controller/selectVariantOptions', data); -``` - -### controller/recommendation/update -The `controller/recommendation/update` event takes a payload with an optional `controllerIds` similar to the `controller/selectVariantOptions` example above. If not provided then all recommendation controllers will be affected. - -This event will loop through recommendation controllers and invoke the `controller.search()` method if the `controller.config.realtime` option is `true`. This also requires [cart attribute tracking](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_TRACKING.md) - -## Controller Events - -On the controller we can attach middleware via `on` or `plugin` methods. - -We can attach events to our controllers after they have been created by a Snap instance via the `getController` method. This method returns a promise that resolves to the requested controller object. - -Let's use the `config` from above. Since our search controller has an `id` of `'search'`, we can reference it as follows: - -```typescript -const snap = new Snap(config); -snap.getController('search').then((search) => { - console.log("Search Controller: ", search); -}); -``` - -We can now attach middleware events in the following methods: - -### controller.on - -```typescript -snap.getController('search').then((search) => { - search.on('afterSearch', async(eventData, next) => { - console.log("runs on afterSearch", eventData); - await next(); - }); -}); -``` - -### controller.plugin - -```typescript -snap.getController('search').then((search) => { - search.plugin((controller) => { - controller.on('afterStore', async(eventData, next) => { - console.log("runs on afterStore", eventData); - await next(); - }); - }); -}); - -``` - -Next we will attach a plugin that takes additional parameters. This could be useful for sending contextual data into your plugin. - -```typescript -const paramPlugin = (controller, ...params) => { - // params = [ 'param1', 'param2' ] - controller.on('afterStore', async(eventData, next) => { - console.log("runs on afterStore", eventData); - await next(); - }); -} - -snap.getController('search').then((search) => { - search.plugin(paramPlugin, 'param1', 'param2'); -}); -``` - -## Available Events - -## Search Events - -### init -- Called with `eventData` = { controller } -- Called automatically with first `search()` of the controller or invoked by a call to the `init` controller method - -### error -- Called with `eventData` = { controller, error } -- Invoked when an error has been caught within the controller - -### beforeSearch -- Called with `eventData` = { controller, request } -- Always invoked before an API request is made -- Sets `controller.store.loading = true` - -### afterSearch -- Called with `eventData` = { controller, request, response } -- Always invoked after an API request is made -- Invokes `window.location.replace()` if API response contains merchandising redirects AND if `config.settings.redirects.merchandising = true` (default) -- Invokes `window.location.replace()` to redirect to product detail page if API response returned a single product AND `config.settings.redirects.singleResult = true` (default) -- Sets `controller.store.loading = false` - -### afterStore -- Called with `eventData` = { controller, request, response } -- Always invoked after data has been stored in Mobx store - -### restorePosition -- Called with `eventData` = { controller, element } -- If an element position data exists, `element` data will include `domRect` (of the element with selector), `href` and `selector` -- Invoked during final stages of `afterStore` just prior to setting loading state to false - -### track.product.render -- Called with `eventData` = { controller, product, trackEvent } -- Always invoked after `track.product.render()` method has been invoked - -### track.product.impression -- Called with `eventData` = { controller, product, trackEvent } -- Always invoked after `track.product.impression()` method has been invoked - -### track.product.clickThrough -- Called with `eventData` = { controller, event, product, trackEvent } -- Always invoked after `track.product.clickThrough()` or `track.product.click()` method has been invoked - -### track.product.addToCart -- Called with `eventData` = { controller, product, trackEvent } -- Always invoked after `track.product.addToCart()` method has been invoked - -### track.redirect -- Called with `eventData` = { controller, redirectURL, trackEvent } -- Always invoked upon searching for a query that yields a merchandising redirect - -### addToCart -- Called with `eventData` = { controller, products } -- Always invoked after `addToCart()` method has been invoked - - -## Autocomplete Events - -### init -- Called with `eventData` = { controller } -- Called automatically with first `search()` of the controller or invoked by a call to the `init` controller method - -### error -- Called with `eventData` = { controller, error } -- Invoked when an error has been caught within the controller - -### beforeSearch -- Called with `eventData` = { controller, request } -- Always invoked before an API request is made -- Sets `controller.store.loading = true` - -### afterSearch -- Called with `eventData` = { controller, request, response } -- Always invoked after an API request is made -- Sets `controller.store.loading = false` -- Cancels search if input doesn't match current urlManager input state - -### afterStore -- Called with `eventData` = { controller, request, response } -- Always invoked after data has been stored in Mobx store - -### focusChange -- Called with `eventData` = { controller } -- Invoked when a new input element has been focused - -### beforeSubmit -- Called with `eventData` = { controller, input } -- Invoked prior to submission of autocomplete search - -### track.product.render -- Called with `eventData` = { controller, product, trackEvent } -- Always invoked after `track.product.render()` method has been invoked - -### track.product.impression -- Called with `eventData` = { controller, product, trackEvent } -- Always invoked after `track.product.impression()` method has been invoked - -### track.product.clickThrough -- Called with `eventData` = { controller, event, product, trackEvent } -- Always invoked after `track.product.clickThrough()` or `track.product.click()` method has been invoked - -### track.product.addToCart -- Called with `eventData` = { controller, product, trackEvent } -- Always invoked after `track.product.addToCart()` method has been invoked - -### track.redirect -- Called with `eventData` = { controller, redirectURL, trackEvent } -- Always invoked upon submitting the search form input - -### addToCart -- Called with `eventData` = { controller, products } -- Always invoked after `addToCart()` method has been invoked - - -## Finder Events - -### init -- Called with `eventData` = { controller } -- Called automatically with first `search()` of the controller or invoked by a call to the `init` controller method - -### error -- Called with `eventData` = { controller, error } -- Invoked when an error has been caught within the controller - -### beforeSearch -- Called with `eventData` = { controller, request } -- Always invoked before an API request is made -- Sets `controller.store.loading = true` - -### afterSearch -- Called with `eventData` = { controller, request, response } -- Always invoked after an API request is made -- Sets `controller.store.loading = false` - -### afterStore -- Called with `eventData` = { controller, request, response } -- Always invoked after data has been stored in Mobx store -- no operation - -### beforeFind -- Called with `eventData` = { controller } -- Invoked after invoking the `find` method, before `window.location.href` is changed - -## Recommendation Events - -### init -- Called with `eventData` = { controller } -- Called automatically with first `search()` of the controller or invoked by a call to the `init` controller method - -### error -- Called with `eventData` = { controller, error } -- Invoked when an error has been caught within the controller - -### beforeSearch -- Called with `eventData` = { controller, request } -- Always invoked before an API request is made -- Sets `controller.store.loading = true` - -### afterSearch -- Called with `eventData` = { controller, request, response } -- Always invoked after an API request is made -- Sets `controller.store.loading = false` -- Cancels search if input doesn't match current urlManager input state - -### afterStore -- Called with `eventData` = { controller, request, response } -- Always invoked after data has been stored in Mobx store - -### track.product.clickThrough -- Called with `eventData` = { controller, event, result, trackEvent } -- Always invoked after `track.product.clickThrough()` or `track.product.click()` method has been invoked -- Allows for adding custom product click events (ie. Google Analytics) - -### track.product.impression -- Called with `eventData` = { controller, result, trackEvent } -- Always invoked after `track.product.impression()` method has been invoked - -### track.product.render -- Called with `eventData` = { controller, result, trackEvent } -- Always invoked after `track.product.render()` method has been invoked - -### track.product.addToCart -- Called with `eventData` = { controller, product, trackEvent } -- Always invoked after `track.product.addToCart()` method has been invoked - -### addToCart -- Called with `eventData` = { controller, products } -- Always invoked after `controller.addToCart()` method has been invoked \ No newline at end of file diff --git a/docs/PREACT_RECOMMENDATIONS.md b/docs/PREACT_RECOMMENDATIONS.md deleted file mode 100644 index 620cbcc7f..000000000 --- a/docs/PREACT_RECOMMENDATIONS.md +++ /dev/null @@ -1,194 +0,0 @@ -## Recommendations - -### Instantiator config -The instantiators config provides a reference to our template component so that it can be imported at run time. - -If you plan to utilize template branching, `instantiators.recommendation.config.branch` must define the current git branch name. Otherwise, a fallback value with the repository default branch name (typically `production`) should be defined. - -```typescript -const config: SnapConfig = { - ... - instantiators: { - recommendation: { - components: { - DefaultRecommendations: async () => { - return (await import('./components/Recommendations/DefaultRecommendations/DefaultRecommendations')).DefaultRecommendations; - }, - }, - - config: { - branch: BRANCHNAME || 'production', - }, - }, - }, -} -``` - -The `BRANCHNAME` can be defined at runtime via webpack's `DefinePlugin` - -```javascript -const webpack = require('webpack'); -const childProcess = require('child_process'); -const branchName = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); -module.exports = { - ... - plugins: [ - new webpack.DefinePlugin({ - BRANCHNAME: `"${branchName}"`, - }), - ], -} -``` - -## Default Template -The Searchspring Management Console contains a `Default` template availble for standard profiles (non-bundle) that does not require the use of the Snapfu CLI to create a custom template. To use the `Default` template, the following instantiator config should be added to your `snap-preact` config. - -```js -instantiators: { - recommendation: { - components: { - Default: async () => { - return (await import('./components/Recommendations/Recs')).Recs; - }, - }, - config: { - branch: BRANCHNAME || 'production', - }, - }, -}, -``` - -Note that the component is not required to be named `Default`, however `instantiators.recommendation.component` must contain the `Default` key as seen in the example above. - -Component file: - -```jsx -import { h } from 'preact'; -import { observer } from 'mobx-react-lite'; - -import { Recommendation } from '@searchspring/snap-preact-components'; - -export const Recs = observer((props) => { - const controller = props.controller; - const store = controller?.store; - - if (!controller.store.loaded && !controller.store.loading) { - controller.search(); - } - - const parameters = store?.profile?.display?.templateParameters; - - return store.results.length > 0 && ; -}); -``` - -## Default Bundle Template -The Searchspring Management Console also contains a `Bundle` template availble for bundle profiles, this template does not require the use of the Snapfu CLI to create a custom template. To use the `Bundle` template, another component mapping will need to be added to your `snap-preact` instantiator config. - -```js -instantiators: { - recommendation: { - components: { - Default: async () => { - return (await import('./components/Recommendations/Recs')).Recs; - }, - Bundle: async () => { - return (await import('./components/Recommendations/Bundled')).Bundled; - }, - }, - config: { - branch: BRANCHNAME || 'production', - }, - }, -}, -``` - -Note that the component is not required to be named `Bundle`, however `instantiators.recommendation.component` must contain the `Bundle` key as seen in the example above. - -The example Bundled component below uses the `RecommendationBundle` component imported from the snap component library. See [Components Preact > RecommendationBundle](https://searchspring.github.io/snap/#/components-preact?params=%3Fpath%3D%2Fstory%2Forganisms-recommendationbundle--default) for more details. - -```jsx -import { h } from 'preact'; -import { observer } from 'mobx-react-lite'; - -import { RecommendationBundle } from '@searchspring/snap-preact-components'; - -export const Bundled = observer((props) => { - const controller = props.controller; - const store = controller?.store; - - if (!controller.store.loaded && !controller.store.loading) { - controller.search(); - } - - const parameters = store?.profile?.display?.templateParameters; - - return store.results.length > 0 && console.log("need to add these to the platform cart", items)} title={parameters?.title} />; -}); -``` - -## Custom Template -Let's look at how to setup a custom recommendation template using the Snapfu CLI. See [Getting Started > Setup](https://searchspring.github.io/snap/#/start-setup) for installing Snapfu. - -There are three steps required for adding recommendations: -- Creating the local template files -- Syncing the template to the Searchspring Management Console -- Updating our Snap config (see instantiator config above) - -### Creating a new recommendation template -To generate a new template, run the following at the root of the project. This command will prompt you to provide various inputs such as the template type, template name, an optional description, and the path to the component. - -```bash -snapfu recs init -``` - -In this example, we'll create a new template with a name of "DefaultRecommendations" - -This will generate three files which should be commited to your repository: -- The `.jsx` file (/src/components/Recommendations/Default.jsx) is the template itself containing our component. -- The `.scss` file (/src/components/Recommendations/DefaultRecommendations.scss) is imported by the `.jsx` template file and optional CSS styling can be defined here. -- The `.json` file (/src/components/Recommendations/DefaultRecommendations.json) contains various meta data for this template and is used when running `snapfu recs sync` to sync this template to Searchspring's Management Console API. See syncing documentation below. - -#### `.json` file parameters -The `.json` file allows template parameters to be specified. Template parameters are returned in the Recommendations API and accessible in your recommendation components via `controller.store.profile.display.templateParameters`. Template parameters can be set in the Searchspring Management Console after the template has been synced. - -Example: -```json -{ - "type": "snap/recommendation/default", - "name": "defaultrecommendations", - "label": "DefaultRecommendations", - "description": "DefaultRecommendations custom template", - "component": "DefaultRecommendations", - "orientation": "horizontal", - "parameters": [ - { - "name": "title", - "label": "Title", - "description": "text used for the heading", - "defaultValue": "Recommended Products" - } - ] -} -``` - -At this point, you can customize the `.jsx` template to your requirements. By default it will utilize our [Recommendation](https://searchspring.github.io/snap/#/components-preact?params=%3Fpath%3D%2Fstory%2Forganisms-recommendation--default) component. - -### Syncing Templates - -Syncing custom templates to Searchspring's Management Console is required before they can be used. This is due to the recommendations API that returns the template's component name defined for the profile we set in the `.json` file above. - -Templates also support branching. For production-ready templates, please ensure you are on the repository's default branch (typically `production`) before running `snapfu recs sync` - -If you are using Snap's Github action, merges into the `production` branch will execute the sync command within the action to ensure your templates are always synced. See [Getting Started > Github Setup](https://searchspring.github.io/snap/#/start-github) for Github Actions usage. - -To sync templates, run the follow at the root of the project. - -```bash -snapfu recs sync -``` - -#### Syncing to multiple accounts -To sync the template(s) to multiple accounts, multiple siteIds must be defined in the project's package.json file - -See [Getting Started > Github Setup](https://searchspring.github.io/snap/#/start-github) \ No newline at end of file diff --git a/docs/REFERENCE_CONFIGURATION.md b/docs/REFERENCE_CONFIGURATION.md new file mode 100644 index 000000000..26af4ec68 --- /dev/null +++ b/docs/REFERENCE_CONFIGURATION.md @@ -0,0 +1,132 @@ +# Configuration + +This configuration refers to the object passed to the `new Snap()` constructor. + +## Client + +Required. Config object that is passed to the core `@searchspring/snap-client` package. This service handles the network requests to Searchspring APIs to retrieve data to be displayed. + + +| Option | Type | Description | +|---|---|---| +| client.globals | `Partial` | Base query parameters to the API; these are parameters that will ALWAYS be included on every request. At the bare minimum, `siteId` is required and can be obtained in the [Searchspring Management Console](https://manage.searchspring.net/). See [Snap client](https://github.com/searchspring/snap/tree/main/packages/snap-client) for more information. | +| client.config | `ClientConfig` | Optional. See [Snap client](https://github.com/searchspring/snap/tree/main/packages/snap-client) for more information. | + + +## Mode + +| Option | Type | Description | +|---|---|---| +| mode | `string` \| `AppMode` enum
(e.g. `'development'`, `'production'`, `AppMode.development`, `AppMode.production`) | Optional. Sets the application mode. `'development'` enables additional logging and disables network caching; Can also be set at runtime via url parameter `?dev=1` or `?dev=0`. `'production'` is for live usage and is the default mode. | + +## Context + +Optional Context object to be used to set the global context. If no context is provided, a default context taken from the integration script will be used, otherwise the provided config.context is merged with the script context. This context becomes the globalContext that is passed to all controllers that are created. + + +| Option | Type | Description | +|---|---|---| +| context | `ContextVariables` | Optional. Provides contextual variables for Snap, such as collection or user info. | + +## URL + +Optional. [`UrlTranslator` config](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url/README.md) object that is passed to the core `@searchspring/snap-url-manager` package used by all controllers. This parameter configuration will be applied to all controllers created via Snap, but can be specified per controller for specific customization. + +| Option | Type | Description | +|---|---|---| +| url | `UrlTranslatorConfig` | Optional. Configures URL translation and parameter mapping. | + + +## Tracker + +| Option | Type | Description | +|---|---|---| +| tracker | `{ globals?: TrackerGlobals; config?: TrackerConfig }` | Optional. Tracker configuration for analytics and event tracking. | + +## Instantiators + +| Option | Type | Description | +|---|---|---| +| instantiators | `{ recommendation?: RecommendationInstantiatorConfig }` | Optional. Custom instantiators for advanced controller/component instantiation, such as recommendations. See [`RecommendationInstantiator`](https://searchspring.github.io/snap/reference-snap-preact-instantiators) for more information. | + +## Controllers + +Specifies all of the controllers that we wish to create. + +| Option | Type | Description | +|---|---|---| +| controllers | `{ search?: Controller[]; autocomplete?: Controller[]; finder?: Controller[]; recommendation?: Controller[] }` | Optional. Defines the controllers to instantiate (search, autocomplete, finder, recommendation), each with their own config. | + +> [!NOTE] +> - Each controller array (e.g. `search`, `autocomplete`, etc.) contains objects with at least a `config` property, and may include `targeters`, `services`, `url`, and `context`. +> - For detailed controller configuration, see the Snap documentation or type definitions for each controller type. + +## Feature Flags + +`config.features` is optional and defines features to enable. + +| Option | Type | Description | +|---|---|---| +| features.integratedSpellCorrection.enabled | `boolean` | Optional. Enables or disables integrated spell correction. | + + +### Integrated Spell Correction + +Integrated spell correction is disabled by default. When disabled and a query is typed into autocomplete, a request is made to the suggest API to retrieve a list of terms. The highest scoring term is then used to query the search API for results. + +Enabling integrated spell correction `config.features.integratedSpellCorrection.enabled = true` will still retrieve terms from the suggest API to display, however the query that was entered will be used as the term sent to the search API. Spell correction will occur within the search API. The correction and original query is returned in the response and available to be render. Upon submitting the autocomplete form, a `fallbackQuery` parameter is also submitted. This contains a value of the highest scoring suggested term and will be searched for if the initial query yields 0 results. + +Note: Enabling integrated spell correction modifies [AutocompleteController](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Autocomplete)'s config by setting `config.settings.integratedSpellCorrection = true` + + +## Snap Methods + +### getController + +The `config` object contains all controller configurations. The most notable property here is the required `id` with a given value of `'search'`. This will be the name of the search controller that we can then interface with the return of the `new Snap()` instance via the `getController` method. + +In snap-preact controllers are created only as needed (typically when a targeter has found a target), their creation is an asynchronous process. The `getController` method will return a promise that will resolve to the controller object requested immediately after its creation. + +For example: + +```js +const snap = new Snap(config); +snap.getController('search').then((search) => { + // do things with controller +}); +``` + + +### getControllers + +If multiple controllers are needed at the same time, usage of the `getControllers` method is necessary. The `getControllers` method returns a promise that resolves to an array of controllers in the order requested by the parameters. The promise only resolves when ALL of the controllers have been created - if a controller is specified that is never created the promise will never resolve. For this reason this method should only be used when all controllers are needed simultaneously. + +```js +const snap = new Snap(config); +snap.getControllers('search', 'autocomplete').then(([search, autocomplete]) => { + // do things with controllers +}); +``` + + +### getInstantiator + + +Snap also provides a method to retrieve instantiators. Instantiatiors are used to create instances of the [`RecommendationInstantiator`](https://searchspring.github.io/snap/reference-snap-preact-instantiators) class, which is responsible for instantiating recommendations. + +```js +const snap = new Snap(config); +snap.getInstantiator('recommendation').then((instantiator) => { + // do things with instantiator +}); +``` + + +## Snap Properties + +| Property | Description | +|---|---| +| `client` | A reference to the [snap-client](https://github.com/searchspring/snap/tree/main/packages/snap-client) default instance used when constructing all controllers | +| `tracker` | A reference to the [snap-tracker](https://github.com/searchspring/snap/tree/main/packages/snap-tracker) default instance used when constructing all controllers. It can also be used to access storage helper methods and event tracking methods. | +| `context` | A reference to the context object used when constructing all controllers. | +| `controllers` | An object containing all controllers that have been constructed. | diff --git a/docs/REFERENCE_CONFIGURATION_MIDDLEWARE.md b/docs/REFERENCE_CONFIGURATION_MIDDLEWARE.md new file mode 100644 index 000000000..9f0b43373 --- /dev/null +++ b/docs/REFERENCE_CONFIGURATION_MIDDLEWARE.md @@ -0,0 +1,202 @@ +# Middleware + +Middleware allows you to tie into the search lifecycle of a controller or various global events. + +Controllers share many of the same events, however some may contain additional events. For a full list of available events and their payloads see Search, Autocomplete, Finder, or Recommendations sections. + +There are two ways we can attach events to our controllers. Using the config, or directly on the controller. + +## Config Middleware + +On the config we can specify middleware via `middleware` or `plugins` attributes. + +### middleware + +The `middleware` property is an object that has event name(s) as the key and and array of the middleware functions as the values. + +The value can be a single function or an array of functions if attaching multiple middleware to a single event. + +```js +const initMiddleware = async(eventData, next) => { + console.log("runs on init", eventData); + await next(); +} + +const afterSearchMiddlewareOne = async(eventData, next) => { + console.log("runs on afterSearch", eventData); + await next(); +} + +const afterSearchMiddlewareTwo = async(eventData, next) => { + console.log("runs on afterSearch, after afterSearchMiddlewareOne", eventData); + await next(); +} + +const config = { + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + middleware: { + init: initMiddleware, + afterSearch: [ + afterSearchMiddlewareOne, + afterSearchMiddlewareTwo + ] + } + }, + }, + ], + }, +}; +``` + + +### plugins + +The `plugins` property is an array of arrays of functions and optional function parameters that are used to attach functionality to controllers. Parameters can optionally be passed to the functions as shown with the `paramPlugin` below: + +```js +import { Snap } from '@searchspring/snap-preact'; + +const plugin = (controller) => { + controller.on('init', async(eventData, next) => { + console.log("runs on init", eventData); + await next(); + }); +} + +const paramPlugin = (controller, ...params) => { + controller.on('afterStore', async(eventData, next) => { + console.log("runs on afterStore", eventData, params); + await next(); + }); +} + +const config = { + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + plugins: [ + [ plugin ], + [ paramPlugin, 'param1', 'param2' ] + ] + }, + }, + ], + }, +}; +``` + +## Controller Middleware + +On the controller we can attach middleware via `on` or `plugin` methods. + +We can attach events to our controllers after they have been created by a Snap instance via the `getController` method. This method returns a promise that resolves to the requested controller object. + +Let's use the `config` from above. Since our search controller has an `id` of `'search'`, we can reference it as follows: + +```js +import { Snap } from '@searchspring/snap-preact'; + +const snap = new Snap(config); +snap.getController('search').then((search) => { + console.log("Search Controller: ", search); +}); +``` + +We can now attach middleware events in the following methods: + +### controller.on + +```js +import { Snap } from '@searchspring/snap-preact'; + +snap.getController('search').then((search) => { + search.on('afterSearch', async(eventData, next) => { + console.log("runs on afterSearch", eventData); + await next(); + }); +}); +``` + +### controller.plugin + +```js +import { Snap } from '@searchspring/snap-preact'; + +snap.getController('search').then((search) => { + search.plugin((controller) => { + controller.on('afterStore', async(eventData, next) => { + console.log("runs on afterStore", eventData); + await next(); + }); + }); +}); + +``` + +Next we will attach a plugin that takes additional parameters. This could be useful for sending contextual data into your plugin. + +```js +import { Snap } from '@searchspring/snap-preact'; + +const paramPlugin = (controller, ...params) => { + // params = [ 'param1', 'param2' ] + controller.on('afterStore', async(eventData, next) => { + console.log("runs on afterStore", eventData); + await next(); + }); +} + +snap.getController('search').then((search) => { + search.plugin(paramPlugin, 'param1', 'param2'); +}); +``` + + +## Global Events + +We can attach global events to the `window.searchspring` object via the event-manager created by the Snap instance. These events can be listened for or fired via the `on` and `fire` methods. + +Example: +```js + window.searchspring.on('myEvent', (data) => { + console.log('myEvent happened!', data); + }) + + window.searchspring.fire('myEvent', data); +``` + +## Provided Global Events + +### controller/selectVariantOptions +The `controller/selectVariantOptions` event takes a payload with 2 values, `options` and `controllerIds` (optional). + +This event will loop through all available controllers on the window, find matches to the passed controllerIds (will use all controllers if no ids are provided), and then call `variants.makeSelections` with the options provided for each `product` type result in that controller store. This allows you to programmatically select variant options for results completely outside your result components. + +The `controllerIds` can be an exact controller id string match or an array of matches - + +```js +controllerIds: `search`, +controllerIds: [`search`, `autocomplete`] +``` + +it can also take regex for partial matches - + +```js +controllerIds: [/^recommend_/] +``` diff --git a/docs/REFERENCE_CONFIGURATION_TARGETERS.md b/docs/REFERENCE_CONFIGURATION_TARGETERS.md new file mode 100644 index 000000000..28d8b49a0 --- /dev/null +++ b/docs/REFERENCE_CONFIGURATION_TARGETERS.md @@ -0,0 +1,116 @@ +# Targeters + +When defining controllers in the Snap configuration, we can specify a `targeters` array of configuration objects. +Each object in the array defines an entry point on the page where a component will be rendered and it's corresponding controller will be available via props. + +## Targeter Configuration + +| Property | Type | Description | +|----------|------|-------------| +| `selector` | `string` | The DOM selector to target | +| `component` | `function` | A function that returns a reference to the component to render at the target selector. Making this an async function is recommended to allow for code splitting. | +| `hideTarget` | `boolean` | Whether to hide the target node before the component is mounted and rendered. It is recommended to enable this to prevent flashy behaviour. | +| `autoRetarget` | `boolean` | Whether to continuously query for the selector in the DOM until it finds it and triggers a retarget. This is useful for dynamically generated selectors that might not exist at dom ready. | +| `skeleton` | `function` | A function that returns a reference to the component to render immediately at the target selector to show briefly while the data is returning and the real component is rendering. You can use any component you want for this, although `snap-preact-components` provides a `skeleton` component for you to use if preferred. | +| `props` | `object` | Convenient way of passing additional props to the component, by default we pass `controller` | +| `onTarget` | `function` | Callback that fires after a target is found. This is useful for triggering other actions such as initializing the controller. | +| `name` | `string` | Name to give the targeter for later reference using `controller.targeters` | + + +## Example + +In this example we're creating a single search controller with two targeters. The `` component is injected into `
` and the `` component is injected into `
`. Each component will have access to the same controller via it's props. + +```js +// src/index.js + +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + }, + targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content/Content')).Content; + }, + }, + { + selector: '#searchspring-sidebar', + component: async () => { + return (await import('./components/Sidebar/Sidebar')).Sidebar; + }, + }, + ], + }, + ], + }, +}); +``` + +To access the targeters after they have been created, we can first retrieve the controller and then use the `controller.targeters` property. This is a map of the targeter names (or selector if name is not provided) to the targeter objects. + +```js +snap.getController('search').then((controller) => { + const contentTargeter = controller.targeters['#searchspring-content']; + const sidebarTargeter = controller.targeters['#searchspring-sidebar']; +}); +``` + +## Skeleton Component +A `skeleton` component can be defined to render inside the target until search is ready to render the `component`. + +A [Skeleton component](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-skeleton--default) is available in the components package. + +```jsx +import { Snap } from '@searchspring/snap-preact'; +import { Skeleton } from '@searchspring/snap-preact-components'; + +const ContentSkeleton = () => { + return (); +}; + +const config = { + controllers: { + search: [{ + config: { + id: 'search', + }, + targeters: [{ + selector: '#searchspring-content', + component: () => import('./Search'), + skeleton: () => ContentSkeleton, + hideTarget: false, // Keep target visible to render skeleton + }] + }] + } +}; +``` + +## Targeter Methods + +| Method | Description | +|--------|-------------| +| `getTargets` | Returns the array of targets specified during construction | +| `retarget` | Triggers a retarget for the targeter | + +### retarget method + +If the targets are created after the DomTargeter and DOMContentLoaded event has fired, the retarget method can be used to check for target existence. + +```js +// manually retarget +searchPageTarget.retarget(); +``` + +### getTargets method + +Return the array of targets specified during construction. diff --git a/docs/PREACT_BADGES.md b/docs/REFERENCE_CUSTOM_BADGE_TEMPLATES.md similarity index 52% rename from docs/PREACT_BADGES.md rename to docs/REFERENCE_CUSTOM_BADGE_TEMPLATES.md index 223e2d9f8..575f647b7 100644 --- a/docs/PREACT_BADGES.md +++ b/docs/REFERENCE_CUSTOM_BADGE_TEMPLATES.md @@ -1,44 +1,8 @@ -## Badges +# Custom Badge Templates -Badges are self-configured in the Searchspring Management Console +Custom Badge Templates can be created and sync to the Searchspring Management Console using the Snapfu CLI. See [Welcome > Setup](https://searchspring.github.io/snap/snap-setup) for installing Snapfu -To displays badges the Result card must include the [OverlayBadge](https://searchspring.github.io/snap/#/components-preact?params=%3Fpath%3D%2Fstory%2Fmolecules-overlaybadge--default) and [CalloutBadge](https://searchspring.github.io/snap/#/components-preact?params=%3Fpath%3D%2Fstory%2Fmolecules-calloutbadge--default) components - - -### OverlayBadge - -The `OverlayBadge` component wraps elements (children) that should have badges overlayed - typically the product image - -```jsx - - - -``` - -### CalloutBadge - -The `CalloutBadge` component displays badges inline and can be placed in any position in the Result card - -```jsx - -``` - -### Badge Components -The `OverlayBadge` and `CalloutBadge` components are responsible for displaying badges - -The default badges available: - -- [BadgePill](https://searchspring.github.io/snap/#/components-preact?params=%3Fpath%3D%2Fstory%2Fatoms-badgepill--default) -- [BadgeText](https://searchspring.github.io/snap/#/components-preact?params=%3Fpath%3D%2Fstory%2Fatoms-badgetext--default) -- [BadgeRectangle](https://searchspring.github.io/snap/#/components-preact?params=%3Fpath%3D%2Fstory%2Fatoms-badgerectangle--default) -- [BadgeImage](https://searchspring.github.io/snap/#/components-preact?params=%3Fpath%3D%2Fstory%2Fatoms-badgeimage--default) - - -## Custom Badge Templates - -Custom Badge Templates can be created and sync to the Searchspring Management Console using the Snapfu CLI. See [Getting Started > Setup](https://searchspring.github.io/snap/#/start-setup) for installing Snapfu - -### Initialize Custom Badges +## Initialize Custom Badges First we'll initialize a new custom badge. The code examples on this page will use a `[badgename]` of `CustomBadge` @@ -94,7 +58,7 @@ export const CustomBadge = observer((props) => { } ``` -### Syncing Custom Badges +## Syncing Custom Badges Next we'll sync our custom badge - registering it to the Searchspring Management Console @@ -102,11 +66,12 @@ Next we'll sync our custom badge - registering it to the Searchspring Management snapfu badges sync [badgename] ``` -### Using Custom Badges +## Using Custom Badges Finally in order to use a custom badge component, we'll need to provide a `componentMap` prop containing a mapping of our custom components to the `OverlayBadge` and `CalloutBadge` components -**Note:** This is not required if using the default selection of badges +> [!NOTE] +> This is not required if using the default selection of badges ```jsx import { CustomBadge } from './components/Badges/CustomBadge'; @@ -131,38 +96,25 @@ import { CustomBadge } from './components/Badges/CustomBadge'; The `componentMap` prop can also be used to overwrite the default badge components without the need of initializing and syncing a dedicated custom component -### Badge Template Overview (JSON file) - -#### Required: - -`type` - should not be changed. It is utilized by the Snapfu CLI when syncing - -`name` - unique badge template identifier - -`label` - label that is displayed when selecting this badge template within the Searchspring Management Console - -`description` - badge template description - -`component` - component name this badge template should use. It should line up with the mapping provided to the `componentMap` props. See `Using Custom Badges` section above - -`locations` - a list of template locations this badge template can be placed in. This can be used to restrict certain badges to certain locations. See `Custom Badge Locations` section below for adding locations. See `Badge Template Locations` section below for possible values - -`parameters` - a list of badge template parameters. Can be an empty array to not contain template parameters. See `Badge Template Parameters` section below for possible parameters - -#### Optional: - -`value.enabled` - boolean that when true, required a badge `value` to be provided when using this template - -`value.validations.min` - ensures `value` meets a numerical minimum or string length - -`value.validations.max` - ensures `value` meets a numerical maximum or string length - -`value.validations.regex` - ensures `value` meets a regex definition. Must also provide `value.validations.regexExplain` - -`value.validations.regexExplain` - required if using `value.validations.regex`. Describes the regex definition and is displayed as an error message if the regex validation fails - - -### Badge Template Locations +## Badge Template Overview (JSON file) + +| Field | Required | Description | +|-------|----------|-------------| +| `type` | ✔️ | Should not be changed. It is utilized by the Snapfu CLI when syncing | +| `name` | ✔️ | Unique badge template identifier | +| `label` | ✔️ | Label that is displayed when selecting this badge template within the Searchspring Management Console | +| `description` | ✔️ | Badge template description | +| `component` | ✔️ | Component name this badge template should use. It should line up with the mapping provided to the `componentMap` props. See `Using Custom Badges` section above | +| `locations` | ✔️ | A list of template locations this badge template can be placed in. This can be used to restrict certain badges to certain locations. See `Custom Badge Locations` section below for adding locations. See `Badge Template Locations` section below for possible values | +| `parameters` | ✔️ | A list of badge template parameters. Can be an empty array to not contain template parameters. See `Badge Template Parameters` section below for possible parameters | +| `value.enabled` | ➖ | Boolean that when true, required a badge `value` to be provided when using this template | +| `value.validations.min` | ➖ | Ensures `value` meets a numerical minimum or string length | +| `value.validations.max` | ➖ | Ensures `value` meets a numerical maximum or string length | +| `value.validations.regex` | ➖ | Ensures `value` meets a regex definition. Must also provide `value.validations.regexExplain` | +| `value.validations.regexExplain` | ➖ | Required if using `value.validations.regex`. Describes the regex definition and is displayed as an error message if the regex validation fails | + + +## Badge Template Locations Badge template locations is an array of strings Possible values when using default locations: `left`, `left/left`, `right`, `right/right`, `callout`, `callout/callout` @@ -189,43 +141,24 @@ For example, if the locations.json file contains the following location definiti To restrict a badge template to a custom location, the badge template `locations` array should contain the `tag` of the locations. Ie. `left/left-bottom` -### Badge Template Parameters -Badge template parameters is an array of objects. Each object is a template parameter and contains the following properties: - -#### Required: - -`name` - unique badge parameter identifier - -`type` - parameter value type. Available types: `array`, `string`, `color`, `url`, `integer`, `decimal`, `boolean`, `checkbox`, `toggle`. See example below for example usage of each type - -`label` - label that is displayed when selecting this badge parameter within the Searchspring Management Console - -`description` - badge parameter description - -`options` - required only if `type` is `array`. Define an array of strings containing dropdown value options - - -#### Optional: - -`defaultValue` - default value that will be used unless specified when configuring a new badge rule. Must be a string regardless of different `type` options - -`validations` - only applicable if `type` is `string`, `url`. `integer`, `decimal` - -`validations.min` - only applicable if `type` is `integer`, `decimal`, `string`, `url`. Should be a number (negative values also accepted) - -- If `type` is `integer` or `decimal`, ensures `defaultValue` or the user defined `value` meets a **numerical minimum** - -- If `type` is `string` or `url`, ensures `defaultValue` or the user defined `value` meets a minimum **character length** - -`validations.max` - only applicable if `type` is `integer`, `decimal`, `string`, `url`. Should be a number (negative values also accepted) - -- If `type` is `integer` or `decimal`, ensures `defaultValue` or the user defined `value` meets a **numerical maximum** - -- If `type` is `string` or `url`, ensures `defaultValue` or the user defined `value` meets a maximum **character length** +## Badge Template Parameters +Badge template parameters is an array of objects. Each object is a template parameter and contains the following properties: -`validations.regex` - ensures `defaultValue` or the user defined `value` meets a regex definition. Must also provide `validations.regexExplain` +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | ✔️ | string | Unique badge parameter identifier | +| `type` | ✔️ | string | Parameter value type. Available types: `array`, `string`, `color`, `url`, `integer`, `decimal`, `boolean`, `checkbox`, `toggle`. See example below for example usage of each type | +| `label` | ✔️ | string | Label that is displayed when selecting this badge parameter within the Searchspring Management Console | +| `description` | ✔️ | string | Badge parameter description | +| `options` | ✔️* | string[] | Required only if `type` is `array`. Define an array of strings containing dropdown value options | +| `defaultValue` | ➖ | string | Default value that will be used unless specified when configuring a new badge rule. Must be a string regardless of different `type` options | +| `validations` | ➖ | object | Only applicable if `type` is `string`, `url`, `integer`, `decimal` | +| `validations.min` | ➖ | number | Only applicable if `type` is `integer`, `decimal`, `string`, `url`. Should be a number (negative values also accepted). If `type` is `integer` or `decimal`, ensures `defaultValue` or the user defined `value` meets a **numerical minimum**. If `type` is `string` or `url`, ensures `defaultValue` or the user defined `value` meets a minimum **character length** | +| `validations.max` | ➖ | number | Only applicable if `type` is `integer`, `decimal`, `string`, `url`. Should be a number (negative values also accepted). If `type` is `integer` or `decimal`, ensures `defaultValue` or the user defined `value` meets a **numerical maximum**. If `type` is `string` or `url`, ensures `defaultValue` or the user defined `value` meets a maximum **character length** | +| `validations.regex` | ➖ | string | Ensures `defaultValue` or the user defined `value` meets a regex definition. Must also provide `validations.regexExplain` | +| `validations.regexExplain` | ➖ | string | Required if using `validations.regex`. Describes the regex definition and is displayed as an error message if the regex validation fails | -`validations.regexExplain` - required if using `validations.regex`. Describes the regex definition and is displayed as an error message if the regex validation fails +*Required only when `type` is `array` ```json @@ -310,7 +243,7 @@ Badge template parameters is an array of objects. Each object is a template para ## Custom Badge Locations -Custom Badge Locations can be created and synced to the Searchspring Management Console using the Snapfu CLI. See [Getting Started > Setup](https://searchspring.github.io/snap/#/start-setup) for installing Snapfu +Custom Badge Locations can be created and synced to the Searchspring Management Console using the Snapfu CLI. See [Welcome > Setup](https://searchspring.github.io/snap/snap-setup) for installing Snapfu Custom overlay and callout locations can be created by defining a `locations.json` file in the project. It is recommended to create it at: `src/components/Badges/locations.json` @@ -325,7 +258,8 @@ Custom overlay and callout locations can be created by defining a `locations.jso `['left' | 'right' | 'callout'].name` - badge location name that is displayed when selecting this location within the Searchspring Management Console -**important** - it is strongly recommended to keep the default location tags (ie. `left[0].tag="left"`, `right[0].tag="right"`, `callout[0].tag="callout"`) to ensure any existing badges are backwards compatible with additional locations +> [!IMPORTANT] +> It is strongly recommended to keep the default location tags (ie. `left[0].tag="left"`, `right[0].tag="right"`, `callout[0].tag="callout"`) to ensure any existing badges are backwards compatible with additional locations ```json { diff --git a/docs/REFERENCE_CUSTOM_RECOMMENDATION_TEMPLATES.md b/docs/REFERENCE_CUSTOM_RECOMMENDATION_TEMPLATES.md new file mode 100644 index 000000000..d35bd36c5 --- /dev/null +++ b/docs/REFERENCE_CUSTOM_RECOMMENDATION_TEMPLATES.md @@ -0,0 +1,66 @@ +# Custom Templates + +Let's look at how to setup a custom recommendation template using the Snapfu CLI. See [Getting Started > Setup](https://searchspring.github.io/snap/snap-setup) for installing Snapfu. + +There are three steps required for adding recommendations: +- Creating the local template files +- Syncing the template to the Searchspring Management Console +- Updating our Snap config (see instantiator config above) + +### Creating a new recommendation template +To generate a new template, run the following at the root of the project. This command will prompt you to provide various inputs such as the template type, template name, an optional description, and the path to the component. + +```bash +snapfu recs init +``` + +In this example, we'll create a new template with a name of "DefaultRecommendations" + +This will generate three files which should be committed to your repository: +- The `.jsx` file (/src/components/Recommendations/Default.jsx) is the template itself containing our component. +- The `.scss` file (/src/components/Recommendations/DefaultRecommendations.scss) is imported by the `.jsx` template file and optional CSS styling can be defined here. +- The `.json` file (/src/components/Recommendations/DefaultRecommendations.json) contains various meta data for this template and is used when running `snapfu recs sync` to sync this template to Searchspring's Management Console API. See syncing documentation below. + +#### `.json` file parameters +The `.json` file allows template parameters to be specified. Template parameters are returned in the Recommendations API and accessible in your recommendation components via `controller.store.profile.display.templateParameters`. Template parameters can be set in the Searchspring Management Console after the template has been synced. + +Example: +```json +{ + "type": "snap/recommendation/default", + "name": "defaultrecommendations", + "label": "DefaultRecommendations", + "description": "DefaultRecommendations custom template", + "component": "DefaultRecommendations", + "orientation": "horizontal", + "parameters": [ + { + "name": "title", + "label": "Title", + "description": "text used for the heading", + "defaultValue": "Recommended Products" + } + ] +} +``` + +At this point, you can customize the `.jsx` template to your requirements. By default it will utilize our [Recommendation](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Forganisms-recommendation--default) component. + +### Syncing Templates + +Syncing custom templates to Searchspring's Management Console is required before they can be used. This is due to the recommendations API that returns the template's component name defined for the profile we set in the `.json` file above. + +Templates also support branching. For production-ready templates, please ensure you are on the repository's default branch (typically `production`) before running `snapfu recs sync` + +If you are using Snap's Github action, merges into the `production` branch will execute the sync command within the action to ensure your templates are always synced. See [Reference > Build & Deploy > Github Action](https://searchspring.github.io/snap/build-deploy#github-action) for Github Actions usage. + +To sync templates, run the follow at the root of the project. + +```bash +snapfu recs sync +``` + +#### Syncing to multiple accounts +To sync the template(s) to multiple accounts, multiple siteIds must be defined in the project's package.json file + +See [Reference > Build & Deploy > Project package.json configuration](https://searchspring.github.io/snap/build-deploy#project-packagejson-configuration) \ No newline at end of file diff --git a/docs/REFERENCE_SEARCHCONTROLLER.md b/docs/REFERENCE_SEARCHCONTROLLER.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/REFERENCE_VARIANTS.md b/docs/REFERENCE_VARIANTS.md new file mode 100644 index 000000000..9b1f1bd3e --- /dev/null +++ b/docs/REFERENCE_VARIANTS.md @@ -0,0 +1,274 @@ +# Variants + +Product variants allow you to represent different versions of the same base product - such as different sizes, colors, styles or other attributes that distinguish product options. This helps to improve the shopping experience by making product options easily discoverable, and allow customers to switch between variants directly from search/category pages. Common examples include: + +- Clothing items available in multiple sizes and colors +- Electronics with different storage capacities or features +- Furniture pieces with different fabric or finish options +- Beauty products in different scents or formulations + +Snap's variants functionality helps manage these product variations by providing tools to configure, display and interact with variant data in your components. + +Configure variants by setting the variant field in either: +- Controller config: `controllers[controller].config.settings.variants.field` +- Recommendation config: `instantiators.recommendation.config.settings.variants.field` + +```js +const config = { + instantiators: { + recommendation: { + components: { + Bundle: async () => (await import('./components/Recommendations/Bundle/Bundle')).Bundle, + }, + config: { + settings: { + variants: { + field: 'ss_variants', + }, + }, + }, + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + settings: { + variants: { + field: 'ss_variants', + }, + }, + }, + }, + ], + }, +}; + +const snap = new Snap(config); +``` + +Once configured, each result that has variants in `controller.store.results` should include a `variants` object with: + +| Property | Description | +| --- | --- | +| `active` | Currently selected variant data | +| `data` | Array of all available variants | +| `selections` | Variant selection state and options (detailed below) | +| `setActive(variant)` | Select a specific variant | +| `update(newData)` | Update variant data post-initialization | + +## Updating Variant Data + +Use `result.variants.update(newData)` to modify variant data after initialization. This is useful for adding dynamic data like: +- Pricing +- Inventory levels +- Swatch information + +The update function expects the new variant data to match the variantData type. + +```js +export type VariantData = { + mappings: SearchResponseModelResultMappings; + attributes: Record; + options: Record; +}; + +snap.getController('search').then((search) => { + search.on('afterStore', async (eventData, next) => { + search.store.results.forEach((result) => { + //fetch new variant data from external source + const newVariantData: VariantData = await fetchVariantData(result.id); + //update the variant data in the result + result.variants.update(newVariantData); + }); + await next(); + }); +}); +``` + +## `result.variants.selections` + +The `result.variants.selections` object helps you build variant option picker components (like size/color selectors). When a user makes a selection, it automatically: +- Refines selection value options to update available sibling options based on current selections +- Updates the active variant on the main result +- Updates the `result.display` property with the selected variant's data (eg: price, image URL, name, etc..) + +| Property | Description | +| :--- | :--- | +| `field` | Selection field name (e.g. "Color", "Size") | +| `selected` | Currently selected value (type: VariantSelectionValue) | +| `previouslySelected` | Previously selected value (type: VariantSelectionValue) | +| `values` | Array of available selection values (type: VariantSelectionValue[]) | +| `select(value)` | Select a variant option (e.g. `selection.select('blue')`) | +| `reset()` | Reset selection to default state | + +```js +export type VariantSelectionValue = { + value: string; + label?: string; + thumbnailImageUrl?: string; + backgroundImageUrl?: string; + background?: string; + available?: boolean; +}; +``` + +Below is a React component demonstrating how to implement variant selections: + +```jsx +const selections = result.variants.selections; + +{ selections?.map((selection) => ( +
+
{selection.field}
+
    + {selection.values.map((option) => { + const selected = selection.selected.value == option.value; + return ( +
  • selection.select(option.value)} + title={option.label} + role="option" + aria-selected={selected} + > + +
  • + ); + })} +
+
+)) } +``` + +## Realtime Variants + +Automatically sync variant selections between your product page and recommendation results by: + +1. Enable the feature in your instantiator config: + +```js +const config = { + instantiators: { + recommendation: { + components: { + Bundle: async () => (await import('./components/Recommendations/Bundle/Bundle')).Bundle, + }, + config: { + settings: { + variants: { + field: 'ss_variants', + realtime: { + enabled: true, + }, + }, + }, + }, + }, + }, +}, +``` + +2. On your product detail page, add the following attributes to the variant selectors for the main product. (see example below) + +On your product page templates, add these attributes to the clickable variant selector elements: + +- `ss-variant-option="${field}:${value}"` on each option element (e.g. "Color:Blue") +- `ss-variant-option-selected` on currently selected options + +Alternatively, you can use the `controller/selectVariantOptions` global event to programmatically select variants. See [Global Events](https://github.com/searchspring/snap/tree/main/docs/REFERENCE_CONFIGURATION_MIDDLEWARE.md) for details. + +The following shows color and size selectors with "Night" and "S" selected: + +```jsx +
+ +
+ + + + + + + + + +
+
+``` + +### Realtime Variant Filters + +You can filter which results update in realtime by adding filters to your config: + +- `first` - Updates only the first product in results (useful for Bundle Recommendations' seed product) +- `unaltered` - Updates only products that haven't been manually selected by the user on the current page. + +```js +variants: { + field: 'ss_variants', + realtime: { + enabled: true, + filters: ['first'] + } +} +``` + +### Variant options + +Configure individual variant fields using `controllers[controller].config.settings.variants.options`. This object is keyed by option field name for field-specific settings. + +| Property | Description | Default | Required | +| :--- | :--- | :---: | :---: | +| `label` | Custom display label for the option field (e.g., "color" → "colour") | ➖ | | +| `preSelected` | Array of option values to select by default (e.g., `['red', 'blue']`) | ➖ | | +| `thumbnailBackgroundImages` | When true, uses variant thumbnail images as option background images | ➖ | | +| `mappings` | Object containing value-specific overrides | ➖ | | +| `mappings[optionValue].label` | Override display label for a specific option value | ➖ | | +| `mappings[optionValue].background` | Override background color for a specific option value | ➖ | | +| `mappings[optionValue].backgroundImageUrl` | Override background image URL for a specific option value | ➖ | | + +```js +const config = { + settings: { + variants: { + field: "ss_variants", + options: { + color: { + label: "Colour", + preSelected: ['transparent'], + mappings: { + red: { + label: 'Cherry', + backroundImageUrl: '/images/cherry.png' + }, + blue: { + label: "Sky", + background: "teal", + } + } + } + } + } + } +} +``` \ No newline at end of file diff --git a/docs/SETUP.md b/docs/SETUP.md deleted file mode 100644 index 90d5ba0ab..000000000 --- a/docs/SETUP.md +++ /dev/null @@ -1,125 +0,0 @@ -## Setup - - -## Prerequisites - -- npm v7 - -- node v14+ - -## Snapfu - -_To follow the way of Snap - you must have strong Snap-fu._ - -Snapfu is a CLI interface for creating a Snap project from a template. You do not need to utilize Snapfu to develop using Snap packages, but it does simplify the process. - -### Install - -```sh -npm install -g snapfu -``` - - -### Login - -Snapfu will prompt you to authenticate with Github. - -```sh -snapfu login -``` - -### Initialize new project - -Initialize a new project using Snapfu: - -```sh -snapfu init [projectname] -cd [projectname] -``` - -You will be asked for your Site ID, which you find in the [Searchspring Management Console](https://manage.searchspring.net) - -Then select your desired framework, such as Preact. - -This will create a new project with git repo. - -### Install project dependencies - -```sh -npm install -``` - -## Development - -After initializing a Snapfu project, it will contain a template ready for you to use. - -```sh -npm run dev -``` - -The local server will run at [https://localhost:3333](https://localhost:3333) and serves the contents of the `/public` directory as well as the project bundle files. The development bundle files are be served from [https://localhost:3333/bundle.js](https://localhost:3333/bundle.js). - -Note: The local server uses a self-signed certificate for HTTPS, this will require adding a browser security exception for allowing the loading of these resources. There is a Chrome browser flag that can be set to prevent adding an exception: chrome://flags/#allow-insecure-localhost - -Using Snapfu, there are two common ways to develop a project, using a local mockup file and leveraging the Snapfu Chrome extension. - -### Local mockup - -Creation of a local mockup file (for example, `index.html` or `mockup.html`) within the `/public` directory allows for the quickest development of a Snap integration. Mockup files can be created by copying the source files of particular pages that will contain the Snap integration - typically the search and category pages. These pages will need to include a link to the local bundle file (`bundle.js`) in order for the Snap bundle to run. A minimal mockup file (shown below) contains only the necessary script and target elements to get components rendered on the page. More complete mockups would normally contain DOM structure and links to website resources that would be present on the live site (CSS, etc...). - -```html - - - - - Snap - - -
-
-
- - - - -``` - -A `mockup.html` file within the `/public` directory is viewable at [https://localhost:3333/mockup.html](https://localhost:3333/mockup.html). - -### Snapfu extension - -The Snapfu extension is a tool that can be used to test local changes on a live website. Visit the website you are developing for, then click the extension to enable it and set the mode to `local`, then press `Save`. - -The page will reload with the local development bundle [https://localhost:3333/dist/bundle.js](https://localhost:3333/dist/bundle.js) injected into the current website. While `npm run dev` is running, the page will automatically reload upon saving any code modifications. - -The Github repository for the [Snapfu extension](https://github.com/searchspring/snapfu-extension) has additional usage and installation documentation. -## Publishing - -### Searchspring Managed Integration - -When a Snap project resides within the [Searchspring Implementations Github organization](https://github.com/searchspring-implementations), the Snap bundle will automatically be built when the Snap (Github) action runs - this run is triggered by a Git push to the repository. - -```sh -git commit -am "Hello, snapfu" -git push -``` - -Github action runs triggered on the `production` branch will build and deploy bundle files to this URL: - -`https://snapui.searchspring.io/[your_site_id]/bundle.js` - -Builds on different branch names will be deployed to: - -`https://snapui.searchspring.io/[your_site_id]/[branch]/bundle.js` - -### Self Integration (self-snap) - -If you have opted to self integrate and use Snap you will need to host the build files yourself. If the URL above is used it will result in a 403 Error. - -To host your own build files follow the below steps in your project. - -1. Ensure you have changed directories so that you are in the parent directory of the project in your terminal -2. In your terminal run the command `npm run build`, will output build files to `./dist` -3. Navigate to `./dist` and copy the generated build files -4. Go to the codebase of your E-commerce platform (Shopify, Bigcommerce, Magento, etc.) and copy/paste the generated build files in a directory (most platforms have an ***assets*** directory) -5. On the frontend of the site, add a script block as outlined in the [integration](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION.md) section - be sure to change the `src` attribute to point to the `bundle.js` file and align the URL with your self-hosted build files (eg: /assets/bundle.js) diff --git a/docs/SNAP_AUTOCOMPLETE.md b/docs/SNAP_AUTOCOMPLETE.md new file mode 100644 index 000000000..0b1e712c5 --- /dev/null +++ b/docs/SNAP_AUTOCOMPLETE.md @@ -0,0 +1,106 @@ +# Autocomplete + +To set up Autocomplete using Snap, we'll need to define an autocomplete controller in our Snap configuration. See [AutocompleteController reference](https://searchspring.github.io/snap/reference-controller-autocomplete) for all available configuration options + +One notable thing to mention as you may see a duplicate `selector` property in both the `config` and `targeter`. + +The `config.selector` specifies the `` element(s) to attach events (focus, keydown, submit) to that respond to Autocomplete actions. This supports a selector that targets many elements. + +The `targeter.selector` specifies the DOM node where the `targeter.component` will be rendered into. + +However in our example, since they are both the same value, the Autocomplete component will be rendered as a child DOM node below the `` element that is currently focused. + + +```js +// src/index.js + +import { Snap } from '@searchspring/snap-preact'; + +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + autocomplete: [ + { + config: { + id: 'autocomplete', + selector: 'header input[type="search"]', + settings: { + trending: { + limit: 5, + }, + history: { + limit: 5, + }, + }, + }, + targeters: [ + { + selector: 'header input[type="search"]', + hideTarget: true, + component: async () => { + return (await import('@searchspring/snap-preact-components')).Autocomplete; + }, + }, + ], + }, + ], + }, +}); +``` + + + +## Autocomplete Store + +It is recommended to utilizing the [Autocomplete](https://searchspring.github.io/snap/preact-components?params=?path=/story/organisms-autocomplete--default) component from `@searchspring/snap-preact-components` to display Autocomplete. + +The following properties are specific to an Autocomplete Store via an Autocomplete Controller. + +### AutocompleteController.store.merchandising + +See `SearchController.store.merchandising` section above. + +### AutocompleteController.store.search + +The `search` property contains information about the current query. However unlike SearchController.store.search, AutocompleteController.store.search does not contain a `didYouMean` query. + + +### AutocompleteController.store.facets + +See `SearchController.store.facets` section above. + +In addition, each facet value will contain a `preview` method that should be invoked on the `onFocus` event of a facet value. This method will lock the current facets such that when the store is updated with the filtered results, the original facets do not get replaced with the new facets from the filtered query. + +### AutocompleteController.store.filters + +See `SearchController.store.filters` section above. + +### AutocompleteController.store.results + +See `SearchController.store.results` section above. + +### AutocompleteController.store.terms + +The `terms` property contains an array of autocomplete terms that are relevant to the query. Each term contains a `preview` method that should be invoked on the `onFocus` event of a term value. This method will lock the current terms and unlock the previous facets (if changing terms with a facet filter applied) such that when the store is updated with the results for the new term, the original terms do not change. + +### AutocompleteController.store.trending + +The `trending` property contains an array of trending `terms`. Trending terms are not relevant to the current query and are generated from collected reporting data. It is recommended to display trending terms as a starting point when the `` is focused and does not yet contain a value. Trending terms must be enabled via settings in the AutocompleteController config. + + +### AutocompleteController.store.pagination + +See `SearchController.store.pagination` section above. + +### AutocompleteController.store.sorting + +See `SearchController.store.sorting` section above. + +### AutocompleteController.store.history + +The `history` property contains an array of previously searched `terms`. Historical terms are not relevant to the current query and are stored in localstorage. Historical terms can be displayed in the Autocomplete component in place of or in addition to trending and suggested terms. Historical terms must be enabled via settings in the AutocompleteController config. + diff --git a/docs/INTEGRATION_BACKGROUND_FILTERS.md b/docs/SNAP_BACKGROUND_FILTERS.md similarity index 55% rename from docs/INTEGRATION_BACKGROUND_FILTERS.md rename to docs/SNAP_BACKGROUND_FILTERS.md index 1440cb332..3ac004f00 100644 --- a/docs/INTEGRATION_BACKGROUND_FILTERS.md +++ b/docs/SNAP_BACKGROUND_FILTERS.md @@ -1,18 +1,27 @@ -## Background Filters -Background filters allow a page to be refined without displaying the active filter to the end-user. This is primarily used for category pages, although can also be used for custom functionality such as restricting visibility of products to user groups. The filter value is commonly retrieved from a context variable and applied as a background filter within the Snap config object. +# Background Filters +Background filters allow a page to be refined without displaying the active filter to the end-user. This is primarily used for category pages, although can also be used for custom functionality such as restricting visibility of products to user groups. The filter value is commonly retrieved from a context variable and applied as a background filter within the Snap config object. Background filters could also be applied to all services by setting `client.globals.filters` in the Snap config instead of on a per-controller basis. -In this example, we'll retrieve the `collection` object from the context and apply it as a category background filter for our search controller. +## Background Filter Object + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `field` | string | ✔️ | The field name to filter on (e.g., 'collection_handle', 'category', 'brand') | +| `value` | string \| number \| array | ✔️ | The value(s) to filter by. Can be a single value or array of values (if type is 'range'). For multiple 'value' type filters provide an entry for each value as a new background filter object. | +| `type` | string | ✔️ | Filter type. Common values: 'value', 'range' | +| `background` | boolean | ✔️ | Must be set to `true` to indicate this is a background filter | +In this example, we'll retrieve the `collection` object from the context and apply it as a category background filter for our search controller. + ```html - ``` -```typescript +```js import { getContext } from '@searchspring/snap-toolbox'; const context = getContext(['collection']); @@ -33,7 +42,7 @@ if (context.collection?.handle) { const config = { client: { globals: { - siteId: 'abc123', + siteId: 'REPLACE_WITH_YOUR_SITE_ID', }, }, controllers: { @@ -52,5 +61,3 @@ const config = { const snap = new Snap(config); ``` - -Background filters could also be applied to all services by setting `client.globals.filters` in the Snap config instead of on a per-controller basis. \ No newline at end of file diff --git a/docs/SNAP_BADGES.md b/docs/SNAP_BADGES.md new file mode 100644 index 000000000..2f8191bbc --- /dev/null +++ b/docs/SNAP_BADGES.md @@ -0,0 +1,39 @@ +# Badges + +Badges are self-configured in the Searchspring Management Console. + +> [!NOTE] +> This feature may not be enabled by default. Please contact your account manager to enable this feature. + +To displays badges the Result card must include the [OverlayBadge](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fmolecules-overlaybadge--default) and [CalloutBadge](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fmolecules-calloutbadge--default) components + + +## OverlayBadge + +The `OverlayBadge` component wraps elements (children) that should have badges overlayed - typically the product image + +```jsx + + + +``` + +## CalloutBadge + +The `CalloutBadge` component displays badges inline and can be placed in any position in the Result card + +```jsx + +``` + +## Badge Components +The `OverlayBadge` and `CalloutBadge` components are responsible for displaying badges + +The default badges available: + +- [BadgePill](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-badgepill--default) +- [BadgeText](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-badgetext--default) +- [BadgeRectangle](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-badgerectangle--default) +- [BadgeImage](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Fatoms-badgeimage--default) + +Additional custom badge components can be created and synced to the Searchspring Management Console using the Snapfu CLI. See [Custom Badge Templates reference](https://searchspring.github.io/snap/reference-custom-badge-templates) \ No newline at end of file diff --git a/docs/SNAP_CLIENT.md b/docs/SNAP_CLIENT.md new file mode 100644 index 000000000..21c6d01d6 --- /dev/null +++ b/docs/SNAP_CLIENT.md @@ -0,0 +1,184 @@ +# Snap Client + +The `@searchspring/snap-client` package is a client for the Searchspring API. It is a wrapper around the Searchspring API that provides a simple interface for fetching data from the API. + + +## ClientGlobals + +When constructing a new Client, the first argument is a `ClientGlobals` object. This object can be used to set client search parameters that will apply to all requests made with the client (and subsequently any controllers using the client as a service). Typically only the siteId will be set here, but could be used for setting globally utilized background filters and/or sorts as well. + +You can find your Searchspring siteId in the [Searchspring Management Console](https://manage.searchspring.net) + +```js +import { Client } from '@searchspring/snap-client'; + +const client = new Client({ + siteId: '1234567890', + filters: [{ + field: 'stock_status', + value: 'yes', + type: 'value', + background: true + }], +}); +``` + + +## ClientConfig + +The second argument is an optional `ClientConfig` object. + +| Option | Type | Description | +|--------|------|-------------| +| mode | `string` \| `AppMode` enum
(e.g. `'development'`, `'production'`, `AppMode.development`, `AppMode.production`) | Optional. Sets the client mode. `'development'` disables network caching; | +| fetchApi | `WindowOrWorkerGlobalScope['fetch']` | Alternative fetch implementation to use for requests. Defaults to global `fetch`. | +| meta | `RequesterConfig` | Configuration for the `meta` endpoint (origin, headers, cache, globals). | +| search | `RequesterConfig` | Configuration for the `search` endpoint (origin, headers, cache, globals). | +| autocomplete | `RequesterConfig & { requesters?: HybridRequesterConfig }` | Configuration for the `autocomplete` endpoint, including hybrid requesters. | +| finder | `RequesterConfig` | Configuration for the `finder` endpoint (origin, headers, cache, globals). | +| recommend | `RequesterConfig` | Configuration for the `recommend` endpoint (origin, headers, cache, globals). | +| suggest | `RequesterConfig` | Configuration for the `suggest` endpoint (origin, headers, cache, globals). | + + +Where `RequesterConfig` is defined as: + +```js +type RequesterConfig = { + origin?: string; + headers?: { [key: string]: string }; + cache?: { + enabled?: boolean; // default: true - enables caching + ttl?: number; // default: 300000 (5 minutes) - time in milliseconds to store a response in the cache + maxSize?: number; // default: 200 - maximum size in KB to store in sessionStorage + purgeable?: boolean; // default: true - allows the cache to be purged from sessionStorage when maxSize is reached (with exception when used for meta) + entries?: { [key: string]: Response }; // default: undefined - allows for pre-populating the cache with entries, primarily used for email recommendations + }; + globals?: Partial; +}; +``` + +Example: + +```js +import { Client } from '@searchspring/snap-client'; + +const client = new Client({ + siteId: '1234567890', +}, { + mode: 'development', + fetchApi: window.fetch, + search: { + cache: { + enabled: true, + ttl: 300000, + maxSize: 100, + purgeable: true, + }, + }, +}); +``` + +## Caching & Retries + +If the Client is running in a browser environment, the client will cache responses for 5 minutes in sessionStorage if available. This prevents unnecessary network requests and improves performance for previously made requests. + +Also, for unexpected API response status codes, the client will retry failed requests by default up to 8 times with starting with a 1 second delay with fibonacci backoff for each subsequent retry. + +Both caching and retries can be disabled or configured for each endpoint in the `ClientConfig` object. + + +## API Types + +The client uses the [@searchspring/snapi-types](https://www.npmjs.com/package/@searchspring/snapi-types) package to define the API types. + + +### Methods + +#### meta + +The `meta` endpoint is used to fetch metadata about the site and also contains common static data such as sort options that don't change between other API queries. + + +```js +const meta: MetaResponseModel = await client.meta(); +``` + +#### autocomplete + +The `autocomplete` endpoint is used to fetch autocomplete suggestions and results for a given search query and also requests the meta data for the site. + + +```js +const [meta, autocomplete]: [MetaResponseModel, AutocompleteResponseModel] = await client.autocomplete({ + suggestions: { + count: 5 + }, + search: { + query: { + string: 'search query', + spellCorrection: true + } + } +}); +``` + +#### search + +The `search` endpoint is used to fetch search results for a given search query and also requests the meta data for the site. + + +```js +const [meta, search]: [MetaResponseModel, SearchResponseModel] = await client.search({ + search: { + query: { + string: 'search query', + }, + }, +}); +``` + + +#### finder + +The `finder` method makes a request to the Searchspring Finder API to fetch search results for a given search query. + +```js +const [meta, finder]: [MetaResponseModel, SearchResponseModel] = await client.finder({ + search: { + query: { + string: 'search query', + }, + }, + filters: [{ + type: 'value', + field: 'color', + background: false, + value: 'red', + }], +}); +``` + +#### trending + +The `trending` method makes a request to the Searchspring Trending API to fetch trending search queries. + +```js +const results: TrendingResponseModel = await client.trending({ + limit: 5 +}); +``` + + +#### recommend + +The `recommend` method makes a request to the Searchspring Profile API and Recommend API to fetch recommendations for a given profile tag. This will also request the meta data. + +```js +const results: RecommendCombinedResponseModel = await client.recommend({ + tag: 'similar', + products: ['product123'], +}); +``` + + + \ No newline at end of file diff --git a/docs/SNAP_FINDER.md b/docs/SNAP_FINDER.md new file mode 100644 index 000000000..07157937c --- /dev/null +++ b/docs/SNAP_FINDER.md @@ -0,0 +1,166 @@ +# Finder + +To set up a product Finder using Snap, we'll need to define a finder controller in our Snap configuration. See [FinderController reference](https://searchspring.github.io/snap/reference-controller-finder) for all available configuration options. + +## Configuration +There are two types of Finder configurations, a Hierarchy and Non-Hierarchy. The difference is the type of field being used and how it is configured in the Searchspring Management Console. + +Note: Ensure that the field that you will be using for Finder is marked for 'Filter' on the [Field Settings page](https://manage.searchspring.net/management/field-settings/display-fields). + +Also within the Searchspring Management Console, ensure that under Site Customizations > Display Settings > Filtering, the field is set as a heiarchy display type and under advanced settings, the 'Hierarchy Delimiter' is set to the appropriate delimiter for your field. + +### Hierarchy Configuration +To use a Hierarchy configuration, ensure that the config's `fields` array contain a single entry, and that the field is of type `hierarchy` in the Searchspring Management Console. Here is an example of a Hierarchy `FinderControllerConfig` object: + +```js +// src/index.js + +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + finder: [ + { + config: { + id: 'finder', + url: '/search' + fields: [{ + field: 'ss_tire', + label: 'Wheel Finder', + levels: ['Year', 'Make', 'Model', 'Wheel Size'] + }] + }, + targeters: [ + { + selector: '#searchspring-finder', + component: async () => { + return (await import('./components/Finder/Finder')).Finder; + }, + }, + ], + }, + ], + }, +}); +``` + +### Non-Hierarchy Configuration +To use a Non-Hierarchy configuration, multiple `fields` are specified. All fields must have a `type` or `value` and NOT `hierarchy`. Facet types can be configured in the Searchspring Management Console. Here is an example of a Non-Hierarchy `FinderControllerConfig` object: + +```js +// src/index.js + +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + finder: [ + { + config: { + id: 'finder', + url: '/search', + fields: [ + { + field: 'custom_wheel_size', + label: 'Size' + }, + { + field: 'custom_wheel_width', + label: 'Width' + }, + { + field: 'custom_wheel_bolt_pattern', + label: 'Bolt Pattern' + }, + { + field: 'custom_color', + label: 'Color' + } + ] + }, + targeters: [ + { + selector: '#searchspring-finder', + component: async () => { + return (await import('./components/Finder/Finder')).Finder; + }, + }, + ], + }, + ], + }, +}); +``` + +Note: When using fields that are not of hierarchy type, `levels` are not required. Specifying `levels` will display a dropdown for each hierarchy level. Finders that use hierarchy fields will enforce selecting dropdowns in order by disabling the following dropdowns. If `levels` are not defined, a single dropdown will be displayed on the initial load. Each selection will dynamically append additional dropdowns until there are no more available selections. + + +## Component Example + +```js +// src/components/Finder/Finder.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { ControllerProvider } from '@searchspring/snap-preact-components'; + +export const Finder = observer((props) => { + const { controller } = props; + const { store } = controller; + const { selections, loading } = store; + const allAreSelected = selections.every((selection) => selection.selected); + + return selections?.length > 0 ? ( +
+
    + {selections.map((selection, i) => ( +
  • + + +
  • + ))} +
+
+ + +
+
+ ) : null +}); +``` \ No newline at end of file diff --git a/docs/INTEGRATION_FOREGROUND_FILTERS.md b/docs/SNAP_FOREGROUND_FILTERS.md similarity index 63% rename from docs/INTEGRATION_FOREGROUND_FILTERS.md rename to docs/SNAP_FOREGROUND_FILTERS.md index 71da1301b..40208f430 100644 --- a/docs/INTEGRATION_FOREGROUND_FILTERS.md +++ b/docs/SNAP_FOREGROUND_FILTERS.md @@ -1,15 +1,34 @@ -## Foreground Filters -***Note:*** **Foreground filters are only usable with a SearchController.** +# Foreground Filters -Foreground filters provide a way for pre-applying a filter on page load. The applied filter will be applied to the URL and can be removed as any other applied filter would. Foreground filtering is accomplished by setting the inital UrlManager state; this can be used for setting various states, but only filtering will be covered in this document. +> [!NOTE] +> Foreground filters are only usable with a SearchController + +Foreground filters provide a way for pre-applying a filter on page load. The applied filter will be applied to the URL and can be removed as any other applied filter would. Foreground filtering is accomplished by setting the initial UrlManager state; this can be used for setting various states, but only filtering will be covered in this document. + + +| Property | Type | Required | Default | Description | +|----------|------|----------|---------|-------------| +| `initial.settings` | Object | ➖ | - | Global configuration settings for initial state | +| `initial.settings.ignoreParameters` | string[] | ➖ | `['query', 'tag', 'oq', 'fallbackQuery']` | Parameters to ignore when determining whether to apply initial state | +| `initial.settings.useDefaultIgnoreParameters` | boolean | ➖ | `true` | Whether to use the default ignore parameters list | +| `parameters` | Object | ✔️ | - | UrlManager state parameters to set initially | +| `initial.parameters.filter` | Object | ➖ | - | Filter state configuration | +| `initial.parameters.sort` | Object | ➖ | - | Sort state configuration | +| `initial.parameters.page` | Object | ➖ | - | Page state configuration | +| `initial.parameters.pageSize` | Object | ➖ | - | Page size state configuration | +| `initial.parameters.[custom]` | Object | ➖ | - | Any custom UrlManager state parameter | +| `initial.parameters.[param].state` | Object | ✔️ | - | The actual state values to set for the parameter | +| `initial.parameters.[param].useGlobalIgnoreParameters` | boolean | ➖ | `true` | Whether to use global ignore parameters for this parameter | +| `initial.parameters.[param].action` | 'merge' \| 'set' | ➖ | `'merge'` | How to handle existing state values ('merge' = merge with existing, 'set' = replace completely) | +| `initial.parameters.[param].ignoreParameters` | string[] | ➖ | - | Individual ignore parameters for this specific parameter | In the simplified example below, a foreground filter is used to pre-apply a filter for the `on_sale` field. -```typescript +```js const config = { client: { globals: { - siteId: 'abc123', + siteId: 'REPLACE_WITH_YOUR_SITE_ID', }, }, controllers: { @@ -42,11 +61,11 @@ The `initial.parameters` object is keyed by `UrlManager` state parameters - such There is also an optional `ignoreParameter` param you can set on the `initial.settings` object, this allows for specifying additional UrlManager state parameters to be added to the ignore list. See example where the `initial.state` filter `on_sale:yes` will be set even if there are other `filter` params present in the UrlManager state. The default values in the `ignoreParameter` are `query`, `tag`, `oq` and `fallbackQuery`. This list is used to determine wether or not to apply the initial state provided - if the UrlManager state contains any states that are not being ignored, the initial state will not be applied. -```typescript +```js const config = { client: { globals: { - siteId: 'abc123', + siteId: 'REPLACE_WITH_YOUR_SITE_ID', }, }, controllers: { @@ -84,11 +103,11 @@ More configuration can be made within each `initial.parameter` object. It is pos Example using advanced configurations shown below: -```typescript +```js const config = { client: { globals: { - siteId: 'abc123', + siteId: 'REPLACE_WITH_YOUR_SITE_ID', }, }, controllers: { diff --git a/docs/SNAP_OVERVIEW.md b/docs/SNAP_OVERVIEW.md new file mode 100644 index 000000000..f3586fff5 --- /dev/null +++ b/docs/SNAP_OVERVIEW.md @@ -0,0 +1,99 @@ +# Overview + +Snap is built using the Model View Controller (MVC) pattern internally. When constructing an instance of the `Snap` class, the configuration object that is provided contains all the controllers that the project will use and where they will be rendered on the page. Each controller can be configured with various settings and custom functionality can be implemented via middleware to tie into the search lifecycle. + +Each controller contains a `controller.search()` method that is used to trigger a search to Searchspring API with parameters derived from the `controller.urlManager` state. A urlManager contains the state of the url (query and hash parameters), or in the case of Autocomplete, Recommendations, and Finder, is detached from the url and contains it's own state. + +Data that is returned from the API is then stored in each controller's `controller.store`. The data schema is unique to each controller type (Search, Autocomplete, Recommendations, Finder) + +A root level Preact component is rendered to the target selector(s) for each controller. The component will contain a reference to the controller via `props.controller`. Data from `props.controller.store` can then be rendered within the component and sub-components and will be reactive to changes in the store. + +Links attached to facet values, pagination, sort by, etc.. that are clicked will update the urlManager state and trigger a new search and re-render of the component. + + +Here is an example of the bare minimum configuration to create a search controller and render a root level `Content` component to the page. + +```js +// src/index.js +import { Snap } from '@searchspring/snap-preact'; + +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + }, + targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content/Content')).Content; + }, + }, + ], + }, + ], + }, +}); +``` + +The `Content` component is considered a root level component since it is being rendered onto the page using a targeter. + +To provide a reference to the controller via props to any subcomponents, we can utilize the `ControllerProvider` component from the `@searchspring/snap-preact-components` package. + +```jsx +// src/components/Content/Content.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { ControllerProvider } from '@searchspring/snap-preact-components'; +import { Results } from '../Results/Results'; + +export const Content = observer((props) => { + const { controller } = props; + + return controller.store.loaded ? ( + + + + ) : null; +}); +``` + +Then from any subcomponent such as `Results` in this example, the `withController` higher order component is used to access the controller via props. It should be placed before any other decorators/HOCs. + +Finally, the `observer` higher order component is used to make the component reactive to changes in the store. + +```jsx +// src/components/Results/Results.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { withController, InlineBanner, Result } from '@searchspring/snap-preact-components'; + +export const Results = withController(observer((props) => { + const { controller } = props; + + return ( +
    + {controller.store.results.map((result) => ( +
  • + {{ + banner: , + }[result.type] || } +
  • + ))} +
+ ); +})); +``` + +## Preact Component Library + +Snap provides an extensive [library of Preact components](https://searchspring.github.io/snap/preact-components) that can be used exclusively or in combination with custom components throughout your project. \ No newline at end of file diff --git a/docs/SNAP_RECOMMENDATIONS.md b/docs/SNAP_RECOMMENDATIONS.md new file mode 100644 index 000000000..6c52c3b36 --- /dev/null +++ b/docs/SNAP_RECOMMENDATIONS.md @@ -0,0 +1,190 @@ +# Recommendations + +While it is possible to construct recommendation controllers via the Snap configuration, it is recommended to utilize the [RecommendationInstantiator config](https://searchspring.github.io/snap/reference-snap-preact-instantiators#recommendationinstantiatorconfig) instead for integration of recommendations. The `RecommendationInstantiator` will only create recommendation controllers if the page contains recommendation profiles. + +There are three types of recommendations that Searchspring offers: + +- Product Recommendations / Personalized Recommendations +- Bundle Recommendations +- Email Recommendations (documentation coming soon) + + +This guide will cover usage of the Default templates that are available in the Searchspring Management Console. If you are looking to create a custom template, please refer to the [Custom Templates reference](https://searchspring.github.io/snap/reference-custom-recommendation-templates) + +Changes to the recommendation integration scripts were made in Snap `v0.60.0`. Legacy Recommendation Integrations docs can still be found [`here`](https://searchspring.github.io/snap/snap-recommendations-legacy) + + +## More Information +See [Recommendations Controller reference](https://searchspring.github.io/snap/reference-controller-recommendation) for more information on all available context variables that can be used to configure recommendations. The reference also contains more information regarding batching, ordering, deduplication, filtering, examples, and more. + +## Product Recommendations / Personalized Recommendations + +The Searchspring Management Console contains a `Default` template available for standard profiles (non-bundle) that does not require the use of the Snapfu CLI to create a custom template. To use the `Default` template, the following instantiator config should be added to your `snap-preact` config. + +```js +// src/index.js + +import { Snap } from '@searchspring/snap-preact'; + +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + instantiators: { + recommendation: { + components: { + Default: async () => { + return (await import('./components/Recommendations/Recs')).Recs; + }, + }, + }, + }, +}); +``` + +Note that the component is not required to be named `Default`, however `instantiators.recommendation.component` must contain the `Default` key as seen in the example above. + + +This example assumes a `recently-viewed` profile has been configured in the Searchspring Management Console (SMC) with the `Default` template selected. The profile (specified via the `tag` property) will render inside the `.ss__recs__recently-viewed` element below the script block. While the target element can be placed anywhere on the page, it's recommended to group elements with their corresponding script blocks for easier integration management. The component configuration is handled within the [`RecommendationInstantiator`](https://searchspring.github.io/snap/reference-snap-preact-instantiators). + +```html + + +
+``` + + +### Recommendation Component +In this example, the `Recs` component is a wrapper around the `Recommendation` component from the `@searchspring/snap-preact-components` package. See [Components Preact > Recommendation](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Forganisms-recommendation--default) for more details. + +```jsx +// components/Recommendations/Recs.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { useEffect } from 'preact/hooks'; +import { Recommendation } from '@searchspring/snap-preact-components'; + +import './Recs.scss'; + +export const Recs = observer((props) => { + + const controller = props.controller; + const store = controller?.store; + + useEffect(() => { + if (!controller.store.loaded && !controller.store.loading) { + controller.search(); + } + }, []); + + const parameters = store?.profile?.display?.templateParameters; + + return ( + store.results.length > 0 && ( + + ) + ); +}); +``` + + +## Bundle Recommendations +The Searchspring Management Console also contains a `Bundle` template available for bundle profiles, this template does not require the use of the Snapfu CLI to create a custom template. To use the `Bundle` template, another component mapping will need to be added to your instantiator config. + +```js +// src/index.js + +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + instantiators: { + recommendation: { + components: { + Default: async () => { + return (await import('./components/Recommendations/Recs')).Recs; + }, + Bundle: async () => { + return (await import('./components/Recommendations/Bundled')).Bundled; + }, + }, + }, + }, +}); +``` + +Note that the component is not required to be named `Bundle`, however `instantiators.recommendation.component` must contain the `Bundle` key as seen in the example above. + +This example assumes a `bundle` profile has been configured in the Searchspring Management Console (SMC) with the `Bundle` template selected. The profile (specified via the `tag` property) will render inside the `.ss__recs__bundle` element below the script block. While the target element can be placed anywhere on the page, it's recommended to group elements with their corresponding script blocks for easier integration management. The component configuration is handled within the [`RecommendationInstantiator`](https://searchspring.github.io/snap/reference-snap-preact-instantiators). + +The `products` global context variable is required for bundle recommendations to specify the sku of the currently viewed product. + +```html + + +
+``` + +### Bundle Component +The example `Bundled` component below uses the `RecommendationBundle` component imported from the snap component library. See [Components Preact > RecommendationBundle](https://searchspring.github.io/snap/preact-components?params=%3Fpath%3D%2Fstory%2Forganisms-recommendationbundle--default) for more details. + +```jsx +// components/Recommendations/Bundled.jsx + +import { h } from 'preact'; +import { useEffect } from 'preact/hooks'; +import { observer } from 'mobx-react'; +import { RecommendationBundle } from '@searchspring/snap-preact-components'; + +import './Bundled.scss'; + +const addToCart = (data) => { + // handle adding products to the cart +}; + +export const Bundled = observer((props) => { + const controller = props.controller; + const store = controller?.store; + + useEffect(() => { + if (!controller.store.loaded && !controller.store.loading) { + controller.search(); + } + }, []); + + const parameters = store?.profile?.display?.templateParameters; + + return store.results.length > 0 && addToCart(data)} title={parameters?.title} />; +}); +``` + + + \ No newline at end of file diff --git a/docs/INTEGRATION_RECOMMENDATIONS.md b/docs/SNAP_RECOMMENDATIONS_INTEGRATION.md similarity index 74% rename from docs/INTEGRATION_RECOMMENDATIONS.md rename to docs/SNAP_RECOMMENDATIONS_INTEGRATION.md index f968dd7aa..1555da18d 100644 --- a/docs/INTEGRATION_RECOMMENDATIONS.md +++ b/docs/SNAP_RECOMMENDATIONS_INTEGRATION.md @@ -1,35 +1,38 @@ -## Recommendations Integration -It is recommended to utilize the [`RecommendationInstantiator`](https://github.com/searchspring/snap/blob/main/packages/snap-preact/src/Instantiators/README.md) for integration of product recommendations (standard when using Snap object). +# Recommendations Integration -Changes to the recommendation integration scripts were made in Snap `v0.60.0`. Legacy Recommmendation Integrations docs can still be found [`here`](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_LEGACY_RECOMMENDATIONS.md) +Changes to the recommendation integration scripts were made in Snap `v0.60.0`. Legacy Recommendation Integrations docs can still be found [`here`](https://searchspring.github.io/snap/snap-recommendations-legacy) -Recommendations script blocks can be placed anywhere on the page and will automatically target and batch requests for all profiles specified in the block (requires the `bundle.js` script also). Batching profiles is important for deduplication of recommended products (see more below). +## Prerequisites -The block below uses the `recently-viewed` profile which would typically be setup to display the last products viewed by the shopper. Profiles must be setup in the Searchspring Management Console (SMC) and have associated Snap templates selected. +Profiles must be setup in the Searchspring Management Console (SMC) and have associated Snap templates selected. The template selected contains a `component` that will be used to render the recommendations profile. This component must be configured in the Snap [RecommendationInstantiator config](https://searchspring.github.io/snap/reference-snap-preact-instantiators#recommendationinstantiatorconfig) + +## Installation + +Recommendations script blocks can be placed anywhere on the page and will automatically target and batch requests for all profiles specified in the block. + +- If this is the **first profile** you are adding to the storefront, you will need to place the script block in a common location that is likely to be present on all pages that contain recommendations. For example, within the `` tag. + +- Or **Additional profiles** being added should be appended to the existing script block in the `profiles` array and can be conditionally rendered (via templating logic) based on the page type. However if the page does not contain any elements matching any of the profile's `selector`, the profile will also not be rendered so alternatively the `div` element can be conditionally rendered instead. We do not recommend creating a new script block for each profile, as this will result in multiple API requests and products will not be deduplicated across profiles. Batching profiles is important for deduplication of recommended products across profiles on the same page (see [Deduping](#deduping)). ```html - -
``` -The `RecommendationInstantiator` will look for these script blocks on the page and attempt to inject components based on the `selector` specified in each profile. In the example above, the profile specified (via tag) is the `recently-viewed` profile, and is set to render inside the `.ss__recs__recently-viewed` element just below the script block. The targeted element could exist anywhere on the page - but it is recommended to group elements with script blocks whenever possible (for easy integration identification). The component to render into the targeted `selector` is setup within the `RecommendationInstantiator` configuration. The targeted element should be given a `min-height` inline style to prevent cumulative layout shift. +```html +
+``` +In this example the `recently-viewed` profile `tag` is set to render inside the `.ss__recs__recently-viewed` element. ## Recommendation Context Variables Context variables are set within the script blocks and can be used to set either global or per profile (profile specific) functionality. Variables are used to alter the results displayed by our recommendations and may be required depending on the profile placements in use. @@ -41,7 +44,7 @@ Context variables are set within the script blocks and can be used to set either | blockedItems | array of strings | all | SKU values to identify which products to exclude from the response | | | filters | array of filters | all | optional recommendation filters to apply to ALL profiles in the batch | | | cart | array (or function that returns an array) of current cart skus | all | optional method of setting cart contents | | -| shopper.id | logged in user unique identifier | all | required for personalization functionallity if not provided to the bundle (global) context | | +| shopper.id | logged in user unique identifier | all | required for personalization functionality if not provided to the bundle (global) context | | ### Profile Specific Variables @@ -57,8 +60,8 @@ Context variables are set within the script blocks and can be used to set either | options.dedupe | boolean (default: `true`) | all | dedupe products across all profiles in the batch | | | options.query | string | dynamic custom | query to search | | | options.filters | array of filters | all | optional recommendation filters | | -| options.realtime | boolean | all | optional update recommendations if cart contents change (requires [cart attribute tracking](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_TRACKING.md)) | | -| options.limit | number (default: 20, max: 20) | all | optional maximum number of results to display, can also be set globally [via config globals](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Recommendation) | | +| options.realtime | boolean | all | optional update recommendations if cart contents change (requires [cart attribute tracking](https://github.com/searchspring/snap/tree/main/docs/SNAP_TRACKING.md#cart-attribute-tracking)) | | +| options.limit | number (default: 20, max: 20) | all | optional maximum number of results to display, can also be set globally via RecommendationController config globals | | ## Batching and Ordering @@ -113,7 +116,7 @@ Here's an example that demonstrates deduping: ## Additional Examples -The examples below assume the `similar` profile has been setup in the Searchspring Management Console (SMC), and that a Snap `bundle.js` script exists on the page and has been configured with a `RecommendationInstantiator`. +The examples below assume the `similar` profile has been setup in the Searchspring Management Console (SMC), and that a Snap `bundle.js` script exists on the page and has been configured with a [`RecommendationInstantiator`](https://searchspring.github.io/snap/reference-snap-preact-instantiators) A typical "similar" profile displays products similar to the product passed in via the `products` global context variable. @@ -196,4 +199,4 @@ The example below filters the recommendations for products matching field `color } ]; -``` +``` \ No newline at end of file diff --git a/docs/INTEGRATION_LEGACY_RECOMMENDATIONS.md b/docs/SNAP_RECOMMENDATIONS_LEGACY.md similarity index 85% rename from docs/INTEGRATION_LEGACY_RECOMMENDATIONS.md rename to docs/SNAP_RECOMMENDATIONS_LEGACY.md index 31a3e63c9..36cd200d6 100644 --- a/docs/INTEGRATION_LEGACY_RECOMMENDATIONS.md +++ b/docs/SNAP_RECOMMENDATIONS_LEGACY.md @@ -1,9 +1,9 @@ ## Recommendations Integration (Legacy) -For integrations using Snap `v0.60.0` and newer, please reference the updated [`integration docs`](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_RECOMMENDATIONS.md). +For integrations using Snap `v0.60.0` and newer, please reference the updated [`integration docs`](https://searchspring.github.io/snap/snap-recommendations). -It is recommended to utilize the [`RecommendationInstantiator`](https://github.com/searchspring/snap/blob/main/packages/snap-preact/src/Instantiators/README.md) for integration of product recommendations. This method allows recommendations to be placed anywhere on the site with a single script block (requires the `bundle.js` script also). +It is recommended to utilize the [`RecommendationInstantiator`](https://searchspring.github.io/snap/reference-snap-preact-instantiators) for integration of product recommendations. This method allows recommendations to be placed anywhere on the site with a single script block (requires the `bundle.js` script also). ```html ``` -The `RecommendationInstantiator` will look for these elements on the page and attempt to inject components based on the `profile` specified in the script attribute. In the example above, the profile specified is the `recently-viewed` profile, and would typically be setup to display the last products viewed by the shopper. These profiles must be setup in the Searchspring Management Console (SMC). +The [`RecommendationInstantiator`](https://searchspring.github.io/snap/reference-snap-preact-instantiators) will look for these elements on the page and attempt to inject components based on the `profile` specified in the script attribute. In the example above, the profile specified is the `recently-viewed` profile, and would typically be setup to display the last products viewed by the shopper. These profiles must be setup in the Searchspring Management Console (SMC). ## Recommendation Context Variables @@ -23,19 +23,19 @@ Profile configurations are applied to recommendation via script context variable | cart | array (or function that returns an array) of current cart skus | all | optional method of setting cart contents (global) | | blockedItems | array of strings | all | SKU values to identify which products to exclude from the response (global) | | filters | array of filters | all | optional recommendation filters (global) | -| shopper.id | logged in user unique identifier | all | required for personalization functionallity if not provided to the bundle context (global) | +| shopper.id | logged in user unique identifier | all | required for personalization functionality if not provided to the bundle context (global) | | options.siteId | siteId overwrite | all | optional siteId overwrite (will force a new batch) | | options.categories | array of category path strings | all | optional category identifiers used in category trending recommendation profiles | | options.brands | array of brand strings | all | optional brand identifiers used in brand trending recommendation profiles | | options.branch | template branch overwrite | all | optional branch overwrite for recommendations template (advanced usage) | | options.filters | array of filters | all | optional recommendation filters | | options.query | string | all | query to search | -| options.realtime | boolean | all | optional update recommendations if cart contents change (requires [cart attribute tracking](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_TRACKING.md)) | +| options.realtime | boolean | all | optional update recommendations if cart contents change (requires [cart attribute tracking](https://searchspring.github.io/snap/snap-tracking#cart-attribute-tracking)) | | options.blockedItems | array of strings | all | SKU values to identify which products to exclude from the response | -| options.batched | boolean (default: `true`)| all | only applies to recommendation context, optional disable profile from being batched in a single request, can also be set globally [via config](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Recommendation) | +| options.batched | boolean (default: `true`)| all | only applies to recommendation context, optional disable profile from being batched in a single request, can also be set globally [via config](https://searchspring.github.io/snap/reference-controller-recommendation#recommendationcontrollerconfig) | | options.dedupe | boolean (default: `true`) | all | specify wether or not the profile should deduplicate products when in a batch | | options.order | number | all | optional order number for recommendation params to be added to the batched request. Profiles that do not specify an order will be placed at the end, in the occurrence they appear in the DOM. -| options.limit | number (default: 20, max: 20) | all | optional maximum number of results to display, can also be set globally [via config globals](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Recommendation) | +| options.limit | number (default: 20, max: 20) | all | optional maximum number of results to display, can also be set globally [via config globals](https://searchspring.github.io/snap/reference-controller-recommendation#recommendationcontrollerconfig) | ## Batching and Ordering By default, recommendation profile results are fetched in the same API request (batch), this is done in an effort to prevent the display of duplicate products across multiple profiles. The order of the profiles in the DOM determines the priority of results for de-duplication (best recommendations). If you wish to change the order, an `order` value can be provided (lowest value has highest priority). For some profiles (like product bundles) it is important that they receive the best suggested products prior to de-duplication, for these, the `order` should be set manually so that de-duplication does not occur. @@ -91,7 +91,7 @@ Alternatively, a profile can be placed in it's own batch via the `batched: false ## Additional Examples -The examples below assume that profiles used have been setup in the Searchspring Management Console (SMC), and that a Snap `bundle.js` script exists on the page and has been configured with a `RecommendationInstantiator`. +The examples below assume that profiles used have been setup in the Searchspring Management Console (SMC), and that a Snap `bundle.js` script exists on the page and has been configured with a [`RecommendationInstantiator`](https://searchspring.github.io/snap/reference-snap-preact-instantiators). A typical "similar" profile that would display products similar to the product passed in via the `product` context variable. diff --git a/docs/SNAP_SEARCH.md b/docs/SNAP_SEARCH.md new file mode 100644 index 000000000..e4903baa0 --- /dev/null +++ b/docs/SNAP_SEARCH.md @@ -0,0 +1,507 @@ +# Search + +To set up Search using Snap, we'll need to define a search controller in our Snap configuration. See [SearchController reference](https://searchspring.github.io/snap/reference-controller-search) for all available configuration options. + + +```js +// src/index.js + +import { Snap } from '@searchspring/snap-preact'; + +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + }, + targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content/Content')).Content; + }, + }, + ], + }, + ], + }, +}); +``` + +## Category Pages / Background Filters +Optionally, apply filters from the page's content to the SearchControllerConfig `globals.filters` property. The controller globals are similar to the client globals in that all search requests will include the parameters specified. This can be used to configure category/brand pages, or other special filtering to apply to the current page's search requests. + +For example, if a global variable `snapConfig` exists on the page (must be defined prior to our Snap script): + +```html + +``` + +```js +// src/index.js + +import { Snap } from '@searchspring/snap-preact'; + +const backgroundFilters = []; +if (snapConfig?.category) { + backgroundFilters.push({ + type: 'value', + background: true, + field: 'categories_hierarchy', + value: snapConfig.category.value, + }); +} +const snap = new Snap({ + client: { + globals: { + siteId: 'REPLACE_WITH_YOUR_SITE_ID', + }, + }, + controllers: { + search: [ + { + config: { + id: 'search', + globals: { + filters: backgroundFilters, + }, + }, + targeters: [ + { + selector: '#searchspring-content', + component: async () => { + return (await import('./components/Content/Content')).Content; + }, + }, + ], + }, + ], + }, +}); +``` + +## Search Store + +This section covers the properties available on the Search Store via a Search Controller with examples of how to implement common custom components. Alternatively, equivalent and additional components are available in the `@searchspring/snap-preact-components` package. See [Preact Component Library](https://searchspring.github.io/snap/preact-components) for all available components and their usage. + + +### SearchController.store.merchandising + +The `merchandising` property contains merchandising redirects and banner content. It is recommended to utilize the `` component from `@searchspring/snap-preact-components` to display the various merchandising banners. + +The available banner types include: `header`, `banner`, `footer`, `left`, `inline` + +For inline banners, the `` component should be used instead. An example of this usage can be found in the [store.results](https://searchspring.github.io/snap/snap-search#searchcontrollerstoreresults) section below. + +```jsx +// src/components/Content/Content.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { ControllerProvider, Banner, Pagination } from '@searchspring/snap-preact-components'; +import { Results } from '../Results/Results'; +import { NoResults } from '../NoResults/NoResults'; +import { SearchHeader } from '../SearchHeader/SearchHeader'; + +export const Content = observer((props) => { + const { controller } = props; + + return controller.store.loaded ? ( + +
+ + + + { + pagination.totalResults > 0 + ? () : + () + } + + +
+
+ ) : null; +}); +``` + +### SearchController.store.search + +The `search` property contains information about the current query, typically displayed above results and used in combination with the `store.pagination` data. + +```jsx +// src/components/SearchHeader/SearchHeader.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { withController } from '@searchspring/snap-preact-components'; + +export const SearchHeader = withController(observer((props) => { + const { controller } = props; + const { store } = controller; + const { pagination, search } = store; + const originalQuery = search.originalQuery; + + return ( + store.loaded && ( +
+ {pagination.totalResults ? ( +

+ {`Showing `} + {pagination.multiplePages && {` ${pagination.begin} - ${pagination.end} of `}} + {pagination.totalResults} + {` result${pagination.totalResults == 1 ? '' : 's'}`} + {search?.query && ( + + {` for `} + "{search.query.string}" + + )} +

+ ) : ( + pagination.totalResults === 0 && ( +

+ {search?.query ? ( + + No results for "{search.query.string}" found. + + ) : ( + No results found. + )} +

+ ) + )} + + {originalQuery && ( +
+ Search instead for "{originalQuery.string}" +
+ )} +
+ ) + ); +})); +``` + +### SearchController.store.pagination + +The `pagination` property is not only used for information about the current query, but also contains everything needed for handling pagination of a query that yields multiple pages. Invoking the `getPages` method will retrieve the specified number of page objects. For more about the pagination store, checkout the [Search Controller reference](https://searchspring.github.io/snap/reference-controller-search). + +```jsx +// src/components/Pagination/Pagination.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { withController } from '@searchspring/snap-preact-components'; + +export const Pagination = withController(observer((props) => { + const { controller } = props; + const { store } = props.controller; + const { pagination } = store; + + const MINIMUM_PAGES = 5; + const pages = pagination.getPages(MINIMUM_PAGES); + + return ( +
+ {pagination.previous && ( + + + Prev + + + )} + + {pages.map((page) => ( + + {page.number} + + ))} + + {pagination.next && ( + + + Next + + + )} +
+ ) +})); +``` + +### SearchController.store.sorting + +The `sorting` property contains sorting options applicable to the current query. Typically used to render a ` { + const selectedOption = sorting.options.filter((option) => option.value == e.target.value).pop(); + selectedOption && selectedOption.url.go(); + }} + > + {sorting.options.map((option) => ( + + ))} + +
+ ) : null; +})); +``` + +### SearchController.store.results + +The `results` property contains an array of result objects for the current page. + +Each result object contains the following notable properties: + +`result.type` will be 'product' or 'banner' (inline banner) + +`result.mappings.core` core attributes configured in the [Searchspring Management Console](https://manage.searchspring.net/) + +`result.attributes` remaining attributes + +`result.mask` provides a way to temporarily modify result data without changing the underlying store data. This can be used in combination with the `result.display` for simple UI effects like showing alternate product images on hover, or more complex interactions like updating displayed prices when selecting different product variants. + +`result.mask.merge` a function to merge new mask data with the current display state. This function accepts a single object as its only parameter. + +`result.mask.set` a function to set the mask data. Overwrites the current mask data. This function accepts a single object as its only parameter. + +`result.mask.clear` a function to clear the mask data, reverting to the original display state. + +`result.display` an object used for display in result components. Containing the currently set display state from the `result.mask` combined with the underlying core data for the result. + +`result.variants` contains information about product variants like size and color options, as well as the variant selections data. (requires variants to be enabled and configured) For more variant integration information, see [Variants Reference](https://github.com/searchspring/snap/tree/main/docs/REFERENCE_VARIANTS.md) + +`result.custom` an empty object that is not modified by core Snap packages. This is available for you to modify and store custom data to be rendered. See [`custom` property](https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx/src/Abstract) + +Note: if you will be creating a custom Result component, the `withTracking` hook is required to capture product impression and click analytics. See [Tracking](https://github.com/searchspring/snap/tree/main/docs/SNAP_TRACKING.md#impressions) for more information. + +```jsx +// src/components/Results/Results.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { withController, withTracking, InlineBanner, Price } from '@searchspring/snap-preact-components'; + +export const Results = withController(observer((props) => { + const { controller } = props; + const { store } = controller; + const { results } = store; + + return ( +
    + {results.map((result) => ( +
  • + {{ + banner: , + }[result.type] || } +
  • + ))} +
+ ) +})); + +const Result = withController(withTracking(observer((props) => { + const { trackingRef, controller, result } = props; + const { core } = result.mappings; + + return ( +
+ + { core.name } + + + +
+ ) +}))); +``` + +### SearchController.store.facets + +The `facets` property contains an array of facet objects for the current query. + +Each result object contains the following notable properties: + +`facet.collapsed` facet collapse state. Facets can be configured to start collapsed by default in the [Searchspring Management Console](https://manage.searchspring.net/) + +`facet.toggleCollapse` a method that toggles the collapse state for this facet + +`facet.clear` a method to remove the facet if it is currently active + +`facet.label` the facet label to display (ie. Price, Size, Brand) + +`facet.field` the raw facet field name + +`facet.display` the facet display type - used to conditionally render different facet components. Available display types: `list` (default), `grid`, `palette`, `hierarchy`, `slider`. Facet display types can be configured in the [Searchspring Management Console](https://manage.searchspring.net/) + +The example below displays a custom `FacetOptionsList` component for facets with a display type of `list`. + +The `@searchspring/snap-preact-components` component library includes the following components that can be imported or used as a reference: `FacetListOptions`, `FacetGridOptions`, `FacetPaletteOptions`, `FacetHierarchyOptions`, `FacetSlider` + +`facet.type` the facet type - Available facet types: `range`, `value`, `range-buckets`. + +Facets that contain a `type` value of `range` will not contain any `values` as this is typically used as a Slider. Instead, the facet will include `range.low`, `range.high`, `active.low`, and `active.high` properties. + +Facets with a `type` value of `value` or `range-buckets` will contain the following properties: + +`facet.search.input` facet search within - setting this will dynamically filter the facet `values` array to only include values that match the `facet.search.input` substring + +`facet.overflow.setLimit` method to set the number of values to display before overflow occurs + +`facet.overflow.toggle` method to toggle overflow of a facet, typically invoked `onClick` event of a facet 'show more' button + +`facet.refinedValues` facet values that have been limitied if any overflow or search within is active; this should be used to render facet values from components + +`facet.values` original facet values - it is not recommended to directly render facet values using this in your components - `facet.refinedValues` should be used instead - however, if you are using an `afterStore` event to reference facet values, `facet.values` should be used + +```jsx +// src/components/Facets/Facets.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { withController, SearchInput, FacetGridOptions, FacetPaletteOptions, FacetOptionsHierarchy, FacetSlider } from '@searchspring/snap-preact-components'; + +export const Facets = withController(observer((props) => { + const { controller } = props; + const { store } = controller; + const { facets } = store; + + return facets.length !== 0 ? ( +
+ {facets.map((facet) => ( + + ))} +
+ ) : null; +})); + +const Facet = withController(observer((props) => { + const { facet } = props; + + return facets.length !== 0 ? ( +
+
{ + facet.toggleCollapse() + }} + className={`ss__facet__header ${facet.collapsed ? 'ss__facet__header--collapsed' : 'ss__facet__header--expanded'}`}> + {facet.label} +
+ {['list', 'grid', 'palette'].includes(facet.display) && ( + facet.search.input = e.target.value} placeholder={`Search ${facet.label}`} /> + )} +
+ {{ + grid: , + palette: , + hierarchy: , + slider: , + }[facet.display] || } +
+
+ ) : null; +})); + +// custom FacetOptionsList component instead of importing from @searchspring/snap-preact-components +const FacetOptionsList = withController(observer((props) => { + const { facet } = props; + const values = facet.refinedValues; + + return ( + + ) +})); +``` + + +### SearchController.store.filters + +The `filters` property contains an array of filters that are currently applied to the query. + +Typically used to display a filter summary with options to remove filters. + +```jsx +// src/components/FilterSummary/FilterSummary.jsx + +import { h } from 'preact'; +import { observer } from 'mobx-react'; +import { withController } from '@searchspring/snap-preact-components'; + +export const FilterSummary = withController(observer((props) => { + const { controller } = props; + const { store } = controller; + const { filters } = store; + + return filters.length !== 0 ? ( + + ) : null; +})); +``` + diff --git a/docs/SNAP_SETUP.md b/docs/SNAP_SETUP.md new file mode 100644 index 000000000..8c7b504f0 --- /dev/null +++ b/docs/SNAP_SETUP.md @@ -0,0 +1,92 @@ +# Setup + + +To create a new project, we recommend using the [Snapfu CLI](https://www.npmjs.com/package/snapfu) to initialize a new project from a preconfigured scaffold. + +Snapfu is a CLI interface for creating a Snap project from a template. You do not need to utilize Snapfu to develop using Snap, but it does simplify the process and provides additional features. + + +## Install + +Note: We recommend using Node v20 or higher and npm v10 or higher. + +```sh +npm install -g snapfu +``` + + +## Initialize new project + +This will create a new project with a git repo from a list of available scaffolds. The `[projectname]` argument is optional and will create a directory with the given name. Otherwise the project will be created in the current directory. + +During this you will also be prompted for your Site ID and secret key, which you find in the [Searchspring Management Console](https://manage.searchspring.net) + +```sh +snapfu init [projectname] +cd [projectname] && npm install +``` + + +## Development + +```sh +npm run dev +``` + +This will start the local development server and serve the project build files and the contents of the `public` directory. + +| File/Resource | URL | +|------------------------------|----------------------------------------------------------| +| public/index.html | [https://localhost:3333/](https://localhost:3333/) | +| public/mockup.html (or other files) | [https://localhost:3333/mockup.html](https://localhost:3333/mockup.html) | +| main bundle | [https://localhost:3333/bundle.js](https://localhost:3333/bundle.js) | +| universal bundle | [https://localhost:3333/universal.bundle.js](https://localhost:3333/universal.bundle.js) | + +Note: The local server uses a self-signed certificate for HTTPS and you may be prompted to continue. + + +There are two common ways to develop a project, using a [local mockup file](https://searchspring.github.io/snap/setup#local-mockup-file) and leveraging the [Snapfu Chrome extension](https://searchspring.github.io/snap/setup#snapfu-chrome-extension). + +## Local mockup file + +The preconfigured scaffold includes a `public/index.html` or `public/mockup.html` mockup file. These pages need to include a link to the local bundle file `bundle.js` in order for the local development server to serve the Snap bundle. + +A minimal mockup file (shown below) contains only the necessary script and target elements to get components rendered on the page. More complete mockups would normally contain a copy of a storefront DOM structure and resources to inherit styles and fonts. + +```html + + + + + Snap + + +
+
+
+ + + + +``` + + +## Snapfu Chrome Extension + +The Snapfu Chrome Extension is a Chrome extension that you can install manually via chrome developer mode. The extension allows you to inject the development `bundle.js` into a live storefront page. This is useful for previewing changes on a live domain. + +Visit the website you are developing for, then click the extension to enable it and set the mode to `local`, then press `Save`. + +The page will reload with the local development bundle [https://localhost:3333/bundle.js](https://localhost:3333/bundle.js) injected into the current website. While `npm run dev` is running, the page will automatically reload upon saving any code modifications. + +For more information, see the [Snapfu Chrome Extension](https://github.com/searchspring/snapfu-extension) repository. + + + +## Other Development Methods + +Other possible but less desirable methods of serving the local development server while previewing changes on a live domain include: + +- Browser local override modifying script src, typically requires an override per page +- Alternative development domain (e.g. `https://dev.mysite.com`) or unpublished storefront theme with script src pointing to `https://localhost:3333/bundle.js` or a ngrok tunnel + diff --git a/docs/INTEGRATION_TRACKING.md b/docs/SNAP_TRACKING.md similarity index 81% rename from docs/INTEGRATION_TRACKING.md rename to docs/SNAP_TRACKING.md index 4e82e6afd..1eea476f4 100644 --- a/docs/INTEGRATION_TRACKING.md +++ b/docs/SNAP_TRACKING.md @@ -1,4 +1,4 @@ -## Tracking +# Tracking To ensure accurate tracking of events used for reporting, the following tracking events should be implemented across Search, Category, Autocomplete and Recommendations result components. @@ -102,7 +102,8 @@ const Results = withController((props) => { ## Events invoked outside of the integration code -**Note**: if using Shopify and you have installed Searchspring's Shopify Pixel Tracking extension, the following events will be tracked by the extension and integrating these events is not required. +> [!NOTE] +> If using Shopify and you have installed Searchspring's Shopify Pixel Tracking extension, the following events will be tracked by the extension and integrating these events is not required. Some reports rely on beacon data that is tracked outside of the main integration code. To ensure accurate reporting, these tracking events should be implemented on the relevant pages. Note that these tracking methods require the `bundle.js` script to be present on any page where they are used. @@ -116,19 +117,19 @@ Identifies the logged-in user. Should be invoked if a user is logged into their - (Recommended) using the `shopper.id` context variable on the main `/bundle.js` script. ```html - ``` - (Alternative) using the global `searchspring.tracker.track.shopper.login` method. -```typescript +```js searchspring.tracker.events.shopper.login({ data: { - id: 'snapdev' + id: '[REPLACE WITH LOGGED IN SHOPPER ID]' } }); ``` @@ -139,7 +140,7 @@ Defines the currency of the shopper. This is not required if the storefront is c - (Recommended) using the `currency` context variable on the main `/bundle.js` script. ```html - + Snap Documentation - - - + + +
- - + diff --git a/lerna.json b/lerna.json index a5d1a93c1..4d62c412f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.10.1", + "version": "1.11.0", "packages": ["packages/*"], "npmClient": "npm", "command": { diff --git a/package-lock.json b/package-lock.json index 34f59232e..6873e8a56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "packages/snapps/*" ], "devDependencies": { + "@athoscommerce/snapi-types": "^1.0.4", "@searchspring/prettier": "1.0.2", - "@searchspring/snapi-types": "^0.1.40", "@testing-library/jest-dom": "6.2.0", "@testing-library/preact": "^3.2.4", "@testing-library/user-event": "^14.5.2", @@ -59,6 +59,12 @@ "node": ">=6.0.0" } }, + "node_modules/@athoscommerce/snapi-types": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@athoscommerce/snapi-types/-/snapi-types-1.0.4.tgz", + "integrity": "sha512-e4lKuoIUcVIe7zma/cBTQ69m/bxyViiJXYdm/pBUni1Et3X8EHOtWwTocYh2HcDwfonWouFKnGGqPaBalvqJQQ==", + "dev": true + }, "node_modules/@aw-web-design/x-default-browser": { "version": "1.4.126", "resolved": "https://registry.npmjs.org/@aw-web-design/x-default-browser/-/x-default-browser-1.4.126.tgz", @@ -2364,6 +2370,23 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "license": "MIT" }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -2620,6 +2643,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", @@ -2636,6 +2676,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/openbsd-x64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", @@ -2652,6 +2709,23 @@ "node": ">=12" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", @@ -5242,9 +5316,9 @@ } }, "node_modules/@searchspring/beacon": { - "version": "0.0.40", - "resolved": "https://registry.npmjs.org/@searchspring/beacon/-/beacon-0.0.40.tgz", - "integrity": "sha512-cyr7YFYI29zwnG7zastdRLjd9oRKj+Rx98S83LSh48L96pUh7bDRSseOV7oaoRDRVcH9hH3fOiJRE6O+SeYHAA==", + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@searchspring/beacon/-/beacon-0.0.44.tgz", + "integrity": "sha512-Y4qdlLSueIbKOT+gVtbiaFLHfXbRAryKh/J99GQqqrC0E//CSUUBM+ps/E2HiNj+p60HpuP0TZg6cOq3oQbmJg==", "license": "MIT", "dependencies": { "uuid": "9.0.1" @@ -5314,12 +5388,6 @@ "resolved": "packages/snap-url-manager", "link": true }, - "node_modules/@searchspring/snapi-types": { - "version": "0.1.40", - "resolved": "https://registry.npmjs.org/@searchspring/snapi-types/-/snapi-types-0.1.40.tgz", - "integrity": "sha512-4k03rN819sFqpJYgIAjStxFn1qaft+A/Ls+LU5J4Qbehn2PoIrKcBoZ+XkvqqLc6pcWK++zcoQ/LyG3zSRNwUA==", - "dev": true - }, "node_modules/@sentry/core": { "version": "6.19.7", "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.19.7.tgz", @@ -17255,6 +17323,452 @@ "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/esbuild-loader": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.4.2.tgz", + "integrity": "sha512-8LdoT9sC7fzfvhxhsIAiWhzLJr9yT3ggmckXxsgvM07wgrRxhuT98XhLn3E7VczU5W5AFsPKv9DdWcZIubbWkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.1", + "get-tsconfig": "^4.10.1", + "loader-utils": "^2.0.4", + "webpack-sources": "^1.4.3" + }, + "funding": { + "url": "https://github.com/privatenumber/esbuild-loader?sponsor=1" + }, + "peerDependencies": { + "webpack": "^4.40.0 || ^5.0.0" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild-loader/node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/esbuild-loader/node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, "node_modules/esbuild-plugin-alias": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/esbuild-plugin-alias/-/esbuild-plugin-alias-0.2.1.tgz", @@ -18717,6 +19231,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/get-uri": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", @@ -27138,6 +27665,16 @@ "node": ">=8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve.exports": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", @@ -28116,6 +28653,13 @@ "node": ">=4" } }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -31272,73 +31816,73 @@ }, "packages/snap-client": { "name": "@searchspring/snap-client", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { - "@searchspring/snap-toolbox": "1.10.1", + "@searchspring/snap-toolbox": "1.11.0", "deepmerge": "4.3.1" } }, "packages/snap-controller": { "name": "@searchspring/snap-controller", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { - "@searchspring/snap-toolbox": "1.10.1", + "@searchspring/snap-toolbox": "1.11.0", "css.escape": "1.5.1", "deepmerge": "4.3.1" }, "devDependencies": { - "@searchspring/snap-client": "1.10.1", - "@searchspring/snap-event-manager": "1.10.1", - "@searchspring/snap-logger": "1.10.1", - "@searchspring/snap-profiler": "1.10.1", - "@searchspring/snap-store-mobx": "1.10.1", - "@searchspring/snap-tracker": "1.10.1", - "@searchspring/snap-url-manager": "1.10.1" + "@searchspring/snap-client": "1.11.0", + "@searchspring/snap-event-manager": "1.11.0", + "@searchspring/snap-logger": "1.11.0", + "@searchspring/snap-profiler": "1.11.0", + "@searchspring/snap-store-mobx": "1.11.0", + "@searchspring/snap-tracker": "1.11.0", + "@searchspring/snap-url-manager": "1.11.0" } }, "packages/snap-event-manager": { "name": "@searchspring/snap-event-manager", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT" }, "packages/snap-logger": { "name": "@searchspring/snap-logger", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { - "@searchspring/snap-toolbox": "1.10.1" + "@searchspring/snap-toolbox": "1.11.0" } }, "packages/snap-platforms": { "name": "@searchspring/snap-platforms", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { - "@searchspring/snap-toolbox": "1.10.1" + "@searchspring/snap-toolbox": "1.11.0" }, "devDependencies": { - "@searchspring/snap-controller": "1.10.1", - "@searchspring/snap-store-mobx": "1.10.1" + "@searchspring/snap-controller": "1.11.0", + "@searchspring/snap-store-mobx": "1.11.0" } }, "packages/snap-preact": { "name": "@searchspring/snap-preact", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { "@emotion/react": "11.13.0", - "@searchspring/snap-client": "1.10.1", - "@searchspring/snap-controller": "1.10.1", - "@searchspring/snap-event-manager": "1.10.1", - "@searchspring/snap-logger": "1.10.1", - "@searchspring/snap-platforms": "1.10.1", - "@searchspring/snap-profiler": "1.10.1", - "@searchspring/snap-store-mobx": "1.10.1", - "@searchspring/snap-toolbox": "1.10.1", - "@searchspring/snap-tracker": "1.10.1", - "@searchspring/snap-url-manager": "1.10.1", + "@searchspring/snap-client": "1.11.0", + "@searchspring/snap-controller": "1.11.0", + "@searchspring/snap-event-manager": "1.11.0", + "@searchspring/snap-logger": "1.11.0", + "@searchspring/snap-platforms": "1.11.0", + "@searchspring/snap-profiler": "1.11.0", + "@searchspring/snap-store-mobx": "1.11.0", + "@searchspring/snap-toolbox": "1.11.0", + "@searchspring/snap-tracker": "1.11.0", + "@searchspring/snap-url-manager": "1.11.0", "classnames": "^2.3.2", "color": "^4.2.3", "deepmerge": "4.3.1", @@ -31370,6 +31914,7 @@ "css-loader": "^6.9.0", "cypress": "^13.7.1", "cypress-wait-until": "^1.7.2", + "esbuild-loader": "^4.4.2", "preact": "10.9.0", "react": "16.14.0", "react-dom": "16.14.0", @@ -31447,10 +31992,10 @@ }, "packages/snap-preact-demo": { "name": "@searchspring/snap-preact-demo", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { - "@searchspring/snap-preact": "1.10.1", + "@searchspring/snap-preact": "1.11.0", "deepmerge": "4.3.1", "mobx-react": "7.6.0", "preact": "10.9.0" @@ -33372,43 +33917,45 @@ }, "packages/snap-profiler": { "name": "@searchspring/snap-profiler", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT" }, "packages/snap-shared": { "name": "@searchspring/snap-shared", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "devDependencies": { - "@searchspring/snap-client": "1.10.1" + "@searchspring/snap-client": "1.11.0" } }, "packages/snap-store-mobx": { "name": "@searchspring/snap-store-mobx", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { - "@searchspring/snap-toolbox": "1.10.1", + "@searchspring/snap-toolbox": "1.11.0", "mobx": "6.9.0" }, "devDependencies": { - "@searchspring/snap-client": "1.10.1", - "@searchspring/snap-url-manager": "1.10.1" + "@searchspring/snap-client": "1.11.0", + "@searchspring/snap-toolbox": "1.11.0", + "@searchspring/snap-url-manager": "1.11.0", + "mobx": "6.9.0" } }, "packages/snap-toolbox": { "name": "@searchspring/snap-toolbox", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT" }, "packages/snap-tracker": { "name": "@searchspring/snap-tracker", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { - "@searchspring/beacon": "0.0.40", - "@searchspring/snap-store-mobx": "1.10.1", - "@searchspring/snap-toolbox": "1.10.1", + "@searchspring/beacon": "0.0.44", + "@searchspring/snap-store-mobx": "1.11.0", + "@searchspring/snap-toolbox": "1.11.0", "@types/uuid": "8.3.4", "deepmerge": "4.3.1", "uuid": "9.0.1" @@ -33433,7 +33980,7 @@ }, "packages/snap-url-manager": { "name": "@searchspring/snap-url-manager", - "version": "1.10.1", + "version": "1.11.0", "license": "MIT", "dependencies": { "deepmerge": "4.3.1", @@ -33488,6 +34035,45 @@ "webpack-merge": "^5.8.0" } }, + "packages/snapps/eldesign.com": { + "version": "0.1.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@searchspring/snap-preact": "1.10.1", + "mobx": "6.9.0", + "mobx-react": "7.6.0", + "preact": "10.9.0" + }, + "devDependencies": { + "@babel/core": "^7.21.4", + "@babel/eslint-parser": "^7.21.3", + "@babel/plugin-transform-react-jsx": "^7.21.0", + "@babel/plugin-transform-runtime": "^7.21.4", + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.18.6", + "@babel/runtime": "^7.21.0", + "@emotion/react": "11.13.0", + "@lhci/cli": "^0.14.0", + "@searchspring/browserslist-config-snap": "^1.0.6", + "@searchspring/prettier": "^1.0.2", + "babel-loader": "^9.1.2", + "core-js": "^3.30.0", + "css-loader": "^6.7.3", + "eslint": "^8.37.0", + "eslint-plugin-react": "^7.32.2", + "husky": "^8.0.3", + "lint-staged": "^13.2.0", + "prettier": "^2.8.7", + "ts-loader": "9.5.1", + "webpack": "^5.77.0", + "webpack-bundle-analyzer": "^4.8.0", + "webpack-cli": "^5.0.1", + "webpack-dev-server": "^4.13.2", + "webpack-merge": "^5.8.0", + "whatwg-fetch": "3.6.2" + } + }, "packages/snapps/movieposters.com": { "version": "0.1.0", "extraneous": true, @@ -33577,6 +34163,46 @@ "webpack-dev-server": "^4.13.2", "webpack-merge": "^5.8.0" } + }, + "packages/snapps/templates": { + "version": "0.1.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@searchspring/snap-preact": "1.9.1", + "deepmerge": "4.3.1", + "mobx": "6.9.0", + "mobx-react": "7.6.0", + "preact": "10.9.0" + }, + "devDependencies": { + "@babel/core": "^7.21.4", + "@babel/eslint-parser": "^7.21.3", + "@babel/plugin-transform-react-jsx": "^7.21.0", + "@babel/plugin-transform-runtime": "^7.21.4", + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.18.6", + "@babel/runtime": "^7.21.0", + "@emotion/react": "11.13.0", + "@lhci/cli": "^0.14.0", + "@searchspring/browserslist-config-snap": "^1.0.6", + "@searchspring/prettier": "^1.0.2", + "babel-loader": "^9.1.2", + "core-js": "^3.30.0", + "css-loader": "^6.7.3", + "eslint": "^8.37.0", + "eslint-plugin-react": "^7.32.2", + "husky": "^8.0.3", + "lint-staged": "^13.2.0", + "prettier": "^2.8.7", + "ts-loader": "9.5.1", + "webpack": "^5.77.0", + "webpack-bundle-analyzer": "^4.8.0", + "webpack-cli": "^5.0.1", + "webpack-dev-server": "^4.13.2", + "webpack-merge": "^5.8.0", + "whatwg-fetch": "3.6.2" + } } } } diff --git a/package.json b/package.json index 2da7a819f..8f7eb47a6 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "prettier": "@searchspring/prettier", "devDependencies": { "@searchspring/prettier": "1.0.2", - "@searchspring/snapi-types": "^0.1.40", + "@athoscommerce/snapi-types": "^1.0.4", "@testing-library/jest-dom": "6.2.0", "@testing-library/preact": "^3.2.4", "@testing-library/user-event": "^14.5.2", diff --git a/packages/snap-client/CHANGELOG.md b/packages/snap-client/CHANGELOG.md index b0e52b6fe..8085ad88c 100644 --- a/packages/snap-client/CHANGELOG.md +++ b/packages/snap-client/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0](https://github.com/searchspring/snap/compare/v1.10.1...v1.11.0) (2026-01-15) + +### Bug Fixes + +- add noBeacon param to recommend api, update beacon and snapi-types version ([89e185c](https://github.com/searchspring/snap/commit/89e185c1b06a49274103c50d3bf410e2effa1e3a)) +## [0.72.1](https://github.com/searchspring/snap/compare/v0.72.0...v0.72.1) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-client + +# [0.72.0](https://github.com/searchspring/snap/compare/v0.71.0...v0.72.0) (2026-01-14) + +### Bug Fixes + +- **networkcache:** bugfix to prevent memoryCache from growing too large, and delete expired entries ([0534b6c](https://github.com/searchspring/snap/commit/0534b6c4ccb62389669c19f8f9f87cbd098fa6a5)) + +### Features + +- **client:** adding support to have separate subdomain in api - using this for recommend requests ([ceb6d65](https://github.com/searchspring/snap/commit/ceb6d6500c2c8a57df0356535d0f74d8d0464fae)) +- **networkcache:** get function no longer accounts for personalization params when backforward nav ([de52d7e](https://github.com/searchspring/snap/commit/de52d7e02c7ed70488a31a4f92bd26820a8f3823)) + ## [1.10.1](https://github.com/searchspring/snap/compare/v1.10.0...v1.10.1) (2025-10-06) ### Bug Fixes @@ -134,6 +154,40 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.1.0](https://github.com/searchspring/snap/compare/v0.60.1...v1.1.0) (2024-09-20) +# [0.71.0](https://github.com/searchspring/snap/compare/v0.70.1...v0.71.0) (2025-11-26) + +**Note:** Version bump only for package @searchspring/snap-client + +## [0.70.1](https://github.com/searchspring/snap/compare/v0.70.0...v0.70.1) (2025-11-17) + +**Note:** Version bump only for package @searchspring/snap-client + +# [0.70.0](https://github.com/searchspring/snap/compare/v0.69.2...v0.70.0) (2025-11-13) + +**Note:** Version bump only for package @searchspring/snap-client + +## [0.69.2](https://github.com/searchspring/snap/compare/v0.69.1...v0.69.2) (2025-11-04) + +**Note:** Version bump only for package @searchspring/snap-client + +## [0.69.1](https://github.com/searchspring/snap/compare/v0.69.0...v0.69.1) (2025-10-23) + +**Note:** Version bump only for package @searchspring/snap-client + +# [0.69.0](https://github.com/searchspring/snap/compare/v0.68.0...v0.69.0) (2025-10-16) + +**Note:** Version bump only for package @searchspring/snap-client + +# [0.68.0](https://github.com/searchspring/snap/compare/v0.67.5...v0.68.0) (2025-08-18) + +### Bug Fixes + +- add noBeacon param to recommend api, update beacon and snapi-types version ([89e185c](https://github.com/searchspring/snap/commit/89e185c1b06a49274103c50d3bf410e2effa1e3a)) + +## [0.67.5](https://github.com/searchspring/snap/compare/v0.67.4...v0.67.5) (2025-08-11) + +**Note:** Version bump only for package @searchspring/snap-client + ## [0.67.4](https://github.com/searchspring/snap/compare/v0.67.3...v0.67.4) (2025-07-29) **Note:** Version bump only for package @searchspring/snap-client diff --git a/packages/snap-client/README.md b/packages/snap-client/README.md index 212634ce3..1336ab09f 100644 --- a/packages/snap-client/README.md +++ b/packages/snap-client/README.md @@ -9,7 +9,7 @@ npm install --save @searchspring/snap-client ``` ## Import -```typescript +```js import { Client } from '@searchspring/snap-client'; ``` @@ -20,7 +20,7 @@ Globals are API parameters that will be applied to all searches requested by the `siteId` (required) -```typescript +```js const globals = { siteId: 'a1b2c3' }; @@ -30,7 +30,7 @@ Any other keys defined here will be passed to the API request For example, with background filter: -```typescript +```js const globals = { siteId: 'a1b2c3', filters: [{ @@ -45,7 +45,7 @@ const globals = { ## Client Config Optional configuration for each requester. This can be used to specifiy a development origin URL or to configure cache settings per requester. -```typescript +```js type ClientConfig = { mode?: keyof typeof AppMode | AppMode; fetchApi?: WindowOrWorkerGlobalScope['fetch']; @@ -90,7 +90,7 @@ Each requester in the Snap Client has its own cache settings, which can be confi `entries`: to allow preload the cache. This is primarily used in Email Recommendations. -```typescript +```js const metaResponse = { "facets": { "brand": { @@ -152,7 +152,7 @@ const response = await client.search({ ``` ## Standalone usage -```typescript +```js const client = new Client(globals, clientConfig); const { meta, results } = await client.search({ @@ -167,7 +167,7 @@ const { meta, results } = await client.search({ ## `search` method Makes a request to the Searchspring Search API and returns a promise. -```typescript +```js const client = new Client(globals, clientConfig); const { meta, results } = await client.search({ @@ -182,7 +182,7 @@ const { meta, results } = await client.search({ ## `autocomplete` method Makes a request to the Searchspring Autocomplete API and returns a promise. -```typescript +```js const client = new Client(globals, clientConfig); const { meta, results } = await client.autocomplete({ @@ -201,7 +201,7 @@ const { meta, results } = await client.autocomplete({ ## `meta` method Makes a request to the Searchspring Search API to fetch meta properties, it returns a promise. The `search` method utilizes this method. -```typescript +```js const client = new Client(globals, clientConfig); const meta = await client.meta(); ``` @@ -209,10 +209,10 @@ const meta = await client.meta(); ## `trending` method Makes a request to the Searchspring Trending API and returns a promise. -```typescript +```js const client = new Client(globals, clientConfig); -const trending = await client.trending({ - siteId: 'abc123', +const results = await client.trending({ + siteId: 'REPLACE_WITH_YOUR_SITE_ID', limit: 5 }); ``` @@ -220,7 +220,7 @@ const trending = await client.trending({ ## `finder` method Makes a request to the Searchspring finder API and returns a promise. -```typescript +```js const client = new Client(globals, clientConfig); const { meta, results } = await client.finder({ filters: [{ @@ -235,12 +235,12 @@ const { meta, results } = await client.finder({ ## `recommend` method Makes a request to the Searchspring Recommend API and returns a promise. -```typescript +```js const client = new Client(globals, clientConfig); const { profile, meta, recommend } = await client.recommend({ tag: 'similar', - siteId: 'abc123', + siteId: 'REPLACE_WITH_YOUR_SITE_ID', products: ['product123'], - shopper: 'snapdev', + shopper: '[REPLACE WITH LOGGED IN SHOPPER ID]' }); ``` diff --git a/packages/snap-client/package.json b/packages/snap-client/package.json index a150872c6..fd91635bd 100644 --- a/packages/snap-client/package.json +++ b/packages/snap-client/package.json @@ -1,6 +1,6 @@ { "name": "@searchspring/snap-client", - "version": "1.10.1", + "version": "1.11.0", "description": "Snap Client", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", @@ -11,7 +11,7 @@ "access": "public" }, "scripts": { - "build": "rm -rf ./dist && tsc && tsc -p tsconfig.cjs.json", + "build": "rm -rf ./dist && rm -fr ./components/dist && tsc & p1=$!; tsc -p tsconfig.cjs.json & p2=$!; wait $p1 && wait $p2", "build:docs": "typedoc --out docs src/index.ts", "dev": "tsc --watch", "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'", @@ -20,7 +20,7 @@ "test:watch": "jest --watch" }, "dependencies": { - "@searchspring/snap-toolbox": "1.10.1", + "@searchspring/snap-toolbox": "1.11.0", "deepmerge": "4.3.1" }, "sideEffects": false, diff --git a/packages/snap-client/src/Client/Client.test.ts b/packages/snap-client/src/Client/Client.test.ts index a09600028..e6b903c24 100644 --- a/packages/snap-client/src/Client/Client.test.ts +++ b/packages/snap-client/src/Client/Client.test.ts @@ -3,6 +3,7 @@ import { Client } from './Client'; import type { ClientConfig } from '../types'; import { MockData } from '@searchspring/snap-shared'; import { AppMode, version } from '@searchspring/snap-toolbox'; +import { NO_BEACON_PARAM } from './transforms'; const mockData = new MockData(); @@ -366,10 +367,11 @@ describe('Snap Client', () => { ], test: true, siteId: '8uyt2m', + [NO_BEACON_PARAM]: true, }, }; - const recommendCacheKey = '{"profiles":[{"tag":"dress"}],"siteId":"8uyt2m","test":true}'; + const recommendCacheKey = `{"profiles":[{"tag":"dress"}],"siteId":"8uyt2m","${NO_BEACON_PARAM}":true,"test":true}`; expect(recommendRequesterSpy).toHaveBeenCalledTimes(2); expect(recommendRequesterSpy.mock.calls).toEqual([ @@ -663,10 +665,11 @@ describe('Snap Client', () => { ], test: true, siteId: '8uyt2m', + [NO_BEACON_PARAM]: true, }, }; - const recommendCacheKey = '{"profiles":[{"tag":"dress"}],"siteId":"8uyt2m","test":true}'; + const recommendCacheKey = `{"profiles":[{"tag":"dress"}],"siteId":"8uyt2m","${NO_BEACON_PARAM}":true,"test":true}`; expect(recommendRequesterSpy).toHaveBeenCalledTimes(2); expect(recommendRequesterSpy.mock.calls).toEqual([ diff --git a/packages/snap-client/src/Client/Client.ts b/packages/snap-client/src/Client/Client.ts index b45926bfa..8cc1ef201 100644 --- a/packages/snap-client/src/Client/Client.ts +++ b/packages/snap-client/src/Client/Client.ts @@ -19,7 +19,7 @@ import type { SearchResponseModel, AutocompleteRequestModel, AutocompleteResponseModel, -} from '@searchspring/snapi-types'; +} from '@athoscommerce/snapi-types'; import deepmerge from 'deepmerge'; diff --git a/packages/snap-client/src/Client/NetworkCache/NetworkCache.test.ts b/packages/snap-client/src/Client/NetworkCache/NetworkCache.test.ts index e30cfbc14..d38c53486 100644 --- a/packages/snap-client/src/Client/NetworkCache/NetworkCache.test.ts +++ b/packages/snap-client/src/Client/NetworkCache/NetworkCache.test.ts @@ -31,6 +31,12 @@ describe('Network Cache', () => { mockStorage[key] = value; }); global.Storage.prototype.getItem = jest.fn((key) => mockStorage[key]); + + Object.defineProperty(window, 'performance', { + value: { + getEntriesByType: jest.fn().mockReturnValue([{ type: 'navigate' }]), + }, + }); }); beforeEach(() => { @@ -52,17 +58,17 @@ describe('Network Cache', () => { }); describe('can set, get and clear', () => { - const cache = new NetworkCache(); + it('can use the set function', async () => { + const cache = new NetworkCache(); - // @ts-ignore - const memoryCache = cache.memoryCache; + // @ts-ignore + const memoryCache = cache.memoryCache; - it('can use the set function', async () => { expect(memoryCache).toBeDefined(); expect(memoryCache).toEqual({}); - //no localStorage initially - expect(mockStorage[CACHE_STORAGE_KEY]).toBeUndefined(); + // localStorage empty initially + expect(mockStorage[CACHE_STORAGE_KEY]).toEqual('{}'); cache.set('key', typedResponse); @@ -75,12 +81,18 @@ describe('Network Cache', () => { }); it('can use the get function', async () => { + const cache = new NetworkCache(); + + cache.set('key', typedResponse); const getterResponse = cache.get('key'); expect(getterResponse).toEqual(typedResponse); }); it('can use the get & clear function', async () => { + const cache = new NetworkCache(); + cache.set('key', typedResponse); + const getterResponse = cache.get('key'); expect(getterResponse).toEqual(typedResponse); @@ -94,6 +106,8 @@ describe('Network Cache', () => { }); it('the get function returns undefined when it finds no key', async () => { + const cache = new NetworkCache(); + const getterResponse = cache.get('thisdoesntexist'); expect(getterResponse).toEqual(undefined); @@ -112,42 +126,35 @@ describe('Network Cache', () => { }); describe('can set custom config settings', () => { - const cacheConfig = { - ttl: 1000, - enabled: true, - maxSize: 4, // KB - purgeable: true, - }; + it('can set custom ttl to expire memoryCache entities', () => { + const cacheConfig = { + ttl: 1000, + enabled: true, + maxSize: 4, // KB + purgeable: true, + }; - const cache = new NetworkCache(cacheConfig); + const cache = new NetworkCache(cacheConfig); - cache.set('key', typedResponse); + cache.set('key', typedResponse); - expect(cache.get('key')).toEqual(typedResponse); + expect(cache.get('key')).toEqual(typedResponse); - it('can set custom ttl to expire memoryCache entities', () => { setTimeout(() => { expect(cache.get('key')).toEqual(undefined); }, 1200); }); - it('no max size for memory', async () => { - await cache.set('key', typedResponse); - await cache.set('key2', typedResponse); - await cache.set('key3', typedResponse); - await cache.set('key4', typedResponse); - await cache.set('key5', typedResponse); - await cache.set('key6', typedResponse); - await cache.set('key7', typedResponse); - await cache.set('key8', typedResponse); - await cache.set('key9', typedResponse); - await cache.set('key10', typedResponse); + it('has a max size for memory', async () => { + const cacheConfig = { + ttl: 1000, + enabled: true, + maxSize: 20, // KB + purgeable: true, + }; - expect(cache.get('key')).toEqual(typedResponse); - expect(cache.get('key10')).toEqual(typedResponse); - }); + const cache = new NetworkCache(cacheConfig); - it('has max size for local storage', async () => { await cache.set('key', typedResponse); await cache.set('key2', typedResponse); await cache.set('key3', typedResponse); @@ -159,15 +166,14 @@ describe('Network Cache', () => { await cache.set('key9', typedResponse); await cache.set('key10', typedResponse); - const stored = sessionStorage.getItem(CACHE_STORAGE_KEY); - const localData = stored && JSON.parse(stored); - expect(localData['key']).toBeUndefined(); - expect(localData['key10'].value).toEqual(typedResponse); + // removes oldest entry 'key' as storage limit exceeded + expect(cache.get('key')).not.toEqual(typedResponse); + expect(cache.get('key10')).toEqual(typedResponse); }); it('can disable purging max size for local storage', async () => { const cacheConfig = { - ttl: 1000, + ttl: 10000, enabled: true, maxSize: 4, // KB purgeable: false, @@ -179,7 +185,6 @@ describe('Network Cache', () => { }; const cache = new NetworkCache(cacheConfig); - const cache2 = new NetworkCache(cacheConfig2); await cache.set('thisRemains', typedResponse); @@ -213,4 +218,108 @@ describe('Network Cache', () => { expect(cache.get(key)).toEqual(typedResponse); }); }); + + describe('initialization from storage', () => { + it('loads existing cache from session storage on initialization', () => { + const storedCache = { + existingKey: { + value: typedResponse, + expires: Date.now() + 10000, + purgeable: true, + }, + }; + mockStorage[CACHE_STORAGE_KEY] = JSON.stringify(storedCache); + + const cache = new NetworkCache(); + expect(cache.get('existingKey')).toEqual(typedResponse); + }); + + it('purges expired items from session storage on initialization', () => { + const storedCache = { + expiredKey: { + value: typedResponse, + expires: Date.now() - 10000, // Expired + purgeable: true, + }, + validKey: { + value: typedResponse, + expires: Date.now() + 10000, + purgeable: true, + }, + }; + mockStorage[CACHE_STORAGE_KEY] = JSON.stringify(storedCache); + + const cache = new NetworkCache(); + expect(cache.get('expiredKey')).toBeUndefined(); + expect(cache.get('validKey')).toEqual(typedResponse); + }); + }); + + describe('get function behavior', () => { + it('can retrieve a cached response with ignored keys during back/forward navigation', async () => { + const cache = new NetworkCache(); + const url = '/api/search/search.json'; + const payload = { q: 'dress', lastViewed: ['123'], cart: ['456'] }; + const key = `${url}${JSON.stringify(payload)}`; + const cachedPayload = { q: 'dress', lastViewed: ['789'], cart: ['012'] }; + const cachedKey = `${url}${JSON.stringify(cachedPayload)}`; + + // Mock performance.getEntriesByType + Object.defineProperty(performance, 'getEntriesByType', { + value: jest.fn().mockReturnValue([{ type: 'back_forward' }]), + writable: true, + }); + + // Set initial cache + cache.set(cachedKey, typedResponse); + + // Attempt to get with different ignored keys + const response = cache.get(key); + expect(response).toEqual(typedResponse); + }); + + it('does not retrieve a cached response with different non-ignored keys during back/forward navigation', async () => { + const cache = new NetworkCache(); + const url = '/api/search/search.json'; + const payload = { q: 'dress', lastViewed: ['123'], cart: ['456'] }; + const key = `${url}${JSON.stringify(payload)}`; + const cachedPayload = { q: 'shoes', lastViewed: ['789'], cart: ['012'] }; + const cachedKey = `${url}${JSON.stringify(cachedPayload)}`; + + // Mock performance.getEntriesByType + Object.defineProperty(performance, 'getEntriesByType', { + value: jest.fn().mockReturnValue([{ type: 'back_forward' }]), + writable: true, + }); + + // Set initial cache + cache.set(cachedKey, typedResponse); + + // Attempt to get with different non-ignored keys + const response = cache.get(key); + expect(response).toBeUndefined(); + }); + + it('does not ignore keys during normal navigation', async () => { + const cache = new NetworkCache(); + const url = '/api/search/search.json'; + const payload = { q: 'dress', lastViewed: ['123'], cart: ['456'] }; + const key = `${url}${JSON.stringify(payload)}`; + const cachedPayload = { q: 'dress', lastViewed: ['789'], cart: ['012'] }; + const cachedKey = `${url}${JSON.stringify(cachedPayload)}`; + + // Mock performance.getEntriesByType + Object.defineProperty(performance, 'getEntriesByType', { + value: jest.fn().mockReturnValue([{ type: 'navigate' }]), + writable: true, + }); + + // Set initial cache + cache.set(cachedKey, typedResponse); + + // Attempt to get with different ignored keys + const response = cache.get(key); + expect(response).toBeUndefined(); + }); + }); }); diff --git a/packages/snap-client/src/Client/NetworkCache/NetworkCache.ts b/packages/snap-client/src/Client/NetworkCache/NetworkCache.ts index 27968b299..62a505a22 100644 --- a/packages/snap-client/src/Client/NetworkCache/NetworkCache.ts +++ b/packages/snap-client/src/Client/NetworkCache/NetworkCache.ts @@ -7,7 +7,7 @@ const CACHE_STORAGE_KEY = 'ss-networkcache'; const defaultConfig: DefaultCacheConfig = { enabled: true, ttl: 300000, // ms - maxSize: 200, // KB + maxSize: 1000, // KB purgeable: true, }; @@ -18,6 +18,9 @@ export class NetworkCache { constructor(config?: CacheConfig) { this.config = deepmerge(defaultConfig, config || {}); + this.load(); + + // this allows you to pre-populate the cache from the config - primarily used for email recs this.config?.entries && Object.keys(this.config.entries).map((key: string) => { if (this.config.entries && this.config.entries[key]) { @@ -26,42 +29,118 @@ export class NetworkCache { }); } + public load(): void { + // initialize cache from session storage + if (typeof window !== 'undefined' && window?.sessionStorage) { + const stored: any = window.sessionStorage.getItem(CACHE_STORAGE_KEY); + const newStored: Cache = { + ...(stored && JSON.parse(stored)), + }; + + this.memoryCache = newStored || {}; + } + + this.purgeExpired(); + } + public get(key: string): Response | void { if (this.config.enabled) { + this.load(); + try { - if (this.memoryCache[key]) { - if (Date.now() < this.memoryCache[key].expires) { - return deepmerge({}, this.memoryCache[key].value); + let ignoreKeys: string[] = []; + if (typeof window !== 'undefined') { + const navigationType = (window.performance?.getEntriesByType('navigation')?.[0] as PerformanceNavigationTiming | undefined)?.type; + if (navigationType === 'back_forward') { + ignoreKeys = ['lastViewed', 'cart']; } } - if (typeof window !== 'undefined' && window?.sessionStorage) { - const stored = window.sessionStorage.getItem(CACHE_STORAGE_KEY); - const localData: Cache = stored && JSON.parse(stored); + if (Object.keys(this.memoryCache).length && key) { + let storageKey = key; + + //this only applies to search calls + if (ignoreKeys.length && key.startsWith('/api/search/search.json')) { + try { + const url = key.split('{')[0]; + const payload = '{' + key.split('{')[1]; + const searchParams = JSON.parse(payload); + + const foundKey = Object.keys(this.memoryCache).find((cachedResponse) => { + try { + const storedUrl = cachedResponse.split('{')[0]; + if (storedUrl == url) { + const storedPayload = '{' + cachedResponse.split('{')[1]; + + const parsedPayload = JSON.parse(storedPayload); + + const allKeys = Array.from(new Set([...Object.keys(searchParams), ...Object.keys(parsedPayload)])); + + for (const k of allKeys) { + if (ignoreKeys.includes(k)) continue; + if (JSON.stringify(searchParams[k]) !== JSON.stringify(parsedPayload[k])) { + return false; + } + } + return true; + } else return false; + } catch { + return false; + } + }); + + if (foundKey) { + storageKey = foundKey; + } + } catch (e) { + // key is not json + } + } - if (localData && key && localData[key]) { + if (this.memoryCache[storageKey]) { // compare the expiry time of the item with the current time - if (Date.now() >= localData[key].expires) { + if (Date.now() >= this.memoryCache[storageKey].expires) { // remove item const newStored = { - ...localData, + ...this.memoryCache, }; - delete newStored[key]; + delete newStored[storageKey]; // update storage window.sessionStorage.setItem(CACHE_STORAGE_KEY, JSON.stringify(newStored)); } else { - return localData[key].value; + return this.memoryCache[storageKey].value; } } } - } catch { - console.warn('something went wrong getting from cache'); + } catch (err) { + console.warn('something went wrong getting from cache', err); + } + } + } + + private purgeExpired(): void { + Object.keys(this.memoryCache).forEach((cacheKey) => { + //clean up expired cache keys in memory + if (Date.now() > this.memoryCache[cacheKey].expires) { + delete this.memoryCache[cacheKey]; } + }); + + // update storage + try { + if (typeof window !== 'undefined' && window?.sessionStorage) { + const stringifiedCache = JSON.stringify(this.memoryCache); + window.sessionStorage.setItem(CACHE_STORAGE_KEY, stringifiedCache); + } + } catch { + console.warn('failed to store network cache'); } } public set(key: string, value: Response): void { if (this.config.enabled) { + this.load(); + try { const cacheObject = { value, @@ -69,33 +148,27 @@ export class NetworkCache { purgeable: this.config.purgeable, }; + // purge old items if we are over max size + let size = new Blob([JSON.stringify(this.memoryCache)], { endings: 'native' }).size / 1024; + while (size > this.config.maxSize) { + const oldestKey = Object.keys(this.memoryCache) + .filter((key) => this.memoryCache[key].purgeable) + .sort((a, b) => { + return this.memoryCache[a].expires - this.memoryCache[b].expires; + })[0]; + + if (!oldestKey) break; + delete this.memoryCache[oldestKey]; + + // recalculate size after removing oldest + size = new Blob([JSON.stringify(this.memoryCache)], { endings: 'native' }).size / 1024; + } + + // store cache in memory this.memoryCache[key] = cacheObject; if (typeof window !== 'undefined' && window?.sessionStorage) { - const stored: any = window.sessionStorage.getItem(CACHE_STORAGE_KEY); - const newStored: Cache = { - ...(stored && JSON.parse(stored)), - }; - newStored[key] = cacheObject; - - let size = new Blob([JSON.stringify(newStored)], { endings: 'native' }).size / 1024; - while (size > this.config.maxSize) { - const oldestKey = Object.keys(newStored) - .filter((key) => newStored[key].purgeable) - .sort((a, b) => { - return newStored[a].expires - newStored[b].expires; - })[0]; - - if (!oldestKey) break; - delete newStored[oldestKey]; - - // recalculate size after removing oldest - size = new Blob([JSON.stringify(newStored)], { endings: 'native' }).size / 1024; - } - - if (size < this.config.maxSize) { - window.sessionStorage.setItem(CACHE_STORAGE_KEY, JSON.stringify(newStored)); - } + window.sessionStorage.setItem(CACHE_STORAGE_KEY, JSON.stringify(this.memoryCache)); } } catch { console.warn('something went wrong setting to cache'); diff --git a/packages/snap-client/src/Client/apis/Abstract.test.ts b/packages/snap-client/src/Client/apis/Abstract.test.ts index 22237df50..ea2328638 100644 --- a/packages/snap-client/src/Client/apis/Abstract.test.ts +++ b/packages/snap-client/src/Client/apis/Abstract.test.ts @@ -54,7 +54,7 @@ describe('Abstract Api', () => { config: { enabled: true, ttl: 300000, // ms - maxSize: 200, // KB + maxSize: 1000, // KB purgeable: true, }, memoryCache: {}, diff --git a/packages/snap-client/src/Client/apis/Hybrid.ts b/packages/snap-client/src/Client/apis/Hybrid.ts index 19f0740c7..c6eff4868 100644 --- a/packages/snap-client/src/Client/apis/Hybrid.ts +++ b/packages/snap-client/src/Client/apis/Hybrid.ts @@ -8,7 +8,7 @@ import { MetaResponseModel, SearchRequestModel, SearchResponseModel, -} from '@searchspring/snapi-types'; +} from '@athoscommerce/snapi-types'; import { API, ApiConfigurationParameters, LegacyAPI, SuggestAPI, ApiConfiguration } from '.'; import { transformSearchRequest, transformSearchResponse, transformSuggestResponse } from '../transforms'; diff --git a/packages/snap-client/src/Client/apis/Legacy.ts b/packages/snap-client/src/Client/apis/Legacy.ts index 2d2ddc699..18a711025 100644 --- a/packages/snap-client/src/Client/apis/Legacy.ts +++ b/packages/snap-client/src/Client/apis/Legacy.ts @@ -1,4 +1,4 @@ -import { MetaRequestModel, MetaResponseModel } from '@searchspring/snapi-types'; +import { MetaRequestModel, MetaResponseModel } from '@athoscommerce/snapi-types'; import { API, HTTPQuery } from '.'; import { HTTPHeaders } from '../../types'; diff --git a/packages/snap-client/src/Client/apis/Recommend.test.ts b/packages/snap-client/src/Client/apis/Recommend.test.ts index dc9c29f02..024c80b0e 100644 --- a/packages/snap-client/src/Client/apis/Recommend.test.ts +++ b/packages/snap-client/src/Client/apis/Recommend.test.ts @@ -4,6 +4,7 @@ import { RecommendAPI } from './Recommend'; import { MockData } from '@searchspring/snap-shared'; import type { RecommendPostRequestModel } from '../../types'; +import { NO_BEACON_PARAM } from '../transforms'; const mockData = new MockData(); @@ -95,7 +96,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: `{"profiles":[{"tag":"similar"}],"siteId":"8uyt2m"}`, + body: `{"profiles":[{"tag":"similar"}],"siteId":"8uyt2m","${NO_BEACON_PARAM}":true}`, }; const requestUrl = 'https://8uyt2m.a.p13n.athoscommerce.io/v1/recommend'; @@ -169,7 +170,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"similar","limit":14},{"tag":"crossSell","limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"similar","limit":14},{"tag":"crossSell","limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -215,7 +216,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"similar","categories":["shirts"],"limit":14},{"tag":"crossSell","limit":10},{"tag":"crossSell","categories":["pants"],"limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"similar","categories":["shirts"],"limit":14},{"tag":"crossSell","limit":10},{"tag":"crossSell","categories":["pants"],"limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -254,7 +255,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"similar","brands":["shirts"],"limit":14},{"tag":"crossSell","brands":["pants","pants2"],"limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"similar","brands":["shirts"],"limit":14},{"tag":"crossSell","brands":["pants","pants2"],"limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -290,7 +291,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"similar","limit":20},{"tag":"crossSell"}],"siteId":"8uyt2m","products":["sku1"]}', + body: `{"profiles":[{"tag":"similar","limit":20},{"tag":"crossSell"}],"siteId":"8uyt2m","products":["sku1"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -332,14 +333,14 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"similar","limit":20}],"siteId":"8uyt2m","products":["sku1"]}', + body: `{"profiles":[{"tag":"similar","limit":20}],"siteId":"8uyt2m","products":["sku1"],"${NO_BEACON_PARAM}":true}`, }; const secondBatchPOSTParams = { method: 'POST', headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"crossSell"},{"tag":"static"}],"siteId":"8uyt2m"}', + body: `{"profiles":[{"tag":"crossSell"},{"tag":"static"}],"siteId":"8uyt2m","${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledTimes(2); @@ -382,7 +383,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"similar","limit":20},{"tag":"crossSell","limit":10}],"siteId":"8uyt2m","products":["sku1"]}', + body: `{"profiles":[{"tag":"similar","limit":20},{"tag":"crossSell","limit":10}],"siteId":"8uyt2m","products":["sku1"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -424,14 +425,14 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"similar","limit":20}],"siteId":"8uyt2m","products":["sku1"]}', + body: `{"profiles":[{"tag":"similar","limit":20}],"siteId":"8uyt2m","products":["sku1"],"${NO_BEACON_PARAM}":true}`, }; const POSTParams123abc = { method: 'POST', headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"crossSell","limit":5}],"siteId":"123abc"}', + body: `{"profiles":[{"tag":"crossSell","limit":5}],"siteId":"123abc","${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledTimes(2); @@ -486,7 +487,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"andanother","categories":["pants"],"limit":10},{"tag":"another","limit":10},{"tag":"similar","categories":["shirts"],"limit":14},{"tag":"other","limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"andanother","categories":["pants"],"limit":10},{"tag":"another","limit":10},{"tag":"similar","categories":["shirts"],"limit":14},{"tag":"other","limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; //add delay for paramBatch.timeout @@ -530,7 +531,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"crosssell","categories":["dress"],"limit":20},{"tag":"similar","categories":["shirts"],"limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"crosssell","categories":["dress"],"limit":20},{"tag":"similar","categories":["shirts"],"limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -581,7 +582,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"first","categories":["dress"],"limit":20},{"tag":"second","categories":["shirts"],"limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"first","categories":["dress"],"limit":20},{"tag":"second","categories":["shirts"],"limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -621,7 +622,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"crossSell","limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"filters":[{"field":"color","type":"=","values":["red"]}],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"crossSell","limit":10}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"filters":[{"field":"color","type":"=","values":["red"]}],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -645,7 +646,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"crossSell"}],"siteId":"8uyt2m","products":["some_sku","some_sku2","marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"crossSell"}],"siteId":"8uyt2m","products":["some_sku","some_sku2","marnie-runner-2-7x10"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -669,7 +670,7 @@ describe('Recommend Api', () => { headers: { 'Content-Type': 'text/plain', }, - body: '{"profiles":[{"tag":"crossSell"}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"blockedItems":["blocked_sku1","blocked_sku2"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"]}', + body: `{"profiles":[{"tag":"crossSell"}],"siteId":"8uyt2m","products":["marnie-runner-2-7x10"],"blockedItems":["blocked_sku1","blocked_sku2"],"lastViewed":["marnie-runner-2-7x10","ruby-runner-2-7x10","abbie-runner-2-7x10","riley-4x6","joely-5x8","helena-4x6","kwame-4x6","sadie-4x6","candice-runner-2-7x10","esmeray-4x6","camilla-230x160","candice-4x6","sahara-4x6","dayna-4x6","moema-4x6"],"${NO_BEACON_PARAM}":true}`, }; expect(requestMock).toHaveBeenCalledWith(RequestUrl, POSTParams); @@ -702,6 +703,7 @@ describe('Recommend Api', () => { { field: 'price', type: '<=', values: [40] }, { field: 'color', type: '=', values: ['green'] }, ], + [NO_BEACON_PARAM]: true, }), }; @@ -824,6 +826,7 @@ describe('Recommend Api', () => { 'dayna-4x6', 'moema-4x6', ], + [NO_BEACON_PARAM]: true, }), }; @@ -904,6 +907,7 @@ describe('Recommend Api', () => { 'dayna-4x6', 'moema-4x6', ], + [NO_BEACON_PARAM]: true, }), }; diff --git a/packages/snap-client/src/Client/apis/Recommend.ts b/packages/snap-client/src/Client/apis/Recommend.ts index 3c5477dae..33827c959 100644 --- a/packages/snap-client/src/Client/apis/Recommend.ts +++ b/packages/snap-client/src/Client/apis/Recommend.ts @@ -1,7 +1,7 @@ import { API, ApiConfiguration } from './Abstract'; import { HTTPHeaders, RecommendPostRequestProfileModel } from '../../types'; import { AppMode } from '@searchspring/snap-toolbox'; -import { transformRecommendationFiltersPost } from '../transforms'; +import { NO_BEACON_PARAM, transformRecommendationFiltersPost } from '../transforms'; import { ProfileRequestModel, ProfileResponseModel, RecommendResponseModel, RecommendRequestModel, RecommendPostRequestModel } from '../../types'; class Deferred { @@ -147,6 +147,7 @@ export class RecommendAPI extends API { lastViewed, shopper, }), + [NO_BEACON_PARAM]: true, }; }); @@ -155,12 +156,9 @@ export class RecommendAPI extends API { batch.request.test = true; } - const response = await this.postRecommendations(batch.request as RecommendPostRequestModel); + const response = await this.postRecommendations(batch.request); batch.entries?.forEach((entry, index) => { - response[index]?.results?.forEach((result, idx) => { - result.position = idx + 1; - }); entry.deferred.resolve(response[index]); }); } catch (err) { diff --git a/packages/snap-client/src/Client/apis/Snap.ts b/packages/snap-client/src/Client/apis/Snap.ts index d8b018ecb..049fd3464 100644 --- a/packages/snap-client/src/Client/apis/Snap.ts +++ b/packages/snap-client/src/Client/apis/Snap.ts @@ -5,7 +5,7 @@ import { MetaResponseModel, SearchRequestModel, SearchResponseModel, -} from '@searchspring/snapi-types'; +} from '@athoscommerce/snapi-types'; import { API } from '.'; import { HTTPHeaders } from '../../types'; diff --git a/packages/snap-client/src/Client/transforms/searchRequest.test.ts b/packages/snap-client/src/Client/transforms/searchRequest.test.ts index dd5d3f066..91259df50 100644 --- a/packages/snap-client/src/Client/transforms/searchRequest.test.ts +++ b/packages/snap-client/src/Client/transforms/searchRequest.test.ts @@ -1,4 +1,4 @@ -import { SearchRequestModelFilterValue, SearchRequestModelFilterRange, SearchRequestModelFilter } from '@searchspring/snapi-types'; +import { SearchRequestModelFilterValue, SearchRequestModelFilterRange, SearchRequestModelFilter } from '@athoscommerce/snapi-types'; import { NO_BEACON_PARAM, transformSearchRequest } from './searchRequest'; @@ -287,27 +287,6 @@ describe('search request merchandising transform', () => { expect(params).toEqual({ tag: ['merch.segment/some-segment'], disableMerchandising: true }); }); - it('can disable intellisuggest elevations', () => { - const params = transformSearchRequest.merchandising({ - merchandising: { - intellisuggest: false, - }, - }); - - expect(params).toEqual({ intellisuggest: false }); - }); - - it('can disable intellisuggest elevations even if merchandising is disabled', () => { - const params = transformSearchRequest.merchandising({ - merchandising: { - disabled: true, - intellisuggest: false, - }, - }); - - expect(params).toEqual({ disableMerchandising: true, intellisuggest: false }); - }); - it('can disable inlineBanners', () => { const params = transformSearchRequest.merchandising({ merchandising: { diff --git a/packages/snap-client/src/Client/transforms/searchRequest.ts b/packages/snap-client/src/Client/transforms/searchRequest.ts index 5a37a19df..3f7ed063b 100644 --- a/packages/snap-client/src/Client/transforms/searchRequest.ts +++ b/packages/snap-client/src/Client/transforms/searchRequest.ts @@ -6,7 +6,7 @@ import { SearchRequestModelSorts, SearchRequestModelSortsDirectionEnum, SearchRequestModelFilterRangeAllOfValue, -} from '@searchspring/snapi-types'; +} from '@athoscommerce/snapi-types'; export const NO_BEACON_PARAM = 'noBeacon'; export function transformSearchRequest(request: SearchRequestModel): any { @@ -127,10 +127,6 @@ transformSearchRequest.merchandising = (request: SearchRequestModel = {}) => { }); } - if (typeof reqMerch.intellisuggest == 'boolean') { - merch['intellisuggest'] = reqMerch.intellisuggest; - } - if (reqMerch.disableInlineBanners) { merch['disableInlineBanners'] = reqMerch.disableInlineBanners; } diff --git a/packages/snap-client/src/Client/transforms/searchResponse.test.ts b/packages/snap-client/src/Client/transforms/searchResponse.test.ts index 80ca17f68..f690f84bc 100644 --- a/packages/snap-client/src/Client/transforms/searchResponse.test.ts +++ b/packages/snap-client/src/Client/transforms/searchResponse.test.ts @@ -1,11 +1,14 @@ import { transformSearchResponse, searchResponseType } from './searchResponse'; -import { SearchResponseModelSearchMatchTypeEnum } from '@searchspring/snapi-types'; +import { SearchResponseModelSearchMatchTypeEnum } from '@athoscommerce/snapi-types'; const mockSingleResult = { intellisuggestData: 'eJwrLzbIKTBkYGDwCDcyMDC01HXy8db1CGcwBEITIwYjQ2MLhvSizBQAuS0I0Q', intellisuggestSignature: '73ed9559bc402f46f74ffdac2c207fa4eeaae2342f8cf41c9737d8faf4d62038', image_medium: 'https://cdn.shopify.com/s/files/1/0065/0022/products/OVERTHISSHITMASK_medium.jpg?v=1594226901', variant_images_json: '[]', + available: 'true', + parentId: '12345', + parentImageUrl: 'https://cdn.shopify.com/s/files/1/0065/0022/products/OVERTHISSHITMASK_1024x1024.jpg?v=1594226901', product_price: '8', image_1024x1024: 'https://cdn.shopify.com/s/files/1/0065/0022/products/OVERTHISSHITMASK_1024x1024.jpg?v=1594226901', collection_id: [ @@ -359,22 +362,19 @@ describe('search response transformer pagination', () => { describe('search response transformer result', () => { it('builds response format', () => { - const resultIndex = 123; - const result = transformSearchResponse.result(mockSingleResult, resultIndex); + const result = transformSearchResponse.result(mockSingleResult); expect(result.id).toEqual(mockSingleResult.uid); expect(typeof result.mappings).toEqual('object'); expect(typeof result.mappings?.core).toEqual('object'); expect(typeof result.attributes).toEqual('object'); - expect(result.position).toEqual(resultIndex + 1); }); it('builds core fields', () => { - const result = transformSearchResponse.result(mockSingleResult, 0); + const result = transformSearchResponse.result(mockSingleResult); // TODO: Add all core fields - expect(result.mappings?.core?.name).toEqual(mockSingleResult.name); expect(result.mappings?.core?.sku).toEqual(mockSingleResult.sku); expect(result.mappings?.core?.imageUrl).toEqual(mockSingleResult.imageUrl); @@ -383,10 +383,13 @@ describe('search response transformer result', () => { expect(result.mappings?.core?.brand).toEqual(mockSingleResult.brand); expect(result.mappings?.core?.url).toEqual(mockSingleResult.url); expect(result.mappings?.core?.uid).toEqual(mockSingleResult.uid); + expect(result.mappings?.core?.available).toEqual(true); + expect(result.mappings?.core?.parentId).toEqual(mockSingleResult.parentId); + expect(result.mappings?.core?.parentImageUrl).toEqual(mockSingleResult.parentImageUrl); }); it('leaves core fields out of attributes', () => { - const result = transformSearchResponse.result(mockSingleResult, 0); + const result = transformSearchResponse.result(mockSingleResult); // TODO: Add all core fields @@ -401,7 +404,7 @@ describe('search response transformer result', () => { }); it('builds attributes', () => { - const result = transformSearchResponse.result(mockSingleResult, 0); + const result = transformSearchResponse.result(mockSingleResult); expect(result.attributes?.ss_in_stock).toEqual('In Stock'); expect(result.attributes?.handle).toEqual('over-this-shit-face-mask'); @@ -455,18 +458,19 @@ describe('search response transformer result', () => { }, }; - const result = transformSearchResponse.result(resultWithBadgeFeature, 0); + const result = transformSearchResponse.result(resultWithBadgeFeature); expect(result.attributes?.badges).toBeUndefined(); expect(result.badges).toEqual(resultWithBadgeFeature.badges); // @ts-ignore - typings are wrong intentionally here - const result2 = transformSearchResponse.result(resultWithRandomBadgeField, 0); + const result2 = transformSearchResponse.result(resultWithRandomBadgeField); expect(result2.attributes?.badges).toEqual(resultWithRandomBadgeField.badges); expect(result2.badges).toEqual([]); // @ts-ignore - typings are wrong intentionally here - const result3 = transformSearchResponse.result(resultWithRandomBadgeField2, 0); + const result3 = transformSearchResponse.result(resultWithRandomBadgeField2); expect(result3.attributes?.badges).toEqual(JSON.stringify(resultWithRandomBadgeField2.badges)); + expect(result3.badges).toEqual([]); }); }); diff --git a/packages/snap-client/src/Client/transforms/searchResponse.ts b/packages/snap-client/src/Client/transforms/searchResponse.ts index ebe7e3320..0f8b5a4c7 100644 --- a/packages/snap-client/src/Client/transforms/searchResponse.ts +++ b/packages/snap-client/src/Client/transforms/searchResponse.ts @@ -9,15 +9,18 @@ import { SearchResponseModelSearchMatchTypeEnum, SearchResponseModelMerchandising, SearchResponseModelResultBadges, -} from '@searchspring/snapi-types'; + SearchResponseModelResultVariants, +} from '@athoscommerce/snapi-types'; -// TODO: Add all core fields const CORE_FIELDS = [ 'uid', 'sku', + 'available', 'name', 'url', 'addToCartUrl', + 'parentId', + 'parentImageUrl', 'price', 'msrp', 'imageUrl', @@ -41,7 +44,9 @@ type sortingOption = { }; type rawResult = { + available: string; badges?: SearchResponseModelResultBadges[]; + variants?: SearchResponseModelResultVariants; brand?: string; collection_handle?: string[]; collection_id?: string[]; @@ -52,6 +57,8 @@ type rawResult = { intellisuggestSignature?: string; msrp?: string; name: string; + parentId: string; + parentImageUrl: string; price: string; product_type?: string[]; product_type_unigram?: string; @@ -199,7 +206,7 @@ transformSearchResponse.results = (response: searchResponseType) => { return { results: results.map(transformSearchResponse.result) }; }; -transformSearchResponse.result = (rawResult: rawResult, idx: number): SearchResponseModelResult => { +transformSearchResponse.result = (rawResult: rawResult): SearchResponseModelResult => { const coreFieldValues: SearchResponseModelResultCoreMappings = CORE_FIELDS.reduce((coreFields, key) => { if (typeof rawResult[key as keyof rawResult] != 'undefined') { return { @@ -212,11 +219,16 @@ transformSearchResponse.result = (rawResult: rawResult, idx: number): SearchResp if (coreFieldValues.price) coreFieldValues.price = +coreFieldValues.price; if (coreFieldValues.msrp) coreFieldValues.msrp = +coreFieldValues.msrp; - + if (coreFieldValues.available?.toString() === 'true') { + coreFieldValues.available = true; + } else if (coreFieldValues.available?.toString() === 'false') { + coreFieldValues.available = false; + } const attributes = Object.keys(rawResult) .filter((k) => CORE_FIELDS.indexOf(k) == -1) // remove 'badges' from attributes - but only if it is an object .filter((k) => !(k == 'badges' && Array.isArray(rawResult[k]) && typeof rawResult[k]?.[0] == 'object')) + .filter((k) => !(k == 'variants')) .reduce((attributes, key) => { return { ...attributes, @@ -224,29 +236,14 @@ transformSearchResponse.result = (rawResult: rawResult, idx: number): SearchResp }; }, {}); - const children = - rawResult?.children?.map((child) => { - return { - attributes: { - ...Object.keys(child).reduce((attributes, key) => { - return { - ...attributes, - [key]: decodeProperty(child[key]), - }; - }, {}), - }, - }; - }) || []; - return new Result({ id: rawResult.uid, - position: idx + 1, mappings: { core: coreFieldValues, }, attributes, badges: Array.isArray(rawResult.badges) && typeof rawResult.badges[0] == 'object' ? rawResult.badges : [], - children, + variants: rawResult.variants, }); }; @@ -462,7 +459,7 @@ transformSearchResponse.search = (response: searchResponseType, request: SearchR }; // used for HTML entities decoding -function decodeProperty(encoded: string | string[] | SearchResponseModelResultBadges[]) { +function decodeProperty(encoded: string | string[] | SearchResponseModelResultBadges[] | SearchResponseModelResultVariants) { if (Array.isArray(encoded)) { return encoded.map((item) => { if (typeof item === 'string') { diff --git a/packages/snap-client/src/types.ts b/packages/snap-client/src/types.ts index f730ba41a..46117a475 100644 --- a/packages/snap-client/src/types.ts +++ b/packages/snap-client/src/types.ts @@ -5,7 +5,8 @@ import type { SearchRequestModel, AutocompleteRequestModel, MetaResponseModel, -} from '@searchspring/snapi-types'; +} from '@athoscommerce/snapi-types'; +import { NO_BEACON_PARAM } from './Client/transforms'; export type HTTPHeaders = { [key: string]: string }; @@ -149,6 +150,7 @@ export type RecommendPostRequestModel = { withRecInfo?: boolean; blockedItems?: string[]; filters?: RecommendPostRequestFiltersModel[]; + [NO_BEACON_PARAM]?: boolean; }; export type RecommendPostRequestProfileModel = { diff --git a/packages/snap-controller/CHANGELOG.md b/packages/snap-controller/CHANGELOG.md index c9e7f2c6b..4df5647ac 100644 --- a/packages/snap-controller/CHANGELOG.md +++ b/packages/snap-controller/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0](https://github.com/searchspring/snap/compare/v1.10.1...v1.11.0) (2026-01-15) + +### Bug Fixes + +- add noBeacon param to recommend api, update beacon and snapi-types version ([89e185c](https://github.com/searchspring/snap/commit/89e185c1b06a49274103c50d3bf410e2effa1e3a)) +- **autocomplete:** prevent impressing results when subsequent queries are the same ([2a154b5](https://github.com/searchspring/snap/commit/2a154b5fdecd3d56fbcd122205e16a9edf50b5ec)) +- **controller/search:** adding 'brand' and 'manufacturer' to the list of known background filters ([273e5e4](https://github.com/searchspring/snap/commit/273e5e482078a75796ac430e87b9aa30a83c1d9a)) +- **controller/search:** changing when 'lastStringyParams' is cleared and ensuring persisted pageshow ([7148731](https://github.com/searchspring/snap/commit/71487319cb130af204b833d0ab097694914dd100)) +- **controller/search:** quick fix on setting name for hierarchy ([9f336ea](https://github.com/searchspring/snap/commit/9f336eaf08d6eb5f6bfe764fcd57f1bd24c4cf30)) + +### Features + +- **searchcontroller:** adding the ability to show hierarchy filters in the filterSummary ([43c3890](https://github.com/searchspring/snap/commit/43c3890db4f6e6b2ec5edc7f45fe2069a6f4ce20)) + ## [1.10.1](https://github.com/searchspring/snap/compare/v1.10.0...v1.10.1) (2025-10-06) **Note:** Version bump only for package @searchspring/snap-controller @@ -163,6 +177,57 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **preact-components/storybook:** updating storybook to latest version - build stable ([43c598d](https://github.com/searchspring/snap/commit/43c598d0e4d11d76364ff2775ecdcabe489023a8)) +## [0.72.1](https://github.com/searchspring/snap/compare/v0.72.0...v0.72.1) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-controller + +# [0.72.0](https://github.com/searchspring/snap/compare/v0.71.0...v0.72.0) (2026-01-14) + +**Note:** Version bump only for package @searchspring/snap-controller + +# [0.71.0](https://github.com/searchspring/snap/compare/v0.70.1...v0.71.0) (2025-11-26) + +### Bug Fixes + +- **autocomplete:** prevent impressing results when subsequent queries are the same ([2a154b5](https://github.com/searchspring/snap/commit/2a154b5fdecd3d56fbcd122205e16a9edf50b5ec)) +- **controller/search:** changing when 'lastStringyParams' is cleared and ensuring persisted pageshow ([7148731](https://github.com/searchspring/snap/commit/71487319cb130af204b833d0ab097694914dd100)) + +## [0.70.1](https://github.com/searchspring/snap/compare/v0.70.0...v0.70.1) (2025-11-17) + +**Note:** Version bump only for package @searchspring/snap-controller + +# [0.70.0](https://github.com/searchspring/snap/compare/v0.69.2...v0.70.0) (2025-11-13) + +**Note:** Version bump only for package @searchspring/snap-controller + +## [0.69.2](https://github.com/searchspring/snap/compare/v0.69.1...v0.69.2) (2025-11-04) + +**Note:** Version bump only for package @searchspring/snap-controller + +## [0.69.1](https://github.com/searchspring/snap/compare/v0.69.0...v0.69.1) (2025-10-23) + +**Note:** Version bump only for package @searchspring/snap-controller + +# [0.69.0](https://github.com/searchspring/snap/compare/v0.68.0...v0.69.0) (2025-10-16) + +### Bug Fixes + +- **controller/search:** adding 'brand' and 'manufacturer' to the list of known background filters ([273e5e4](https://github.com/searchspring/snap/commit/273e5e482078a75796ac430e87b9aa30a83c1d9a)) + +### Features + +- **searchcontroller:** adding the ability to show hierarchy filters in the filterSummary ([43c3890](https://github.com/searchspring/snap/commit/43c3890db4f6e6b2ec5edc7f45fe2069a6f4ce20)) + +# [0.68.0](https://github.com/searchspring/snap/compare/v0.67.5...v0.68.0) (2025-08-18) + +### Bug Fixes + +- add noBeacon param to recommend api, update beacon and snapi-types version ([89e185c](https://github.com/searchspring/snap/commit/89e185c1b06a49274103c50d3bf410e2effa1e3a)) + +## [0.67.5](https://github.com/searchspring/snap/compare/v0.67.4...v0.67.5) (2025-08-11) + +**Note:** Version bump only for package @searchspring/snap-controller + ## [0.67.4](https://github.com/searchspring/snap/compare/v0.67.3...v0.67.4) (2025-07-29) ### Bug Fixes diff --git a/packages/snap-controller/README.md b/packages/snap-controller/README.md index 4b27bc1be..72758bd13 100644 --- a/packages/snap-controller/README.md +++ b/packages/snap-controller/README.md @@ -1,76 +1,16 @@ # Snap Controller -NPM Status - The heart of controlling Search, Autocomplete, & Finder functionality. The Controller is responsible for tying various Snap services together. +Although `@searchspring/snap-controller` is published as a standalone package, it is not intended to be used directly. Internally it is a dependency of the `@searchspring/snap-preact` package. -## Dependencies - -Snap Controller is a top-level package that requires the following dependencies as services: - -NPM Status [@searchspring/snap-client](https://github.com/searchspring/snap/tree/main/packages/snap-client) - -NPM Status [@searchspring/snap-store-mobx](https://github.com/searchspring/snap/tree/main/packages/snap-store-mobx) - -NPM Status [@searchspring/snap-url-manager](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager) - -NPM Status [@searchspring/snap-event-manager](https://github.com/searchspring/snap/tree/main/packages/snap-event-manager) - -NPM Status [@searchspring/snap-profiler](https://github.com/searchspring/snap/tree/main/packages/snap-profiler) - -NPM Status [@searchspring/snap-logger](https://github.com/searchspring/snap/tree/main/packages/snap-logger) - -## Installation - -To install the `snap-controller` package and it's services: - -```bash -npm install --save @searchspring/snap-controller @searchspring/snap-client @searchspring/snap-store-mobx @searchspring/snap-url-manager @searchspring/snap-event-manager @searchspring/snap-profiler @searchspring/snap-logger -``` - - -## Instantiation -Each `Controller` must be passed a configuration object as the first parameter to the constructor, and a services object (dependencies) as the second. The contents of these objects will depend on which type of `Controller` is being instantiated. For example, a `SearchController` would usually be paired with a `SearchStore` service, and would take a `SearchControllerConfig` configuration object. - -The complete example below shows how a `SearchController` could be instatiated, initialized and searched: - -```typescript -import { Client } from '@searchspring/snap-client'; -import { SearchStore } from '@searchspring/snap-store-mobx'; -import { UrlManager, UrlTranslator } from '@searchspring/snap-url-manager'; -import { EventManager } from '@searchspring/snap-event-manager'; -import { Profiler } from '@searchspring/snap-profiler'; -import { Logger } from '@searchspring/snap-logger'; -import { Tracker } from '@searchspring/snap-tracker'; -import { SearchController } from '@searchspring/snap-controller'; - -const configuration = { - id: 'search' -}; - -const urlManager = new UrlManager(new UrlTranslator()); -const services = { - client: new Client({ siteId: 'abc123' }), - store: new SearchStore(configuration, { urlManager }), - urlManager, - eventManager: new EventManager(), - profiler: new Profiler(), - logger: new Logger(), - tracker: new Tracker(), -} - -const controller = new SearchController(configuration, services); -controller.init(); -controller.search(); -``` ## Configuration The configuration object provided during instantiation provides a way of configuring the controller for different behavior. Each controller type (`SearchController`, `AutocompleteController`, `FinderController`, etc...) has default configurations that can be modified with the instantiation configuration object. At minimum an `id` attribute is required for identifying controllers. The `id` should be unique to each *instance* of a controller. ## Services Along with a configuration, each controller is passed a collection of services during instantiation. These services are then used by the controller and made available via controller methods. Sometimes controllers might share a reference to a service (the `client` service for example), but in most cases a controller will have it's own instance of a service. Some services (like the `SearchStore`) share services with the controller (in the example above, the `UrlManager` is shared). -```typescript +```js { client, store, urlManager, eventManager, profiler, logger } ``` ### client @@ -94,7 +34,7 @@ The `logger` service provides logging functionality to a controller. Each contro Each Controller can optionally take a 3rd parameter for `Context`. This is to allow each individual controller to have its own individual context if so desired. The context is exposed as `controller.context` -```typescript +```js controller.context; ``` @@ -102,7 +42,7 @@ controller.context; ## Initialization Invoking the `init` method is required to subscribe to changes that occur in the UrlManager. It also fires the `init` event which executes attached middleware. This can be fired manually as needed; if it was not manually fired it will happen automatically on the first call to the controller `search` method. -```typescript +```js controller.init(); ``` @@ -111,7 +51,7 @@ The `search` method of a controller will run the search that is expected by leve Most controllers will provide a means of manipulating the request and response using `beforeSearch` and `afterSearch` events respectively. Read on for details about events. -```typescript +```js controller.search(); ``` @@ -121,7 +61,7 @@ Different controller types will utilize different Snap Stores (typically of the ## Events Each controller will fire various events. Some of the event names are shared between controllers for consistency (ex: `beforeSearch`, `afterSearch`, `afterStore`); however the attaching of middleware and execution of it must remain separate. This is why a new `EventManager` instance is created for each controller. Middleware are attached to events via the `on` method and the functions should almost always end with `await next()` unless purposefully preventing the next attached middleware from executing. -```typescript +```js controller.on('init', async (eventData, next) => { const { controller } = eventData; @@ -135,7 +75,7 @@ Note: Groups of middleware (plugins) can be attached using the `plugin` method. The data available within a middleware (first parameter) is determined by what gets passed into the `fire` method. For existing events on the controller, the `fire` method is already being called when appropriate to the event, and the `eventData` will typically be an object containing a reference to the controller and any other details that may be of importance to the particular event. Custom events can be created as needed; but keep in mind that any middleware tied to the event should be bound (using `on` or `plugin`) prior to the execution of the `fire` method. -```typescript +```js controller.eventManager.fire('customEventName', { thing1: 'one', thing2: 2 }); ``` @@ -146,13 +86,13 @@ A controller's environment is initialized at build time, and is used to control ## Logging The logger provides a clear way of outputting details like profile data or errors to the developer console. A `production` build will supress most logs while a `development` build will show them all. The environment is automatically determined, but can be toggled during runtime by setting it to either `development` or `production`. -```typescript +```js controller.environment = 'development'; ``` The use of `console.log()` is discouraged. Logging should be done via controller instance to help debug and navigate the sea of console logs. Each controller will output the `id` for easily deciphering which controller made the log. -```typescript +```js controller.log.warn('THIS IS A WARNING!'); ``` diff --git a/packages/snap-controller/package.json b/packages/snap-controller/package.json index f3625aaf8..623beb0bb 100644 --- a/packages/snap-controller/package.json +++ b/packages/snap-controller/package.json @@ -1,6 +1,6 @@ { "name": "@searchspring/snap-controller", - "version": "1.10.1", + "version": "1.11.0", "description": "Snap Controllers", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", @@ -11,7 +11,7 @@ "access": "public" }, "scripts": { - "build": "rm -rf ./dist && tsc && tsc -p tsconfig.cjs.json", + "build": "rm -rf ./dist && rm -fr ./components/dist && tsc & p1=$!; tsc -p tsconfig.cjs.json & p2=$!; wait $p1 && wait $p2", "build:docs": "typedoc --out docs src/index.ts", "dev": "tsc --watch", "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'", @@ -20,18 +20,18 @@ "test:watch": "jest --watch" }, "dependencies": { - "@searchspring/snap-toolbox": "1.10.1", + "@searchspring/snap-toolbox": "1.11.0", "css.escape": "1.5.1", "deepmerge": "4.3.1" }, "devDependencies": { - "@searchspring/snap-client": "1.10.1", - "@searchspring/snap-event-manager": "1.10.1", - "@searchspring/snap-logger": "1.10.1", - "@searchspring/snap-profiler": "1.10.1", - "@searchspring/snap-store-mobx": "1.10.1", - "@searchspring/snap-tracker": "1.10.1", - "@searchspring/snap-url-manager": "1.10.1" + "@searchspring/snap-client": "1.11.0", + "@searchspring/snap-event-manager": "1.11.0", + "@searchspring/snap-logger": "1.11.0", + "@searchspring/snap-profiler": "1.11.0", + "@searchspring/snap-store-mobx": "1.11.0", + "@searchspring/snap-tracker": "1.11.0", + "@searchspring/snap-url-manager": "1.11.0" }, "sideEffects": false, "files": [ diff --git a/packages/snap-controller/src/Abstract/README.md b/packages/snap-controller/src/Abstract/README.md index 44f0f37b1..8c11053ea 100644 --- a/packages/snap-controller/src/Abstract/README.md +++ b/packages/snap-controller/src/Abstract/README.md @@ -11,7 +11,7 @@ The required configuration for all controllers is an `id`. This identifier shoul
-```typescript +```js const abstractConfig = { id: 'abstract', }; @@ -49,7 +49,7 @@ This is an abstract method that must be defined in the subclass. ## on This method is used to attach event middleware. Each controller defines it's own events, this method provides a means to attach to them. -```typescript +```js controller.on('init', async(eventData, next) => { eventData.controller.log.debug('initialized!'); await next(); @@ -59,7 +59,7 @@ controller.on('init', async(eventData, next) => { ## plugin Modification or extension of functionality as well as attaching groups of event middleware can be done using the `plugin` method. Plugin functions can be passed additional parameters if needed. -```typescript +```js const paramPlugin = (controller, ...params) => { // params = [ 'param1', 'param2' ] controller.on('init', async({ controller }, next) => { diff --git a/packages/snap-controller/src/Autocomplete/AutocompleteController.ts b/packages/snap-controller/src/Autocomplete/AutocompleteController.ts index d19b62af1..319b14eb2 100644 --- a/packages/snap-controller/src/Autocomplete/AutocompleteController.ts +++ b/packages/snap-controller/src/Autocomplete/AutocompleteController.ts @@ -6,9 +6,9 @@ import { getSearchParams } from '../utils/getParams'; import { ControllerTypes } from '../types'; import { AutocompleteStore } from '@searchspring/snap-store-mobx'; -import type { AutocompleteControllerConfig, AfterSearchObj, AfterStoreObj, ControllerServices, ContextVariables } from '../types'; +import type { AutocompleteControllerConfig, AutocompleteAfterSearchObj, AfterStoreObj, ControllerServices, ContextVariables } from '../types'; import type { Next } from '@searchspring/snap-event-manager'; -import type { AutocompleteRequestModel, SearchRequestModelFilterRange, SearchRequestModelFilterValue } from '@searchspring/snapi-types'; +import type { AutocompleteRequestModel, SearchRequestModelFilterRange, SearchRequestModelFilterValue } from '@athoscommerce/snapi-types'; import { type AutocompleteAddtocartSchemaData, type AutocompleteRedirectSchemaData, @@ -70,6 +70,7 @@ export class AutocompleteController extends AbstractController { declare store: AutocompleteStore; declare config: AutocompleteControllerConfig; public storage: StorageStore; + private lastSearchQuery: string | undefined; events: { product: Record< @@ -139,27 +140,31 @@ export class AutocompleteController extends AbstractController { const controller = search.controller as AutocompleteController; if (controller.store.loaded && !controller.store.error) { const products = controller.store.results.filter((result) => result.type === 'product') as Product[]; - const results = products.length === 0 ? [] : products; - const data = getAutocompleteSchemaData({ params: search.request, store: this.store, results }); - if (!search.response._cached) { + + if (products.length === 0 && !search.response._cached) { + // handle no results + const data = getAutocompleteSchemaData({ params: search.request, store: this.store }); + this.eventManager.fire('track.product.render', { controller: this, trackEvent: data }); this.tracker.events.autocomplete.render({ data, siteId: this.config.globals?.siteId }); } + products.forEach((result) => { - this.events.product[result.id] = this.events.product[result.id] || {}; - this.events.product[result.id].render = true; if (!search.response._cached) { - this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); + this.track.product.render(result); + } else { + this.events.product[result.id] = this.events.product[result.id] || {}; + this.events.product[result.id].render = true; } }); } }); // add 'afterSearch' middleware - this.eventManager.on('afterSearch', async (ac: AfterSearchObj, next: Next): Promise => { + this.eventManager.on('afterSearch', async (ac: AutocompleteAfterSearchObj, next: Next): Promise => { await next(); // cancel search if no input or query doesn't match current urlState - if (ac.response.search.autocomplete.query != ac.controller.urlManager.state.query) { + if (ac.response.search.autocomplete?.query != ac.controller.urlManager.state.query) { return false; } }); @@ -200,10 +205,10 @@ export class AutocompleteController extends AbstractController { return; } const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] }); + this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data }); this.tracker.events.autocomplete.clickThrough({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].clickThrough = true; - this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data }); }, click: (e: MouseEvent, result): void => { if (this.events.product[result.id]?.click) { @@ -226,30 +231,30 @@ export class AutocompleteController extends AbstractController { if (this.events.product[result.id]?.render) return; const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: result ? [result] : [] }); + this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); this.tracker.events.autocomplete.render({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].render = true; - this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); }, impression: (result: Product): void => { if (this.events.product[result.id]?.impression || !this.events.product[result.id]?.render) return; const data = getAutocompleteSchemaData({ params: this.params, store: this.store, results: [result] }); + this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data }); this.tracker.events.autocomplete.impression({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].impression = true; - this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data }); }, addToCart: (result: Product): void => { const data = getAutocompleteAddtocartSchemaData({ params: this.params, store: this.store, results: [result] }); - this.tracker.events.autocomplete.addToCart({ data, siteId: this.config.globals?.siteId }); this.eventManager.fire('track.product.addToCart', { controller: this, product: result, trackEvent: data }); + this.tracker.events.autocomplete.addToCart({ data, siteId: this.config.globals?.siteId }); }, }, redirect: (redirectURL: string): void => { const data = getAutocompleteRedirectSchemaData({ redirectURL }); - this.tracker.events.autocomplete.redirect({ data, siteId: this.config.globals?.siteId }); this.eventManager.fire('track.redirect', { controller: this, redirectURL, trackEvent: data }); + this.tracker.events.autocomplete.redirect({ data, siteId: this.config.globals?.siteId }); }, }; @@ -704,12 +709,24 @@ export class AutocompleteController extends AbstractController { const searchProfile = this.profiler.create({ type: 'event', name: 'search', context: params }).start(); - this.events = { product: {} }; const { meta, search } = await this.client.autocomplete(params); searchProfile.stop(); this.log.profile(searchProfile); + if (search?.search?.query === this.lastSearchQuery) { + const impressedResultIds = Object.keys(this.events.product).filter((resultId) => this.events.product[resultId]?.impression); + this.events = { + product: impressedResultIds.reduce>((acc, resultId) => { + acc[resultId] = { impression: true }; + return acc; + }, {}), + }; + } else { + this.events = { product: {} }; + this.lastSearchQuery = search?.search?.query; + } + const afterSearchProfile = this.profiler.create({ type: 'event', name: 'afterSearch', context: params }).start(); try { @@ -916,7 +933,7 @@ function getAutocompleteAddtocartSchemaData({ results?.map((result: Product): BeaconProduct => { const core = (result as Product).mappings.core!; return { - uid: core.uid || '', + uid: core.parentId || core.uid || '', sku: core.sku, price: Number(core.price), qty: result.quantity || 1, @@ -999,7 +1016,7 @@ function getAutocompleteSchemaData({ return { type: ItemTypeEnum.Product, position, - uid: core.uid || '', + uid: core.parentId || core?.uid || '', sku: core.sku, }; }) || [], diff --git a/packages/snap-controller/src/Autocomplete/README.md b/packages/snap-controller/src/Autocomplete/README.md index c1bb203e2..84a22aef5 100644 --- a/packages/snap-controller/src/Autocomplete/README.md +++ b/packages/snap-controller/src/Autocomplete/README.md @@ -25,52 +25,16 @@ The `AutocompleteController` is used when making queries to the API `autocomplet | settings.bind.input | boolean to disable binding of the input element (selector) | true | | | settings.bind.submit | boolean to disable binding of the submit event (form submission of enter key press) | true | | | settings.variants.field | used to set the field in which to grab the variant data from | ➖ | | +| settings.variants.showDisabledSelectionValues | determines if completely out of stock (disabled) options should appear in variant selections | false | | | settings.variants.realtime.enabled | enable real time variant updates | ➖ | | | settings.variants.realtime.filters | specify which filters to use to determine which results are updated | ➖ | | | settings.variants.options | object keyed by individual option field values for configuration of any option settings | ➖ | | -
- -```typescript -const autocompleteConfig = { - id: 'autocomplete', - selector: '#searchInput', - settings: { - syncInputs: true - }, -}; -``` - -## Instantiate -`AutocompleteController` requires an `AutocompleteControllerConfig` and `ControllerServices` object and is paired with an `AutocompleteStore`. The `AutocompleteStore` takes the same config, and shares the `UrlManager` service with the controller. - -```typescript -import { AutocompleteController } from '@searchspring/snap-controller'; -import { Client } from '@searchspring/snap-client'; -import { AutocompleteStore } from '@searchspring/snap-store-mobx'; -import { UrlManager, UrlTranslator, reactLinker } from '@searchspring/snap-url-manager'; -import { EventManager } from '@searchspring/snap-event-manager'; -import { Profiler } from '@searchspring/snap-profiler'; -import { Logger } from '@searchspring/snap-logger'; -import { Tracker } from '@searchspring/snap-tracker'; - -const autocompleteUrlManager = new UrlManager(new UrlTranslator(), reactLinker).detach(); -const autocompleteController = new AutocompleteController(autocompleteConfig, { - client: new Client(globals, clientConfig), - store: new AutocompleteStore(autocompleteConfig, { urlManager: autocompleteUrlManager }), - urlManager: autocompleteUrlManager, - eventManager: new EventManager(), - profiler: new Profiler(), - logger: new Logger(), - tracker: new Tracker(), - } -)); -``` ## Initialize Invoking the `init` method is required to subscribe to changes that occur in the UrlManager. This is typically done automatically prior to calling the first `search`. -```typescript +```js autocompleteController.init(); ``` @@ -78,7 +42,7 @@ autocompleteController.init(); Invoking the `bind` method is required to attach event listeners to each input specified as `selector` in the `AutocompleteControllerConfig`. -```typescript +```js autocompleteController.bind(); ``` @@ -86,22 +50,22 @@ autocompleteController.bind(); Invoking the `unbind` method will remove attached event listeners previously attached by the `bind` method. -```typescript +```js autocompleteController.unbind(); ``` ## Search This will invoke a search request to Searchspring's search API and populate the store with the response. This should be automatically called by the DOM event binding that occurs when the `bind` method (see above) is invoked. -```typescript +```js autocompleteController.search(); ``` ## AddToCart This will invoke an addToCart event (see below). Takes an array of Products as a parameter. -```typescript -autocompleteController.addToCart(products); +```js +autocompleteController.addToCart([autocompleteController.store.results[0]]); ``` ## Events @@ -161,4 +125,4 @@ autocompleteController.addToCart(products); - Always invoked after `addToCart()` method has been invoked ## Variants -For variant integration details, see [Variant Integration Docs](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_VARIANTS.md) \ No newline at end of file +For variant integration details, see [Variant Integration Docs](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_VARIANTS.md) diff --git a/packages/snap-controller/src/Finder/FinderController.ts b/packages/snap-controller/src/Finder/FinderController.ts index 09b2b0ac7..e2bec9acf 100644 --- a/packages/snap-controller/src/Finder/FinderController.ts +++ b/packages/snap-controller/src/Finder/FinderController.ts @@ -7,7 +7,7 @@ import { getSearchParams } from '../utils/getParams'; import { ControllerTypes } from '../types'; import type { FinderStore } from '@searchspring/snap-store-mobx'; import type { Next } from '@searchspring/snap-event-manager'; -import type { FinderControllerConfig, AfterSearchObj, ControllerServices, ContextVariables } from '../types'; +import type { FinderControllerConfig, FinderAfterSearchObj, ControllerServices, ContextVariables } from '../types'; const defaultConfig: FinderControllerConfig = { id: 'finder', @@ -51,7 +51,7 @@ export class FinderController extends AbstractController { } // TODO: remove this aftersearch when store interface changes - this.eventManager.on('afterSearch', async (finder: AfterSearchObj, next: Next): Promise => { + this.eventManager.on('afterSearch', async (finder: FinderAfterSearchObj, next: Next): Promise => { await next(); finder.controller.store.loading = false; diff --git a/packages/snap-controller/src/Finder/README.md b/packages/snap-controller/src/Finder/README.md index 93f24d053..50cdf5dfa 100644 --- a/packages/snap-controller/src/Finder/README.md +++ b/packages/snap-controller/src/Finder/README.md @@ -19,71 +19,11 @@ The `FinderController` should be used for building product finders. It makes que | persist.lockSelections | boolean to lock selections upon loading persisted selections | true | | | persist.expiration | number of milliseconds before persisted selections are expired | 0 (don't expire) | | -
- -

Hierarchy Config

-Specifying `levels` will display a dropdown for each hierarchy level. Finders that use hierarchy fields will enforce selecting dropdowns in order by disabling the following dropdowns - -```typescript -const finderConfig = { - id: 'finder', - url: '/search', - fields: [ - { - field: 'ss_tire', - label: 'Wheel Finder', - levels: ['Year', 'Make', 'Model', 'Wheel Size'] - }, - ] -}; -``` - -Optionally if `levels` are not defined, a single dropdown will be displayed on the initial load. Each selection will dynamically append additional dropdowns until there are no more available selections - -```typescript -const finderConfig = { - id: 'finder', - url: '/search', - fields: [ - { - field: 'ss_tire' - } - ] -}; -``` - -

Non-Hierarchy Config

-If using fields that are not of hierarchy type, `levels` are not required - -```typescript -const finderConfig = { - id: 'finder', - url: '/search', - fields: [ - { - field: 'custom_wheel_size' - label: 'Size' - }, - { - field: 'custom_wheel_width' - label: 'Width' - }, - { - field: 'custom_wheel_bolt_pattern' - label: 'Bolt Pattern' - }, - { - field: 'custom_color' - label: 'Color' - } - ] -}; -``` ### Persisting Selections Persisting selections allow for finder selections to be persisted across page navigation. If enabled, selections are saved to the browser's local storage upon invoking the controller's `find` method. Selections are then populated automatically upon the next instantiation. (Default: `false`) -```typescript +```js const finderConfig = { id: 'finder', url: '/search', @@ -97,7 +37,7 @@ const finderConfig = { #### expiration Persisted selections will reset automatically if the `persist.expiration` duration has elapsed (milliseconds), or the `config` itself has changed over time. (Default: `0`, never expires) -```typescript +```js const finderConfig = { id: 'finder', url: '/search', @@ -112,7 +52,7 @@ const finderConfig = { #### lockSelections The `config.persist.lockSelections` option will disable selections from being changed after they have been persisted. This forces the user to reset the selections by invoking the controller's `reset` method and making new selections. (Default: `true`) -```typescript +```js const finderConfig = { id: 'finder', url: '/search', @@ -125,56 +65,31 @@ const finderConfig = { ``` -## Instantiate -`FinderController` requires a `FinderControllerConfig` and `ControllerServices` object and is paired with a `FinderStore`. The `FinderStore` takes the same config, and shares the `UrlManager` service with the controller. - -```typescript -import { FinderController } from '@searchspring/snap-controller'; -import { Client } from '@searchspring/snap-client'; -import { FinderStore } from '@searchspring/snap-store-mobx'; -import { UrlManager, UrlTranslator, reactLinker } from '@searchspring/snap-url-manager'; -import { EventManager } from '@searchspring/snap-event-manager'; -import { Profiler } from '@searchspring/snap-profiler'; -import { Logger } from '@searchspring/snap-logger'; -import { Tracker } from '@searchspring/snap-tracker'; - -const finderUrlManager = new UrlManager(new UrlTranslator(), reactLinker).detach(0); -const finderController = new FinderController(finderConfig, { - client: new Client(globals, clientConfig), - store: new FinderStore(finderConfig, { urlManager: finderUrlManager }), - urlManager: finderUrlManager, - eventManager: new EventManager(), - profiler: new Profiler(), - logger: new Logger(), - tracker: new Tracker(), - } -)); -``` ## Initialize Invoking the `init` method is required to subscribe to changes that occur in the UrlManager. This is typically done automatically prior to calling the first `search`. -```typescript +```js finderController.init(); ``` ## Search This will invoke a search request to Searchspring's search API and populate the store with the response. This should be called initially and after finder selections have been made. -```typescript +```js finderController.search(); ``` ## Find After selection(s) have been made, the user will click on a 'Find' button. This click event should invoke the `find` method of the `FinderController` which will redirect to the specified `url` in the config, along with its selection data. -```typescript +```js finderController.find(); ``` ## Reset This mthod should be invoked to 'reset' or remove all finder selections. This will also clear out any persisted selection storage data. -```typescript +```js finderController.reset(); ``` diff --git a/packages/snap-controller/src/Recommendation/README.md b/packages/snap-controller/src/Recommendation/README.md index 55e732c2c..f470496da 100644 --- a/packages/snap-controller/src/Recommendation/README.md +++ b/packages/snap-controller/src/Recommendation/README.md @@ -8,62 +8,37 @@ The `RecommendationController` is used when making queries to the API `recommend |---|---|:---:|:---:| | id | unique identifier for this controller | ➖ | ✔️ | | tag | unique name of the recommendations profile | ➖ | ✔️ | -| realtime | update recommendations if cart contents change (requires [cart attribute tracking](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_TRACKING.md)) | ➖ | | +| realtime | update recommendations if cart contents change (requires [cart attribute tracking](https://github.com/searchspring/snap/tree/main/docs/SNAP_TRACKING.md#cart-attribute-tracking)) | ➖ | | | batched | batch multiple recommendations into a single network request | true | | | limit | maximum number of results to display, can also be set globally via globals | 20 | | | globals | keys defined here will be passed to the API request (can overwrite global config)| ➖ | | | settings.searchOnPageShow | causes a search to be conducted when returning using browser back/forward cache | true | | | settings.variants.field | used to set the field in which to grab the variant data from | ➖ | | +| settings.variants.showDisabledSelectionValues | determines if completely out of stock (disabled) options should appear in variant selections | false | | | settings.variants.realtime.enabled | enable real time variant updates | ➖ | | | settings.variants.realtime.filters | specify which filters to use to determine which results are updated | ➖ | | | settings.variants.options | object keyed by individual option field values for configuration of any option settings | ➖ | | -
-```typescript -const recommendationConfig = { - id: 'recommend', - tag: 'trending', -}; -``` - -## Instantiate -`RecommendationController` requires an `RecommendationControllerConfig` and `ControllerServices` object and is paired with an `RecommendationStore`. The `RecommendationStore` takes the same config, and shares the `UrlManager` service with the controller. - -```typescript -import { RecommendationController } from '@searchspring/snap-controller'; -import { Client } from '@searchspring/snap-client'; -import { RecommendationStore } from '@searchspring/snap-store-mobx'; -import { UrlManager, UrlTranslator, reactLinker } from '@searchspring/snap-url-manager'; -import { EventManager } from '@searchspring/snap-event-manager'; -import { Profiler } from '@searchspring/snap-profiler'; -import { Logger } from '@searchspring/snap-logger'; -import { Tracker } from '@searchspring/snap-tracker'; - -const recommendationUrlManager = new UrlManager(new UrlTranslator(), reactLinker).detach(0); -const recommendationController = new RecommendationController(recommendationConfig, { - client: new Client(globals, clientConfig), - store: new RecommendationsStore(recommendationConfig, { urlManager: recommendationUrlManager }), - urlManager: recommendationUrlManager, - eventManager: new EventManager(), - profiler: new Profiler(), - logger: new Logger(), - tracker: new Tracker(), - } -)); -``` ## Initialize Invoking the `init` method is required to subscribe to changes that occur in the UrlManager. This is typically done automatically prior to calling the first `search`. -```typescript +```js recommendationController.init(); ``` +## Search +This will invoke a search request to Searchspring's search API and populate the store with the response. + +```js +recommendationController.search(); +``` + ## AddToCart This will invoke an addToCart event (see below). Takes an array of Products as a parameter. -```typescript -recommendationController.addToCart(products); +```js +recommendationController.addToCart([recommendationController.store.results[0]]); ``` ## Events @@ -112,4 +87,4 @@ recommendationController.addToCart(products); - Always invoked after `controller.addToCart()` method has been invoked ## Variants -For variant integration details, see [Variant Integration Docs](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_VARIANTS.md) \ No newline at end of file +For variant integration details, see [Variant Integration Docs](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_VARIANTS.md) diff --git a/packages/snap-controller/src/Recommendation/RecommendationController.test.ts b/packages/snap-controller/src/Recommendation/RecommendationController.test.ts index 9ba1ebeb5..5ab8a06c0 100644 --- a/packages/snap-controller/src/Recommendation/RecommendationController.test.ts +++ b/packages/snap-controller/src/Recommendation/RecommendationController.test.ts @@ -292,7 +292,7 @@ describe('Recommendation Controller', () => { await controller.search(); - expect(trackfn).toHaveBeenCalledTimes(1); + expect(trackfn).toHaveBeenCalledTimes(20); trackfn.mockClear(); }); diff --git a/packages/snap-controller/src/Recommendation/RecommendationController.ts b/packages/snap-controller/src/Recommendation/RecommendationController.ts index 9396793ee..c576e076e 100644 --- a/packages/snap-controller/src/Recommendation/RecommendationController.ts +++ b/packages/snap-controller/src/Recommendation/RecommendationController.ts @@ -83,16 +83,20 @@ export class RecommendationController extends AbstractController { const controller = search.controller as RecommendationController; if (controller.store.loaded && !controller.store.error) { const products = controller.store.results.filter((result) => result.type === 'product') as Product[]; - const results = products.length === 0 ? [] : products; - const data = getRecommendationsSchemaData({ store: this.store, results }); - if (!search.response._cached) { + + if (products.length === 0 && !search.response._cached) { + // handle no results + const data = getRecommendationsSchemaData({ store: this.store }); + this.eventManager.fire('track.product.render', { controller: this, trackEvent: data }); this.tracker.events.recommendations.render({ data, siteId: this.config.globals?.siteId }); } + products.forEach((result) => { - this.events.product[result.id] = this.events.product[result.id] || {}; - this.events.product[result.id].render = true; if (!search.response._cached) { - this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); + this.track.product.render(result); + } else { + this.events.product[result.id] = this.events.product[result.id] || {}; + this.events.product[result.id].render = true; } }); } @@ -122,10 +126,10 @@ export class RecommendationController extends AbstractController { if (this.events.product[result.id]?.clickThrough) return; const data = getRecommendationsSchemaData({ store: this.store, results: [result] }); + this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data }); this.tracker.events.recommendations.clickThrough({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].clickThrough = true; - this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data }); }, click: (e: MouseEvent, result): void => { if (this.events.product[result.id]?.click) { @@ -148,26 +152,26 @@ export class RecommendationController extends AbstractController { if (this.events.product[result.id]?.impression || !this.events.product[result.id]?.render) return; const data = getRecommendationsSchemaData({ store: this.store, results: [result] }); + this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data }); this.tracker.events.recommendations.impression({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].impression = true; - this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data }); return data; }, render: (result: Product): RecommendationsSchemaData | undefined => { if (this.events.product[result.id]?.render) return; const data = getRecommendationsSchemaData({ store: this.store, results: [result] }); + this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); this.tracker.events.recommendations.render({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].render = true; - this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); return data; }, addToCart: (result: Product): RecommendationsAddtocartSchemaData | undefined => { const data = getRecommendationsAddtocartSchemaData({ store: this.store, results: [result] }); - this.tracker.events.recommendations.addToCart({ data, siteId: this.config.globals?.siteId }); this.eventManager.fire('track.product.addToCart', { controller: this, product: result, trackEvent: data }); + this.tracker.events.recommendations.addToCart({ data, siteId: this.config.globals?.siteId }); return data; }, }, @@ -353,7 +357,7 @@ function getRecommendationsAddtocartSchemaData({ results?.map((result: Product): BeaconProduct => { const core = (result as Product).mappings.core; return { - uid: core?.uid || '', + uid: core?.parentId || core?.uid || '', sku: core?.sku, price: Number(core?.price), qty: result.quantity || 1, @@ -372,7 +376,7 @@ function getRecommendationsSchemaData({ store, results }: { store: Recommendatio return { type: ItemTypeEnum.Product, position, - uid: core.uid || '', + uid: core.parentId || core?.uid || '', sku: core.sku, }; }) || [], diff --git a/packages/snap-controller/src/Search/README.md b/packages/snap-controller/src/Search/README.md index 49d78aa5b..386021685 100644 --- a/packages/snap-controller/src/Search/README.md +++ b/packages/snap-controller/src/Search/README.md @@ -15,10 +15,16 @@ The `SearchController` is used when making queries to the API `search` endpoint. | settings.facets.trim | facets that do not change results will be removed | true | | | settings.facets.autoOpenActive | setting for "auto open" functionality for facets that are filtered (active), collapsed, and have no stored data | true | | | settings.facets.fields | object keyed by individual facet fields for configuration of any settings.facets options | ➖ | | +| settings.filters.fields | object keyed by individual filter fields for configuration of any settings.filters options | ➖ | | +| settings.filters.hierarchy.enabled | boolean to enable/disable selected hierarchy facets from showing in the filters | false | | +| settings.filters.hierarchy.showFullPath | boolean to show the full hierarchy path in the filter | false | | +| settings.filters.hierarchy.displayDelimiter | string to adjust the delimiter between each level of the full hierarchy path | ' / ' | | +| settings.filters.rangeFormatValue | setting to re-format the value of a range filter using sprintf | false | | | settings.history.max | how many search terms should be kept in the history store | 25 | | | settings.history.url | allows for adjust the root URL for history store terms (default is relative URLs) | ➖ | | | settings.pagination.pageSizeOptions | setting to change the page size options available | ➖ | | | settings.variants.field | used to set the field in which to grab the variant data from | ➖ | | +| settings.variants.showDisabledSelectionValues | determines if completely out of stock (disabled) options should appear in variant selections | false | | | settings.variants.realtime.enabled | enable real time variant updates | ➖ | | | settings.variants.realtime.filters | specify which filters to use to determine which results are updated | ➖ | | | settings.variants.options | object keyed by individual option field values for configuration of any option settings | ➖ | | @@ -27,62 +33,26 @@ The `SearchController` is used when making queries to the API `search` endpoint. | settings.restorePosition.enabled | boolean to enable/disable using `restorePosition` event middleware to restore the window scroll position when navigating back to previous page (when using infinite this is automatically set to true) | false | | | settings.restorePosition.onPageShow | boolean to enable/disable having restorePosition occur on the 'pageshow' window event (requires `restorePosition.enable`) | false | | -
- -```typescript -const searchConfig = { - id: 'search', - globals: { - pagination: { - pageSize: 12 - } - } -}; -``` -## Instantiate -`SearchController` requires a `SearchControllerConfig` and `ControllerServices` object and is paired with a `SearchStore`. The `SearchStore` takes the same config, and shares the `UrlManager` service with the controller. - -```typescript -import { SearchController } from '@searchspring/snap-controller'; -import { Client } from '@searchspring/snap-client'; -import { SearchStore } from '@searchspring/snap-store-mobx'; -import { UrlManager, UrlTranslator, reactLinker } from '@searchspring/snap-url-manager'; -import { EventManager } from '@searchspring/snap-event-manager'; -import { Profiler } from '@searchspring/snap-profiler'; -import { Logger } from '@searchspring/snap-logger'; -import { Tracker } from '@searchspring/snap-tracker'; - -const searchUrlManager = new UrlManager(new UrlTranslator(), reactLinker); -const searchController = new SearchController(searchConfig, { - client: new Client({ siteId: 'abc123' }), - store: new SearchStore(searchConfig, { urlManager: searchUrlManager }), - urlManager: searchUrlManager, - eventManager: new EventManager(), - profiler: new Profiler(), - logger: new Logger(), - tracker: new Tracker(), -}); -``` ## Initialize Invoking the `init` method is required to subscribe to changes that occur in the UrlManager. This is typically done automatically prior to calling the first `search`. -```typescript +```js searchController.init(); ``` ## Search This will invoke a search request to Searchspring's search API and populate the store with the response. -```typescript +```js searchController.search(); ``` ## AddToCart This will invoke an addToCart event (see below). Takes an array of Products as a parameter. -```typescript -searchController.addToCart(products); +```js +searchController.addToCart([searchController.store.results[0]]); ``` ## Search History @@ -93,7 +63,7 @@ When `config.settings.infinite` is defined and `store.pagination.next.url.go({ h If the page has been reloaded, the results will be reset to page 1. -```typescript +```js const searchConfig = { id: 'search', globals: { @@ -110,7 +80,7 @@ const searchConfig = { ### Backfill If `config.settings.infinite.backfill` is specified, any page reloads when paginated up to the specified value will fetch previous pages to backfill. -```typescript +```js const searchConfig = { id: 'search', globals: { @@ -133,7 +103,7 @@ Any time you navigate back to a previous page, this setting will tell the contro When using infinite scroll, it is recommended to specify a value for `config.settings.infinite.backfill` to ensure that when returning to the product listing page, that the product is there to scroll to. -```typescript +```js const searchConfig = { id: 'search', globals: { @@ -167,7 +137,7 @@ The optional `pageSizeOptions` property gives the ability to overwrite the defau `active` - boolean stating if current page size matches the value of this option -```typescript +```js const searchConfig = { id: 'search', settings: { @@ -283,4 +253,4 @@ export class Content extends Component { - Always invoked after `addToCart()` method has been invoked ## Variants -For variant integration details, see [Variant Integration Docs](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_VARIANTS.md) \ No newline at end of file +For variant integration details, see [Variant Integration Docs](https://github.com/searchspring/snap/blob/main/docs/INTEGRATION_VARIANTS.md) diff --git a/packages/snap-controller/src/Search/SearchController.test.ts b/packages/snap-controller/src/Search/SearchController.test.ts index d2f0b34ee..e302b5b4f 100644 --- a/packages/snap-controller/src/Search/SearchController.test.ts +++ b/packages/snap-controller/src/Search/SearchController.test.ts @@ -13,7 +13,7 @@ import { MockClient } from '@searchspring/snap-shared'; import { SearchController, getStorableRequestParams, generateHrefSelector } from './SearchController'; import type { SearchControllerConfig, BeforeSearchObj, RestorePositionObj, ElementPositionObj, AfterSearchObj } from '../types'; -import type { MetaResponseModel, SearchRequestModel, SearchResponseModel } from '@searchspring/snapi-types'; +import type { MetaResponseModel, SearchRequestModel, SearchResponseModel } from '@athoscommerce/snapi-types'; const globals = { siteId: 'ga9kq2' }; @@ -111,6 +111,7 @@ describe('Search Controller', () => { //@ts-ignore delete window.location; + //@ts-ignore window.location = { ...window.location, replace: jest.fn(), @@ -257,8 +258,8 @@ describe('Search Controller', () => { }); controller.on('afterSearch', ({ response }: AfterSearchObj) => { - response.search.results[0].mappings.core.name = 'mutated'; - response.meta.badges = { + response.search.results![0].mappings!.core!.name = 'mutated'; + response.meta!.badges = { locations: { left: [ { @@ -469,6 +470,148 @@ describe('Search Controller', () => { storagefn.mockClear(); }); + it('can set enabled for hierarchy filters', async () => { + const _config: SearchStoreConfig = { + ...searchConfig, + settings: { + ...searchConfig.settings, + filters: { + fields: { + ss_category_hierarchy: { + hierarchy: { + enabled: true, + }, + }, + }, + }, + }, + }; + + const controller = new SearchController(_config, { + client: new MockClient(globals, {}), + store: new SearchStore(_config, services), + urlManager, + eventManager: new EventManager(), + profiler: new Profiler(), + logger: new Logger(), + tracker: new Tracker(globals), + }); + + (controller.client as MockClient).mockData.updateConfig({ search: 'filteredHierarchy', siteId: '8uyt2m' }); + + await controller.search(); + + expect(controller.store.filters).toHaveLength(1); + expect(controller.store.filters[0].label).toBe('Category: Shop by Color'); + }); + + it('can set enabled to false and hide hierarchy filters', async () => { + const _config: SearchStoreConfig = { + ...searchConfig, + settings: { + ...searchConfig.settings, + filters: { + fields: { + ss_category_hierarchy: { + hierarchy: { + enabled: false, + }, + }, + }, + }, + }, + }; + + const controller = new SearchController(_config, { + client: new MockClient(globals, {}), + store: new SearchStore(_config, services), + urlManager, + eventManager: new EventManager(), + profiler: new Profiler(), + logger: new Logger(), + tracker: new Tracker(globals), + }); + + (controller.client as MockClient).mockData.updateConfig({ search: 'filteredHierarchy', siteId: '8uyt2m' }); + + await controller.search(); + + expect(controller.store.filters).toHaveLength(0); + }); + + it('can set enabled to adjust hierarchy filters in summary', async () => { + const _config: SearchStoreConfig = { + ...searchConfig, + settings: { + ...searchConfig.settings, + filters: { + fields: { + ss_category_hierarchy: { + hierarchy: { + enabled: true, + showFullPath: true, + }, + }, + }, + }, + }, + }; + + const controller = new SearchController(_config, { + client: new MockClient(globals, {}), + store: new SearchStore(_config, services), + urlManager, + eventManager: new EventManager(), + profiler: new Profiler(), + logger: new Logger(), + tracker: new Tracker(globals), + }); + + (controller.client as MockClient).mockData.updateConfig({ search: 'filteredHierarchy', siteId: '8uyt2m' }); + + await controller.search(); + + expect(controller.store.filters).toHaveLength(1); + expect(controller.store.filters[0].label).toBe('Category: All Dresses / Shop by Color'); + }); + + it('can set displayDelimiter & showFullPath to adjust hierarchy filters in summary', async () => { + const _config: SearchStoreConfig = { + ...searchConfig, + settings: { + ...searchConfig.settings, + filters: { + fields: { + ss_category_hierarchy: { + hierarchy: { + enabled: true, + displayDelimiter: ' ? ', + showFullPath: true, + }, + }, + }, + }, + }, + }; + + const controller = new SearchController(_config, { + client: new MockClient(globals, {}), + store: new SearchStore(_config, services), + urlManager, + eventManager: new EventManager(), + profiler: new Profiler(), + logger: new Logger(), + tracker: new Tracker(globals), + }); + + (controller.client as MockClient).mockData.updateConfig({ search: 'filteredHierarchy', siteId: '8uyt2m' }); + + await controller.search(); + + expect(controller.store.filters).toHaveLength(1); + expect(controller.store.filters[0].label).toBe('Category: All Dresses ? Shop by Color'); + }); + it('can set personalization cart param', async () => { const controller = new SearchController(searchConfig, { client: new MockClient(globals, {}), diff --git a/packages/snap-controller/src/Search/SearchController.ts b/packages/snap-controller/src/Search/SearchController.ts index 4d698935a..a328b0949 100644 --- a/packages/snap-controller/src/Search/SearchController.ts +++ b/packages/snap-controller/src/Search/SearchController.ts @@ -6,10 +6,10 @@ import { StorageStore, ErrorType } from '@searchspring/snap-store-mobx'; import { getSearchParams } from '../utils/getParams'; import { ControllerTypes, PageContextVariable } from '../types'; -import type { Product, Banner, SearchStore } from '@searchspring/snap-store-mobx'; +import type { Product, Banner, SearchStore, ValueFacet, SearchStoreConfig } from '@searchspring/snap-store-mobx'; import type { SearchControllerConfig, - AfterSearchObj, + SearchAfterSearchObj, AfterStoreObj, ControllerServices, ContextVariables, @@ -18,16 +18,21 @@ import type { BeforeSearchObj, } from '../types'; import type { Next } from '@searchspring/snap-event-manager'; -import type { - SearchRequestModel, - SearchResponseModelResult, - SearchRequestModelSearchRedirectResponseEnum, - MetaResponseModel, - SearchResponseModel, - SearchRequestModelFilterRange, - SearchRequestModelFilterValue, - SearchRequestModelFilter, -} from '@searchspring/snapi-types'; +import { + type SearchRequestModel, + type SearchResponseModelResult, + type SearchRequestModelSearchRedirectResponseEnum, + type MetaResponseModel, + type SearchResponseModel, + type SearchRequestModelFilterRange, + type SearchRequestModelFilterValue, + type SearchRequestModelFilter, + type SearchResponseModelFilter, + type MetaResponseModelFacetHierarchy, + type SearchResponseModelFilterTypeEnum, + SearchResponseModelFacetValue, +} from '@athoscommerce/snapi-types'; + import { type AutocompleteAddtocartSchemaDataBgfilterInner, type AutocompleteAddtocartSchemaDataFilterInner, @@ -41,7 +46,7 @@ import { } from '@searchspring/beacon'; import { CLICK_DUPLICATION_TIMEOUT, isClickWithinProductLink } from '../utils/isClickWithinProductLink'; -const BACKGROUND_FILTER_FIELD_MATCHES = ['collection', 'category', 'categories', 'hierarchy']; +const BACKGROUND_FILTER_FIELD_MATCHES = ['collection', 'category', 'categories', 'hierarchy', 'brand', 'manufacturer']; const BACKGROUND_FILTERS_VALUE_FLAGS = [1, 0, '1', '0', 'true', 'false', true, false]; const defaultConfig: SearchControllerConfig = { @@ -132,9 +137,6 @@ export class SearchController extends AbstractController { key: `ss-controller-${this.config.id}`, }); - // set last params to undefined for compare in search - this.storage.set('lastStringyParams', undefined); - if (typeof this.context?.page === 'object' && ['search', 'category'].includes(this.context.page.type)) { this.page = deepmerge(this.page, this.context.page); } @@ -174,7 +176,7 @@ export class SearchController extends AbstractController { }); // add 'afterSearch' middleware - this.eventManager.on('afterSearch', async (search: AfterSearchObj, next: Next): Promise => { + this.eventManager.on('afterSearch', async (search: SearchAfterSearchObj, next: Next): Promise => { const config = search.controller.config as SearchControllerConfig; const redirectURL = search.response?.search?.merchandising?.redirect; const searchStore = search.controller.store as SearchStore; @@ -206,6 +208,59 @@ export class SearchController extends AbstractController { this.eventManager.fire('restorePosition', { controller: this, element: elementPosition }); }); + this.eventManager.on('afterSearch', async (search: SearchAfterSearchObj, next: Next): Promise => { + await next(); + + // add hierarchy filter to filter summary + const facets = search.response.search.facets; + if (facets) { + facets.forEach((facet: any) => { + if (search.response.meta?.facets && facet.field) { + const field = (facet.field as string) || ''; + const metaFacet = search.response.meta.facets[field]; + + const dataDelimiter = (metaFacet as MetaResponseModelFacetHierarchy)?.hierarchyDelimiter || ' / '; + const filterSettings = (this.config as SearchStoreConfig)?.settings?.filters?.fields + ? this.config?.settings?.filters?.fields![field] + : this.config?.settings?.filters; + + const displayDelimiter = filterSettings?.hierarchy?.displayDelimiter ?? ' / '; // choose delimiter for label + const showFullPath = filterSettings?.hierarchy?.showFullPath ?? false; // display full hierarchy path or just the current level + + if ( + filterSettings?.hierarchy?.enabled && + metaFacet && + metaFacet.display === 'hierarchy' && + facet.filtered && + (facet as ValueFacet).values?.length > 0 + ) { + const filteredValues = (facet as SearchResponseModelFacetValue).values?.filter((val) => val?.filtered === true); + + if (filteredValues && filteredValues.length) { + const filterToAdd: SearchResponseModelFilter = { + field: facet.field, + //escape special charactors used in regex + label: showFullPath + ? (filteredValues[0].value ?? filteredValues[0].label ?? '').replace( + new RegExp(dataDelimiter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), + displayDelimiter + ) + : filteredValues[0].label, + type: 'value' as SearchResponseModelFilterTypeEnum.Value, + }; + + if (search.response.search.filters) { + search.response.search.filters.push(filterToAdd); + } else { + search.response.search.filters = [filterToAdd]; + } + } + } + } + }); + } + }); + this.eventManager.on('afterStore', async (search: AfterStoreObj, next: Next): Promise => { await next(); const controller = search.controller as SearchController; @@ -218,14 +273,13 @@ export class SearchController extends AbstractController { if (products.length === 0 && !response._cached) { // handle no results const data = getSearchSchemaData({ params: search.request, response: response }); + this.eventManager.fire('track.product.render', { controller: this, trackEvent: data }); this.tracker.events[this.page.type].render({ data, siteId: this.config.globals?.siteId }); } products.forEach((result: Product) => { - if (!response._cached) { - const data = schemaMap[result.id]; - this.tracker.events[this.page.type].render({ data, siteId: this.config.globals?.siteId }); - this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); + if (!search.response._cached) { + this.track.product.render(result); } this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].render = true; @@ -322,8 +376,10 @@ export class SearchController extends AbstractController { // fire restorePosition event on 'pageshow' when setting is enabled if (this.config.settings?.restorePosition?.onPageShow) { - window.addEventListener('pageshow', () => { - this.eventManager.fire('restorePosition', { controller: this, element: {} }); + window.addEventListener('pageshow', (e) => { + if (e.persisted && this.store.loaded) { + this.eventManager.fire('restorePosition', { controller: this, element: {} }); + } }); } } @@ -369,10 +425,10 @@ export class SearchController extends AbstractController { // store position data or empty object this.storage.set('scrollMap', scrollMap); const data = schemaMap[result.id]; + this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data }); this.tracker.events[this.page.type].clickThrough({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].clickThrough = true; - this.eventManager.fire('track.product.clickThrough', { controller: this, event: e, product: result, trackEvent: data }); }, click: (e: MouseEvent, result): void => { if (this.events.product[result.id]?.click) { @@ -397,10 +453,10 @@ export class SearchController extends AbstractController { } const data = schemaMap[result.id]; + this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); this.tracker.events[this.page.type].render({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].render = true; - this.eventManager.fire('track.product.render', { controller: this, product: result, trackEvent: data }); }, impression: (result: Product): void => { if (this.events.product[result.id]?.impression || !this.events.product[result.id]?.render) { @@ -408,24 +464,24 @@ export class SearchController extends AbstractController { } const data = schemaMap[result.id]; + this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data }); this.tracker.events[this.page.type].impression({ data, siteId: this.config.globals?.siteId }); this.events.product[result.id] = this.events.product[result.id] || {}; this.events.product[result.id].impression = true; - this.eventManager.fire('track.product.impression', { controller: this, product: result, trackEvent: data }); }, addToCart: (result: Product): void => { const data = getSearchAddtocartSchemaData({ searchSchemaData: schemaMap[result.id], results: [result] }); + this.eventManager.fire('track.product.addToCart', { controller: this, product: result, trackEvent: data }); this.tracker.events[this.page.type].addToCart({ data, siteId: this.config.globals?.siteId, }); - this.eventManager.fire('track.product.addToCart', { controller: this, product: result, trackEvent: data }); }, }, redirect: (redirectURL: string): void => { const data = getSearchRedirectSchemaData({ redirectURL }); - this.tracker.events.search.redirect({ data, siteId: this.config.globals?.siteId }); this.eventManager.fire('track.product.redirect', { controller: this, redirectURL, trackEvent: data }); + this.tracker.events.search.redirect({ data, siteId: this.config.globals?.siteId }); }, }; @@ -507,7 +563,7 @@ export class SearchController extends AbstractController { const stringyParams = JSON.stringify(getStorableRequestParams(params)); const prevStringyParams = this.storage.get('lastStringyParams'); - if (stringyParams == prevStringyParams) { + if (this.store.loaded && stringyParams === prevStringyParams) { // no param change - not searching return; } @@ -725,14 +781,14 @@ function createResultSchemaMapping({ response: search, }); - search.results?.forEach((result) => { + search.results?.forEach((result, idx) => { schemaMap[result.id!] = { ...schema, results: [ { type: ItemTypeEnum.Product, - position: result.position!, - uid: result.mappings?.core?.uid || '', + uid: result.mappings?.core?.parentId || result.mappings?.core?.uid || '', + position: idx + 1, sku: result.mappings?.core?.sku, }, ], @@ -812,7 +868,7 @@ function getSearchAddtocartSchemaData({ results?.map((result: Product): BeaconProduct => { const core = (result as Product).mappings.core!; return { - uid: core.uid || '', + uid: core.parentId || core.uid || '', sku: core.sku, price: Number(core.price), qty: result.quantity || 1, diff --git a/packages/snap-controller/src/types.ts b/packages/snap-controller/src/types.ts index 9dd6153b3..4632810ac 100644 --- a/packages/snap-controller/src/types.ts +++ b/packages/snap-controller/src/types.ts @@ -17,7 +17,13 @@ import type { Tracker, ProductViewEvent } from '@searchspring/snap-tracker'; import type { Profiler } from '@searchspring/snap-profiler'; import type { UrlManager } from '@searchspring/snap-url-manager'; import type { Logger } from '@searchspring/snap-logger'; -import type { SearchRequestModel } from '@searchspring/snapi-types'; +import type { + AutocompleteRequestModel, + // AutocompleteResponseModel, + // MetaResponseModel, + SearchRequestModel, + // SearchResponseModel, +} from '@athoscommerce/snapi-types'; // Middleware export type PluginFunction = (cntrlr: AbstractController, ...args: any) => Promise | void; export type PluginGrouping = [func: PluginFunction, ...args: unknown[]]; @@ -27,12 +33,29 @@ export type BeforeSearchObj = { request: any; }; -export type AfterSearchObj = { - controller: AbstractController; +export type SearchAfterSearchObj = { + controller: SearchController; + // response: { search?: SearchResponseModel, meta?: MetaResponseModel }; + response: any; + request: SearchRequestModel; +}; + +export type AutocompleteAfterSearchObj = { + controller: AutocompleteController; + // response: { autocomplete?: AutocompleteResponseModel, meta?: MetaResponseModel }; + response: any; + request: AutocompleteRequestModel; +}; + +export type FinderAfterSearchObj = { + controller: FinderController; + // response: { search?: SearchResponseModel, meta?: MetaResponseModel }; response: any; request: SearchRequestModel; }; +export type AfterSearchObj = SearchAfterSearchObj | AutocompleteAfterSearchObj | FinderAfterSearchObj; + export type AfterStoreObj = { controller: AbstractController; request: any; diff --git a/packages/snap-controller/src/utils/getParams.ts b/packages/snap-controller/src/utils/getParams.ts index 5284f0863..72ed3dece 100644 --- a/packages/snap-controller/src/utils/getParams.ts +++ b/packages/snap-controller/src/utils/getParams.ts @@ -3,7 +3,7 @@ import { SearchRequestModelFilterTypeEnum, SearchRequestModelFilterRange, SearchRequestModelFilterValue, -} from '@searchspring/snapi-types'; +} from '@athoscommerce/snapi-types'; import type { ImmutableUrlState } from '@searchspring/snap-url-manager'; export function getSearchParams(state: ImmutableUrlState): Record { diff --git a/packages/snap-event-manager/CHANGELOG.md b/packages/snap-event-manager/CHANGELOG.md index 8d142ba78..e04dee21f 100644 --- a/packages/snap-event-manager/CHANGELOG.md +++ b/packages/snap-event-manager/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0](https://github.com/searchspring/snap/compare/v1.10.1...v1.11.0) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-event-manager + ## [1.10.1](https://github.com/searchspring/snap/compare/v1.10.0...v1.10.1) (2025-10-06) **Note:** Version bump only for package @searchspring/snap-event-manager @@ -113,6 +117,46 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.1.0](https://github.com/searchspring/snap/compare/v0.60.1...v1.1.0) (2024-09-20) +## [0.72.1](https://github.com/searchspring/snap/compare/v0.72.0...v0.72.1) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +# [0.72.0](https://github.com/searchspring/snap/compare/v0.71.0...v0.72.0) (2026-01-14) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +# [0.71.0](https://github.com/searchspring/snap/compare/v0.70.1...v0.71.0) (2025-11-26) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +## [0.70.1](https://github.com/searchspring/snap/compare/v0.70.0...v0.70.1) (2025-11-17) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +# [0.70.0](https://github.com/searchspring/snap/compare/v0.69.2...v0.70.0) (2025-11-13) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +## [0.69.2](https://github.com/searchspring/snap/compare/v0.69.1...v0.69.2) (2025-11-04) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +## [0.69.1](https://github.com/searchspring/snap/compare/v0.69.0...v0.69.1) (2025-10-23) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +# [0.69.0](https://github.com/searchspring/snap/compare/v0.68.0...v0.69.0) (2025-10-16) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +# [0.68.0](https://github.com/searchspring/snap/compare/v0.67.5...v0.68.0) (2025-08-18) + +**Note:** Version bump only for package @searchspring/snap-event-manager + +## [0.67.5](https://github.com/searchspring/snap/compare/v0.67.4...v0.67.5) (2025-08-11) + +**Note:** Version bump only for package @searchspring/snap-event-manager + ## [0.67.4](https://github.com/searchspring/snap/compare/v0.67.3...v0.67.4) (2025-07-29) **Note:** Version bump only for package @searchspring/snap-event-manager diff --git a/packages/snap-event-manager/README.md b/packages/snap-event-manager/README.md index 02ce04a52..50f0023b7 100644 --- a/packages/snap-event-manager/README.md +++ b/packages/snap-event-manager/README.md @@ -1,26 +1,9 @@ # Snap Event Manager -NPM Status -The Snap Event Manager is used to create events and attach middleware to them. +The Snap Event Manager is available on each controller via `controller.eventManager` and is used to create events and attach middleware to them. Events are recommended to be configured via [Configuration Middleware](https://github.com/searchspring/snap/tree/main/docs/REFERENCE_CONFIGURATION_MIDDLEWARE.md) to hook into controller events at critical times in the life cycle. It also allows for custom events to be used throughout your implementation. -When used as a service of a controller it allows you to hook into controller events at critical times in the life cycle. It also allows for custom events to be used throughout your implementation. - -## Dependency - -Snap Event Manager is a dependency of [@searchspring/snap-controller](https://github.com/searchspring/snap/tree/main/packages/snap-controller) NPM Status - -## Installation - -```bash -npm install --save @searchspring/snap-event-manager -``` - -## Import -```typescript -import { EventManager } from '@searchspring/snap-event-manager'; -``` ## Controller usage Snap Event Manager is a dependency of Snap Controller and it is recommended to use methods of the controller to attach events to the EventManager. Additionally, different events exist for the different controllers - see the Controller documentation for more details. @@ -28,10 +11,8 @@ Snap Event Manager is a dependency of Snap Controller and it is recommended to u ### `on` method Used to attach middleware to an event. If the event name previously had middleware attached, it will add to the middleware stack. -```typescript -const eventManager = new EventManager(); - -eventManager.on('interestingEvent', async (eventData, next) => { +```js +controller.eventManager.on('interestingEvent', async (eventData, next) => { // do something with the eventData // pass control to the next middleware attached to the event @@ -46,8 +27,8 @@ If a middleware returns `false` the entire middleware flow is interrupted and an ### `fire` method Invoke custom event. Data passed into the second parameter gets handed off to the middleware attached with the `on` method. -```typescript -eventManager.fire('interestingEvent', { data: { some: 'string' } }); +```js +controller.eventManager.fire('interestingEvent', { data: { some: 'string' } }); ``` ## Middleware @@ -58,26 +39,26 @@ The first middleware attached with the `on` method is the first to execute. When ### Order Flow Example -```typescript -eventManager.on('interestingEvent', async (data, next) => { +```js +controller.eventManager.on('interestingEvent', async (data, next) => { console.log('first middleware start'); await next(); console.log('first middleware end'); }); -eventManager.on('interestingEvent', async (data, next) => { +controller.eventManager.on('interestingEvent', async (data, next) => { console.log('second middleware start'); await next(); console.log('second middleware end'); }); -eventManager.on('interestingEvent', async (data, next) => { +controller.eventManager.on('interestingEvent', async (data, next) => { console.log('third middleware start'); await next(); console.log('third middleware end'); }); -eventManager.fire('interestingEvent', { data: { some: 'string' } } ); +controller.eventManager.fire('interestingEvent', { data: { some: 'string' } } ); ``` diff --git a/packages/snap-event-manager/package.json b/packages/snap-event-manager/package.json index 05c1e4982..3d4d42668 100644 --- a/packages/snap-event-manager/package.json +++ b/packages/snap-event-manager/package.json @@ -1,6 +1,6 @@ { "name": "@searchspring/snap-event-manager", - "version": "1.10.1", + "version": "1.11.0", "description": "Snap Event Manager", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", @@ -11,7 +11,7 @@ "access": "public" }, "scripts": { - "build": "rm -rf ./dist && tsc && tsc -p tsconfig.cjs.json", + "build": "rm -rf ./dist && rm -fr ./components/dist && tsc & p1=$!; tsc -p tsconfig.cjs.json & p2=$!; wait $p1 && wait $p2", "build:docs": "typedoc --out docs src/index.ts", "dev": "tsc --watch", "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'", diff --git a/packages/snap-logger/CHANGELOG.md b/packages/snap-logger/CHANGELOG.md index 70408d560..ecd261226 100644 --- a/packages/snap-logger/CHANGELOG.md +++ b/packages/snap-logger/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0](https://github.com/searchspring/snap/compare/v1.10.1...v1.11.0) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-logger + ## [1.10.1](https://github.com/searchspring/snap/compare/v1.10.0...v1.10.1) (2025-10-06) **Note:** Version bump only for package @searchspring/snap-logger @@ -113,6 +117,46 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.1.0](https://github.com/searchspring/snap/compare/v0.60.1...v1.1.0) (2024-09-20) +## [0.72.1](https://github.com/searchspring/snap/compare/v0.72.0...v0.72.1) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-logger + +# [0.72.0](https://github.com/searchspring/snap/compare/v0.71.0...v0.72.0) (2026-01-14) + +**Note:** Version bump only for package @searchspring/snap-logger + +# [0.71.0](https://github.com/searchspring/snap/compare/v0.70.1...v0.71.0) (2025-11-26) + +**Note:** Version bump only for package @searchspring/snap-logger + +## [0.70.1](https://github.com/searchspring/snap/compare/v0.70.0...v0.70.1) (2025-11-17) + +**Note:** Version bump only for package @searchspring/snap-logger + +# [0.70.0](https://github.com/searchspring/snap/compare/v0.69.2...v0.70.0) (2025-11-13) + +**Note:** Version bump only for package @searchspring/snap-logger + +## [0.69.2](https://github.com/searchspring/snap/compare/v0.69.1...v0.69.2) (2025-11-04) + +**Note:** Version bump only for package @searchspring/snap-logger + +## [0.69.1](https://github.com/searchspring/snap/compare/v0.69.0...v0.69.1) (2025-10-23) + +**Note:** Version bump only for package @searchspring/snap-logger + +# [0.69.0](https://github.com/searchspring/snap/compare/v0.68.0...v0.69.0) (2025-10-16) + +**Note:** Version bump only for package @searchspring/snap-logger + +# [0.68.0](https://github.com/searchspring/snap/compare/v0.67.5...v0.68.0) (2025-08-18) + +**Note:** Version bump only for package @searchspring/snap-logger + +## [0.67.5](https://github.com/searchspring/snap/compare/v0.67.4...v0.67.5) (2025-08-11) + +**Note:** Version bump only for package @searchspring/snap-logger + ## [0.67.4](https://github.com/searchspring/snap/compare/v0.67.3...v0.67.4) (2025-07-29) **Note:** Version bump only for package @searchspring/snap-logger diff --git a/packages/snap-logger/README.md b/packages/snap-logger/README.md index 85a99be17..5864789bf 100644 --- a/packages/snap-logger/README.md +++ b/packages/snap-logger/README.md @@ -1,177 +1,92 @@ # Snap Logger -NPM Status +Snap Logger is available on each controller via `controller.log`. Controller logs are prefixed with the controller's id and it is recommended to use logging methods of the controller in place of `window.console` methods. -Simple logger for debugging - - -

-
- Sample code -
- -```typescript -logger.image({ +```js +controller.log.image({ url: 'https://searchspring.com/wp-content/uploads/2020/01/SearchSpring-Primary-FullColor-800-1-1-640x208.png', width: '90px', height: '30px' }); -logger.error('error'); +controller.log.error('error'); -logger.warn('warn'); +controller.log.warn('warn'); -logger.imageText({ +controller.log.imageText({ url: 'https://searchspring.com/wp-content/themes/SearchSpring-Theme/dist/images/favicons/favicon.svg', }, 'imageText'); -logger.debug('debug'); - -logger.dev(`%c ${logger.emoji.vortex} %c${logger.prefix}%c${'magical text'}`, -`color: ${logger.colors.blue}; font-weight: bold; font-size: 10px; line-height: 12px;`, -`color: ${logger.colors.bluegreen}; font-weight: normal;`, -`color: ${logger.colors.bluegreen}; font-weight: bold;`); -``` -
- - - - -## Dependency - -Snap Logger is a dependency of [@searchspring/snap-controller](https://github.com/searchspring/snap/tree/main/packages/snap-controller) NPM Status - -## Installation - -```bash -npm install --save @searchspring/snap-logger -``` - - - -## Import -```typescript -import { Logger } from '@searchspring/snap-logger'; -``` - -## Config -Snap Logger accepts an optional string prefix which when set is prepended to all logs. - -```typescript -const prefix = 'Log:'; -const logger = new Logger(prefix) -``` - -## Controller usage -Snap Logger is a dependency of Snap Controller and it is recommended to use logging methods of the controller in place of `console` methods. - - -## Standalone usage -```typescript -const logger = new Logger(); - -logger.warn('this is a warning'); -``` - -### `setNamespace` method -Sets prefix instead of defining a prefix in the constructor. -```typescript -const logger = new Logger(); - -logger.warn('Hello'); -// 'Hello' - -logger.setNamespace('search'); +controller.log.debug('debug'); -logger.warn('Hello'); -// ' [search] :: Hello' +controller.log.dev(`%c ${controller.log.emoji.vortex} %c${controller.log.prefix}%c${'magical text'}`, +`color: ${controller.log.colors.blue}; font-weight: bold; font-size: 10px; line-height: 12px;`, +`color: ${controller.log.colors.bluegreen}; font-weight: normal;`, +`color: ${controller.log.colors.bluegreen}; font-weight: bold;`); ``` -### `setMode` method -The default logging mode is `production`. +## `setMode` method +The default logging mode is `production`. (Set by Snap config) When set to production, logs using `dev` will not be visible. This also includes `image`, `imageText`, `debug`, and `profile`. When set to `development`, all logging methods will be visible. - -```typescript -import { Logger, LogMode } from '@searchspring/snap-logger'; - -const logger = new Logger(); -logger.setMode(LogMode.DEVELOPMENT); -``` - -```typescript -enum LogMode { - PRODUCTION = 'production', - DEVELOPMENT = 'development', -} -``` - -### `error` method +## `error` method This method takes any number of parameters and logs them to the console. It is best to use this method for error handling. -```typescript -logger.error('error!!!'); -logger.error('text about the error', errorObject, 'more', 'text'); +```js +controller.log.error('error!!!'); +controller.log.error('text about the error', errorObject, 'more', 'text'); ``` -### `warn` method +## `warn` method This method takes any number of parameters and logs them to the console. It is best to use this method for displaying warnings. -```typescript -logger.warn('warning!!!'); -logger.warn('warning', warningObject, 'more text'); +```js +controller.log.warn('warning!!!'); +controller.log.warn('warning', warningObject, 'more text'); ``` -### `dev` method +## `dev` method This method takes any number of parameters and logs them to the console. If mode is set to `LogMode.PRODUCTION`, the `dev` logs will not be displayed. -```typescript -logger.dev('dev') +```js +controller.log.dev('dev') ``` -### `debug` method +## `debug` method This method takes any number of parameters and logs them to the console. If mode is set to `LogMode.PRODUCTION`, `debug` logs will not be displayed. -```typescript -logger.debug('debug'); +```js +controller.log.debug('debug'); ``` -### `image` method +## `image` method This method takes any number of parameters and logs them to the console. The first parameter is special and takes properties that specify the image details. If mode is set to `LogMode.PRODUCTION`, `image` logs will not be displayed. -```typescript -logger.image({ +```js +controller.log.image({ url: 'https://searchspring.com/wp-content/uploads/2020/01/SearchSpring-Primary-FullColor-800-1-1-640x208.png', width: '30px', height: '30px' }); ``` -### `imageText` method +## `imageText` method This method takes any number of parameters and logs them to the console. The first parameter is special and takes properties that specify the image details. If mode is set to `LogMode.PRODUCTION`, `imageText` logs will not be displayed. -```typescript -logger.imageText({ +```js +controller.log.imageText({ url: 'https://searchspring.com/wp-content/uploads/2020/01/SearchSpring-Primary-FullColor-800-1-1-640x208.png', text: `imageText`, style: `color: #4c3ce2; font-weight: bold;`, }); ``` -### `profile` method -This method takes any number of parameters and logs them to the console. The first parameter is special and takes a Snap profile. If mode is set to `LogMode.PRODUCTION`, `profile` logs will not be displayed. - -See [@searchspring/snap-profiler](https://github.com/searchspring/snap/tree/main/packages/snap-profiler) NPM Status - -```typescript -import { Profiler } from '@searchspring/snap-profiler'; -import { Logger } from '@searchspring/snap-logger'; - -const logger = new Logger(); -const profiler = new Profiler(); +## `profile` method -const searchProfile = profiler.create({ +This method takes any number of parameters and logs them to the console. The first parameter is special and takes a [Snap profile](https://searchspring.github.io/snap/reference-profiler). If mode is set to `LogMode.PRODUCTION`, `profile` logs will not be displayed. +```js +const searchProfile = controller.profiler.create({ type: 'event', name: 'search', context: {} @@ -183,17 +98,17 @@ searchProfile.start(); searchProfile.stop(); -logger.profile(searchProfile) +controller.log.profile(searchProfile) ``` -### `emoji` property +## `emoji` property The `emoji` property contains various emojis that can be used The following emojis are available: -```typescript +```js const emoji = { bang: String.fromCodePoint(0x203c), bright: String.fromCodePoint(0x1f506), @@ -214,14 +129,14 @@ const emoji = { }; ``` -### `colors` property +## `colors` property The `colors` property contains various colors that can be used The following colors are available: -```typescript +```js const colors = { blue: '#3379c1', bluelight: '#688BA3', diff --git a/packages/snap-logger/package.json b/packages/snap-logger/package.json index d194aca06..39568c4e0 100644 --- a/packages/snap-logger/package.json +++ b/packages/snap-logger/package.json @@ -1,6 +1,6 @@ { "name": "@searchspring/snap-logger", - "version": "1.10.1", + "version": "1.11.0", "description": "Snap Logger", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", @@ -11,7 +11,7 @@ "access": "public" }, "scripts": { - "build": "rm -rf ./dist && tsc && tsc -p tsconfig.cjs.json", + "build": "rm -rf ./dist && rm -fr ./components/dist && tsc & p1=$!; tsc -p tsconfig.cjs.json & p2=$!; wait $p1 && wait $p2", "build:docs": "typedoc --out docs src/index.ts", "dev": "tsc --watch", "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'", @@ -20,7 +20,7 @@ "test:watch": "jest --watch" }, "dependencies": { - "@searchspring/snap-toolbox": "1.10.1" + "@searchspring/snap-toolbox": "1.11.0" }, "sideEffects": false, "files": [ diff --git a/packages/snap-platforms/CHANGELOG.md b/packages/snap-platforms/CHANGELOG.md index 4456a7866..e00d9f089 100644 --- a/packages/snap-platforms/CHANGELOG.md +++ b/packages/snap-platforms/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0](https://github.com/searchspring/snap/compare/v1.10.1...v1.11.0) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-platforms + ## [1.10.1](https://github.com/searchspring/snap/compare/v1.10.0...v1.10.1) (2025-10-06) **Note:** Version bump only for package @searchspring/snap-platforms @@ -123,6 +127,46 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # [1.1.0](https://github.com/searchspring/snap/compare/v0.60.1...v1.1.0) (2024-09-20) +## [0.72.1](https://github.com/searchspring/snap/compare/v0.72.0...v0.72.1) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-platforms + +# [0.72.0](https://github.com/searchspring/snap/compare/v0.71.0...v0.72.0) (2026-01-14) + +**Note:** Version bump only for package @searchspring/snap-platforms + +# [0.71.0](https://github.com/searchspring/snap/compare/v0.70.1...v0.71.0) (2025-11-26) + +**Note:** Version bump only for package @searchspring/snap-platforms + +## [0.70.1](https://github.com/searchspring/snap/compare/v0.70.0...v0.70.1) (2025-11-17) + +**Note:** Version bump only for package @searchspring/snap-platforms + +# [0.70.0](https://github.com/searchspring/snap/compare/v0.69.2...v0.70.0) (2025-11-13) + +**Note:** Version bump only for package @searchspring/snap-platforms + +## [0.69.2](https://github.com/searchspring/snap/compare/v0.69.1...v0.69.2) (2025-11-04) + +**Note:** Version bump only for package @searchspring/snap-platforms + +## [0.69.1](https://github.com/searchspring/snap/compare/v0.69.0...v0.69.1) (2025-10-23) + +**Note:** Version bump only for package @searchspring/snap-platforms + +# [0.69.0](https://github.com/searchspring/snap/compare/v0.68.0...v0.69.0) (2025-10-16) + +**Note:** Version bump only for package @searchspring/snap-platforms + +# [0.68.0](https://github.com/searchspring/snap/compare/v0.67.5...v0.68.0) (2025-08-18) + +**Note:** Version bump only for package @searchspring/snap-platforms + +## [0.67.5](https://github.com/searchspring/snap/compare/v0.67.4...v0.67.5) (2025-08-11) + +**Note:** Version bump only for package @searchspring/snap-platforms + ## [0.67.4](https://github.com/searchspring/snap/compare/v0.67.3...v0.67.4) (2025-07-29) **Note:** Version bump only for package @searchspring/snap-platforms diff --git a/packages/snap-platforms/README.md b/packages/snap-platforms/README.md index 39d991ddf..f82401f57 100644 --- a/packages/snap-platforms/README.md +++ b/packages/snap-platforms/README.md @@ -1,13 +1,12 @@ # Snap Platforms -NPM Status -Snap Platforms contains functionality that makes working with various shoping plaforms easier. Several platforms have standardized APIs that can be leveraged for things like adding products to the cart. The most commonly used platforms are currently supported with a standard `addToCart` function. This function can be used within Snap projects. Each platform may also support other functionality, checkout the documenation for each platform to find out more. +Snap Platforms contains functionality that makes working with various shopping platforms easier. Several platforms have standardized APIs that can be leveraged for things like adding products to the cart. The most commonly used platforms are currently supported with a standard `addToCart` function. This function can be used within Snap projects. Each platform may also support other functionality, checkout the documentation for each platform to find out more. [Common](https://github.com/searchspring/snap/tree/main/packages/snap-platforms/common) -[BigCommerce](https://github.com/searchspring/snap/tree/main/packages/snap-platforms/bigcommerce) +[Shopify](https://searchspring.github.io/snap/reference-platforms-shopify) -[Magento2](https://github.com/searchspring/snap/tree/main/packages/snap-platforms/magento2) +[BigCommerce](https://searchspring.github.io/snap/reference-platforms-bigcommerce) -[Shopify](https://github.com/searchspring/snap/tree/main/packages/snap-platforms/shopify) \ No newline at end of file +[Magento2](https://searchspring.github.io/snap/reference-platforms-magento2) diff --git a/packages/snap-platforms/bigcommerce/src/addToCart.test.ts b/packages/snap-platforms/bigcommerce/src/addToCart.test.ts index 50344cc21..fed313522 100644 --- a/packages/snap-platforms/bigcommerce/src/addToCart.test.ts +++ b/packages/snap-platforms/bigcommerce/src/addToCart.test.ts @@ -64,6 +64,7 @@ describe('addToCart', () => { beforeAll(async () => { searchConfig = { ...searchConfigDefault }; controller = new SearchController(searchConfig, controllerServices); + (controller.client as MockClient).mockData.updateConfig({ search: 'variants' }); await controller.search(); @@ -208,12 +209,13 @@ describe('addToCart', () => { client.mockData.updateConfig({ siteId: 'tfdz6e', search: 'variants' }); const optionSearchConfig: SearchStoreConfig = { ...searchConfig, + id: 'searchVariants', settings: { redirects: { singleResult: false, }, variants: { - field: 'ss_variants', + autoSelect: true, }, }, }; @@ -386,7 +388,7 @@ describe('addToCart', () => { singleResult: false, }, variants: { - field: 'ss_variants', + autoSelect: true, }, }, }; diff --git a/packages/snap-platforms/common/src/plugins/pluginAddToCart.test.ts b/packages/snap-platforms/common/src/plugins/pluginAddToCart.test.ts index 3b65a7332..91730ee52 100644 --- a/packages/snap-platforms/common/src/plugins/pluginAddToCart.test.ts +++ b/packages/snap-platforms/common/src/plugins/pluginAddToCart.test.ts @@ -45,6 +45,8 @@ describe('common/pluginAddToCart', () => { beforeEach(() => { searchConfig = { ...searchConfigDefault }; controller = new SearchController(searchConfig, createControllerServices()); + (controller.client as MockClient).mockData.updateConfig({ search: 'variants' }); + expect(controller.config.globals).toBeDefined(); expect(controller.config.globals!.filters).toEqual([]); }); diff --git a/packages/snap-platforms/common/src/plugins/pluginLogger.test.ts b/packages/snap-platforms/common/src/plugins/pluginLogger.test.ts index 0b51537bf..0bcbd788f 100644 --- a/packages/snap-platforms/common/src/plugins/pluginLogger.test.ts +++ b/packages/snap-platforms/common/src/plugins/pluginLogger.test.ts @@ -63,6 +63,6 @@ describe('common/pluginLogger', () => { pluginLogger(controller, pluginConfig); await controller.search(); - expect(logMock).toHaveBeenCalled(); + expect(logMock).not.toHaveBeenCalled(); }); }); diff --git a/packages/snap-platforms/magento2/src/addToCart.test.ts b/packages/snap-platforms/magento2/src/addToCart.test.ts index 40d733a2d..5487d4bc0 100644 --- a/packages/snap-platforms/magento2/src/addToCart.test.ts +++ b/packages/snap-platforms/magento2/src/addToCart.test.ts @@ -61,6 +61,7 @@ describe('Magento2', () => { beforeAll(async () => { searchConfig = { ...searchConfigDefault }; controller = new SearchController(searchConfig, controllerServices); + (controller.client as MockClient).mockData.updateConfig({ search: 'variants' }); await controller.search(); diff --git a/packages/snap-platforms/package.json b/packages/snap-platforms/package.json index d61f78198..9baf5d8e0 100644 --- a/packages/snap-platforms/package.json +++ b/packages/snap-platforms/package.json @@ -1,6 +1,6 @@ { "name": "@searchspring/snap-platforms", - "version": "1.10.1", + "version": "1.11.0", "description": "Snap Platforms Library", "author": "Searchspring", "license": "MIT", @@ -9,7 +9,7 @@ "access": "public" }, "scripts": { - "build": "rm -rf ./dist && tsc && tsc -p tsconfig.cjs.json", + "build": "rm -rf ./dist && rm -fr ./components/dist && tsc & p1=$!; tsc -p tsconfig.cjs.json & p2=$!; wait $p1 && wait $p2", "build:docs": "echo 'no docs for snap-platforms'", "dev": "tsc --watch", "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}'", @@ -18,11 +18,11 @@ "test:watch": "jest --watch" }, "dependencies": { - "@searchspring/snap-toolbox": "1.10.1" + "@searchspring/snap-toolbox": "1.11.0" }, "devDependencies": { - "@searchspring/snap-controller": "1.10.1", - "@searchspring/snap-store-mobx": "1.10.1" + "@searchspring/snap-controller": "1.11.0", + "@searchspring/snap-store-mobx": "1.11.0" }, "sideEffects": false, "files": [ diff --git a/packages/snap-platforms/shopify/README.md b/packages/snap-platforms/shopify/README.md index 37e7dbec1..249a1a0cf 100644 --- a/packages/snap-platforms/shopify/README.md +++ b/packages/snap-platforms/shopify/README.md @@ -3,8 +3,26 @@ This platform library gives you helper functions and plugins to use with the Sho ## Functions -### addToCart -The `addToCart` function will automatically add products to the cart and then redirect to the cart page (`/cart`). The function is async, and takes an array of products (Result Store References) to add, and an optional config. The optional config can take two optional properties, `redirect` and `idFieldName`. Variants must be enabled for full functionality. +## Usage +To use the platform library, simply import what you wish to use from `@searchspring/snap-platforms/shopify`. + +```jsx +import { addToCart } from '@searchspring/snap-platforms/shopify'; + +export const AddToCart = (props) => { + const { result } = props; + const config = { + idFieldName: `display.mappings.core.sku`, + } + + return ( +
addToCart([result], config)}>Add To Cart
+ ) +}; +``` + +## addToCart +The `addToCart` function will automatically add products to the cart and then redirect to the cart page (`/cart`). The function is async, and takes an array of products (Result Store References) to add, and an optional config. The optional config can take two optional properties, `redirect` and `idFieldName`. Snap variants must be enabled for full functionality. The `redirect` property can be set to `false` or supplied with an alternate redirect URL instead of the default (`/cart`). diff --git a/packages/snap-platforms/shopify/src/addToCart.test.ts b/packages/snap-platforms/shopify/src/addToCart.test.ts index 5805665f3..5e25d5eb5 100644 --- a/packages/snap-platforms/shopify/src/addToCart.test.ts +++ b/packages/snap-platforms/shopify/src/addToCart.test.ts @@ -57,6 +57,7 @@ describe('Shopify AddToCart', () => { beforeAll(async () => { searchConfig = { ...searchConfigDefault }; controller = new SearchController(searchConfig, controllerServices); + (controller.client as MockClient).mockData.updateConfig({ search: 'variants' }); await controller.search(); diff --git a/packages/snap-platforms/shopify/src/addToCart.ts b/packages/snap-platforms/shopify/src/addToCart.ts index 825e79986..987242d74 100644 --- a/packages/snap-platforms/shopify/src/addToCart.ts +++ b/packages/snap-platforms/shopify/src/addToCart.ts @@ -2,7 +2,7 @@ import type { Product } from '@searchspring/snap-store-mobx'; export type ShopifyAddToCartConfig = { redirect?: boolean | string; - idFieldName?: string; // display.mappings.core.id + idFieldName?: string; // display.mappings.core.uid }; declare global { diff --git a/packages/snap-preact-demo/CHANGELOG.md b/packages/snap-preact-demo/CHANGELOG.md index 2348bc00b..c078f9bc7 100644 --- a/packages/snap-preact-demo/CHANGELOG.md +++ b/packages/snap-preact-demo/CHANGELOG.md @@ -3,6 +3,33 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0](https://github.com/searchspring/snap-1.0/compare/v1.10.1...v1.11.0) (2026-01-15) + +### Bug Fixes + +- add noBeacon param to recommend api, update beacon and snapi-types version ([89e185c](https://github.com/searchspring/snap-1.0/commit/89e185c1b06a49274103c50d3bf410e2effa1e3a)) +- **autocomplete:** prevent impressing results when subsequent queries are the same ([2a154b5](https://github.com/searchspring/snap-1.0/commit/2a154b5fdecd3d56fbcd122205e16a9edf50b5ec)) + +### Features + +- **client:** adding support to have separate subdomain in api - using this for recommend requests ([ceb6d65](https://github.com/searchspring/snap-1.0/commit/ceb6d6500c2c8a57df0356535d0f74d8d0464fae)) +- **preact/components/facet:** adding range facet inputs and the ability to use filterFormatValue for filters ([ef16169](https://github.com/searchspring/snap-1.0/commit/ef16169123f749d45cf4ab4c7d29375c06ab16da)) +- **snap.tsx:** adding support for configurable initiator ([f286c5c](https://github.com/searchspring/snap-1.0/commit/f286c5c5f416ddad5771002bf00a0dc2e570922f)) + +## [1.10.1](https://github.com/searchspring/snap-1.0/compare/v1.10.0...v1.10.1) (2025-10-06) + +**Note:** Version bump only for package @searchspring/snap-preact-demo + +# [1.10.0](https://github.com/searchspring/snap-1.0/compare/v1.9.2...v1.10.0) (2025-10-02) + +### Features + +- **client:** moving to Athos APIs ([d773390](https://github.com/searchspring/snap-1.0/commit/d7733901b97e8fc6e3fa5c6b47dba1401bdff203)) + +## [1.9.2](https://github.com/searchspring/snap-1.0/compare/v1.9.1...v1.9.2) (2025-08-29) + +**Note:** Version bump only for package @searchspring/snap-preact-demo + ## [1.9.1](https://github.com/searchspring/snap-1.0/compare/v1.9.0...v1.9.1) (2025-08-25) **Note:** Version bump only for package @searchspring/snap-preact-demo @@ -145,6 +172,52 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - responsive themestore and breakpoint props ([d4faf3a](https://github.com/searchspring/snap-1.0/commit/d4faf3acdd2703b0225d4784c078ada967c64527)) - separate recommendation types ([4a03491](https://github.com/searchspring/snap-1.0/commit/4a03491766fd3dbd3eeddf8d91ed4572bd4ae6bf)) - support template themes in snap-preact ([df9d905](https://github.com/searchspring/snap-1.0/commit/df9d9057a1554d7eb62830002c3db990e0f8f272)) +## [0.72.1](https://github.com/searchspring/snap/compare/v0.72.0...v0.72.1) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-preact-demo + +# [0.72.0](https://github.com/searchspring/snap/compare/v0.71.0...v0.72.0) (2026-01-14) + +### Features + +- **client:** adding support to have separate subdomain in api - using this for recommend requests ([ceb6d65](https://github.com/searchspring/snap/commit/ceb6d6500c2c8a57df0356535d0f74d8d0464fae)) +- **snap.tsx:** adding support for configurable initiator ([f286c5c](https://github.com/searchspring/snap/commit/f286c5c5f416ddad5771002bf00a0dc2e570922f)) + +# [0.71.0](https://github.com/searchspring/snap/compare/v0.70.1...v0.71.0) (2025-11-26) + +### Bug Fixes + +- **autocomplete:** prevent impressing results when subsequent queries are the same ([2a154b5](https://github.com/searchspring/snap/commit/2a154b5fdecd3d56fbcd122205e16a9edf50b5ec)) + +## [0.70.1](https://github.com/searchspring/snap/compare/v0.70.0...v0.70.1) (2025-11-17) + +**Note:** Version bump only for package @searchspring/snap-preact-demo + +# [0.70.0](https://github.com/searchspring/snap/compare/v0.69.2...v0.70.0) (2025-11-13) + +**Note:** Version bump only for package @searchspring/snap-preact-demo + +## [0.69.2](https://github.com/searchspring/snap/compare/v0.69.1...v0.69.2) (2025-11-04) + +**Note:** Version bump only for package @searchspring/snap-preact-demo + +## [0.69.1](https://github.com/searchspring/snap/compare/v0.69.0...v0.69.1) (2025-10-23) + +**Note:** Version bump only for package @searchspring/snap-preact-demo + +# [0.69.0](https://github.com/searchspring/snap/compare/v0.68.0...v0.69.0) (2025-10-16) + +**Note:** Version bump only for package @searchspring/snap-preact-demo + +# [0.68.0](https://github.com/searchspring/snap/compare/v0.67.5...v0.68.0) (2025-08-18) + +### Bug Fixes + +- add noBeacon param to recommend api, update beacon and snapi-types version ([89e185c](https://github.com/searchspring/snap/commit/89e185c1b06a49274103c50d3bf410e2effa1e3a)) + +## [0.67.5](https://github.com/searchspring/snap/compare/v0.67.4...v0.67.5) (2025-08-11) + +**Note:** Version bump only for package @searchspring/snap-preact-demo ## [0.67.4](https://github.com/searchspring/snap/compare/v0.67.3...v0.67.4) (2025-07-29) diff --git a/packages/snap-preact-demo/package.json b/packages/snap-preact-demo/package.json index 527bf9058..ecf697f79 100644 --- a/packages/snap-preact-demo/package.json +++ b/packages/snap-preact-demo/package.json @@ -1,7 +1,7 @@ { "name": "@searchspring/snap-preact-demo", "private": true, - "version": "1.10.1", + "version": "1.11.0", "description": "demo store for development and testing", "author": "Searchspring", "license": "MIT", @@ -34,7 +34,7 @@ "scaffold": "snapfu-core" }, "dependencies": { - "@searchspring/snap-preact": "1.10.1", + "@searchspring/snap-preact": "1.11.0", "deepmerge": "4.3.1", "mobx-react": "7.6.0", "preact": "10.9.0" diff --git a/packages/snap-preact-demo/snap/src/components/Recommendations/Bundles/Bundles.tsx b/packages/snap-preact-demo/snap/src/components/Recommendations/Bundles/Bundles.tsx index 63dda8cab..d614c8bac 100644 --- a/packages/snap-preact-demo/snap/src/components/Recommendations/Bundles/Bundles.tsx +++ b/packages/snap-preact-demo/snap/src/components/Recommendations/Bundles/Bundles.tsx @@ -1,7 +1,7 @@ import { h } from 'preact'; import { observer } from 'mobx-react-lite'; -import { RecommendationBundle } from '@searchspring/snap-preact/components'; +import { RecommendationBundle, RecommendationBundleProps } from '@searchspring/snap-preact/components'; import { useEffect } from 'preact/hooks'; import './Bundles.scss'; @@ -16,7 +16,7 @@ export const Bundles = observer((props) => { } }, []); - const bundleRecsProps = { + const bundleRecsProps: RecommendationBundleProps = { controller: controller, onAddToCart: (data) => controller.log.debug('ADDING TO CART', data), lazyRender: { diff --git a/packages/snap-preact-demo/snap/src/index.ts b/packages/snap-preact-demo/snap/src/index.ts index 71163c269..e33c71be4 100644 --- a/packages/snap-preact-demo/snap/src/index.ts +++ b/packages/snap-preact-demo/snap/src/index.ts @@ -144,11 +144,6 @@ let config: SnapConfig = { // branch: BRANCHNAME, branch: 'production', plugins: [[mutateResultsURL]], - settings: { - variants: { - field: 'ss_variants', - }, - }, }, }, }, @@ -165,9 +160,6 @@ let config: SnapConfig = { redirects: { singleResult: false, }, - variants: { - field: 'ss_variants', - }, restorePosition: { enabled: true, }, diff --git a/packages/snap-preact-demo/snap/src/modern.ts b/packages/snap-preact-demo/snap/src/modern.ts new file mode 100644 index 000000000..d74d0a816 --- /dev/null +++ b/packages/snap-preact-demo/snap/src/modern.ts @@ -0,0 +1,4 @@ +window.searchspring = window.searchspring || {}; +window.searchspring.managed = true; + +import('./index'); diff --git a/packages/snap-preact-demo/snap/src/universal.ts b/packages/snap-preact-demo/snap/src/universal.ts index 767bb6b9c..f42a6051d 100644 --- a/packages/snap-preact-demo/snap/src/universal.ts +++ b/packages/snap-preact-demo/snap/src/universal.ts @@ -3,18 +3,15 @@ import { polyfills } from '@searchspring/snap-preact'; const promises = []; if (!('fetch' in window)) { - // @ts-ignore - types not important - promises.push(import('whatwg-fetch') as any); + promises.push(import('whatwg-fetch')); } if (!('Symbol' in window) || !('flatMap' in Array.prototype) || !('includes' in Array.prototype)) { - // @ts-ignore - types not important - promises.push(import('core-js/stable') as any); + promises.push(import('core-js/stable')); } promises.push(polyfills); Promise.all(promises).then(() => { - // @ts-ignore - types not important window.searchspring = window.searchspring || {}; - // @ts-ignore - types not important + window.searchspring.managed = true; window.searchspring.build = 'universal'; import('./index'); diff --git a/packages/snap-preact-demo/snap/webpack.modern.js b/packages/snap-preact-demo/snap/webpack.modern.js index 1e2e7d00a..24f0f2678 100644 --- a/packages/snap-preact-demo/snap/webpack.modern.js +++ b/packages/snap-preact-demo/snap/webpack.modern.js @@ -1,12 +1,16 @@ -const { merge } = require('webpack-merge'); -const common = require('../webpack.common.js'); -const path = require('path'); -const childProcess = require('child_process'); +import { merge } from 'webpack-merge'; +import common from '../webpack.common.js'; +import path from 'path'; +import childProcess from 'child_process'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const branchName = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); -module.exports = merge(common, { +export default merge(common, { mode: 'production', - entry: './snap/src/index.ts', + entry: './snap/src/modern.ts', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', diff --git a/packages/snap-preact-demo/snap/webpack.universal.js b/packages/snap-preact-demo/snap/webpack.universal.js index cca3806fb..a19706ece 100644 --- a/packages/snap-preact-demo/snap/webpack.universal.js +++ b/packages/snap-preact-demo/snap/webpack.universal.js @@ -1,10 +1,14 @@ -const { merge } = require('webpack-merge'); -const common = require('../webpack.common.js'); -const path = require('path'); -const childProcess = require('child_process'); +import { merge } from 'webpack-merge'; +import common from '../webpack.common.js'; +import path from 'path'; +import childProcess from 'child_process'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const branchName = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); -module.exports = merge(common, { +export default merge(common, { mode: 'production', entry: './snap/src/universal.ts', output: { @@ -18,7 +22,7 @@ module.exports = merge(common, { rules: [ { test: /\.(js|jsx)$/, - include: [/node_modules\/@searchspring/, path.resolve(__dirname, 'src'), path.resolve(__dirname, '../')], + include: [/node_modules\/\@searchspring/, path.resolve(__dirname, 'src'), path.resolve(__dirname, '../')], use: { loader: 'babel-loader', options: { diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index 7b62255e1..59ad387a7 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -5,12 +5,49 @@ import { combineMerge } from '../../snap/src/middleware/functions'; import type { SnapTemplatesConfig } from '@searchspring/snap-preact'; const siteId = 'atkzs2'; // at3qqg // at0wbx (for variant options) +// const siteId = '8uyt2m'; + +// const clientConfig = { +// meta: { +// origin: `https://${siteId}.a.searchspring.io`, +// }, +// search: { +// origin: `https://${siteId}.a.searchspring.io`, +// }, +// autocomplete: { +// requesters: { +// suggest: { +// origin: `https://${siteId}.a.searchspring.io`, +// }, +// legacy: { +// origin: `https://${siteId}.a.searchspring.io`, +// }, +// }, +// }, +// finder: { +// origin: `https://${siteId}.a.searchspring.io`, +// }, +// recommend: { +// origin: `https://${siteId}.a.searchspring.io`, +// }, +// suggest: { +// origin: `https://${siteId}.a.searchspring.io`, +// }, +// }; let config: SnapTemplatesConfig = { config: { siteId: siteId, language: 'en', currency: 'usd', platform: 'other', + // client: clientConfig + }, + plugins: { + common: { + addToCart: { + function: (data) => console.log('added to cart!', data), + }, + }, }, components: { result: { @@ -18,7 +55,7 @@ let config: SnapTemplatesConfig = { }, }, theme: { - extends: 'base', + extends: 'pike', //resultComponent: 'CustomResult', variables: { breakpoints: { @@ -34,23 +71,7 @@ let config: SnapTemplatesConfig = { }, style: globalStyles, overrides: { - default: { - // 'autocompleteLayout': { - // className: '', - // width: '200px', - // }, - // 'toolbar.top': { - // layout: ['facetsHorizontal'] - // } - // 'toolbar.bottom': { - // layout: ['loadMore'] - // } - }, - // mobile: { - // 'autocompleteFixed': { - // layout: 'standard' - // } - // } + default: {}, }, }, recommendation: { @@ -79,9 +100,6 @@ let config: SnapTemplatesConfig = { }, ], settings: { - variants: { - field: 'ss_variants', - }, // infinite: { // backfill: 5, // }, diff --git a/packages/snap-preact-demo/templates/src/modern.ts b/packages/snap-preact-demo/templates/src/modern.ts new file mode 100644 index 000000000..d74d0a816 --- /dev/null +++ b/packages/snap-preact-demo/templates/src/modern.ts @@ -0,0 +1,4 @@ +window.searchspring = window.searchspring || {}; +window.searchspring.managed = true; + +import('./index'); diff --git a/packages/snap-preact-demo/templates/src/universal.ts b/packages/snap-preact-demo/templates/src/universal.ts index a6aca5057..f42a6051d 100644 --- a/packages/snap-preact-demo/templates/src/universal.ts +++ b/packages/snap-preact-demo/templates/src/universal.ts @@ -3,14 +3,16 @@ import { polyfills } from '@searchspring/snap-preact'; const promises = []; if (!('fetch' in window)) { - // @ts-ignore - types not important - promises.push(import('whatwg-fetch') as any); + promises.push(import('whatwg-fetch')); } if (!('Symbol' in window) || !('flatMap' in Array.prototype) || !('includes' in Array.prototype)) { - // @ts-ignore - types not important - promises.push(import('core-js/stable') as any); + promises.push(import('core-js/stable')); } promises.push(polyfills); Promise.all(promises).then(() => { + window.searchspring = window.searchspring || {}; + window.searchspring.managed = true; + window.searchspring.build = 'universal'; + import('./index'); }); diff --git a/packages/snap-preact-demo/templates/webpack.modern.js b/packages/snap-preact-demo/templates/webpack.modern.js index d78e3b22c..c6cd7289f 100644 --- a/packages/snap-preact-demo/templates/webpack.modern.js +++ b/packages/snap-preact-demo/templates/webpack.modern.js @@ -1,12 +1,16 @@ -const { merge } = require('webpack-merge'); -const common = require('../webpack.common.js'); -const path = require('path'); -const childProcess = require('child_process'); +import { merge } from 'webpack-merge'; +import common from '../webpack.common.js'; +import path from 'path'; +import childProcess from 'child_process'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const branchName = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); -module.exports = merge(common, { +export default merge(common, { mode: 'production', - entry: './templates/src/index.ts', + entry: './templates/src/modern.ts', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', diff --git a/packages/snap-preact-demo/templates/webpack.universal.js b/packages/snap-preact-demo/templates/webpack.universal.js index 7284ef7bf..dca8d3075 100644 --- a/packages/snap-preact-demo/templates/webpack.universal.js +++ b/packages/snap-preact-demo/templates/webpack.universal.js @@ -1,10 +1,14 @@ -const { merge } = require('webpack-merge'); -const common = require('../webpack.common.js'); -const path = require('path'); -const childProcess = require('child_process'); +import { merge } from 'webpack-merge'; +import common from '../webpack.common.js'; +import path from 'path'; +import childProcess from 'child_process'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const branchName = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); -module.exports = merge(common, { +export default merge(common, { mode: 'production', entry: './templates/src/universal.ts', output: { @@ -18,7 +22,7 @@ module.exports = merge(common, { rules: [ { test: /\.(js|jsx)$/, - include: [/node_modules\/@searchspring/, path.resolve(__dirname, 'src'), path.resolve(__dirname, '../')], + include: [/node_modules\/\@searchspring/, path.resolve(__dirname, 'src'), path.resolve(__dirname, '../')], use: { loader: 'babel-loader', options: { diff --git a/packages/snap-preact-demo/tests/cypress/e2e/autocomplete/autocomplete.cy.js b/packages/snap-preact-demo/tests/cypress/e2e/autocomplete/autocomplete.cy.js index 48d4c3b0d..a418d1209 100644 --- a/packages/snap-preact-demo/tests/cypress/e2e/autocomplete/autocomplete.cy.js +++ b/packages/snap-preact-demo/tests/cypress/e2e/autocomplete/autocomplete.cy.js @@ -186,7 +186,7 @@ describe('Autocomplete', () => { cy.get(`${config.selectors.autocomplete.result} a:first`) .should('have.length.greaterThan', 0) .each((result, index) => { - cy.get(result).should('have.attr', 'href', store.results[index].mappings.core.url); + cy.get(result).should('have.attr', 'href', store.results[index].display.mappings.core.url); }); }); }); diff --git a/packages/snap-preact-demo/tests/cypress/e2e/recommendation/recommendation.cy.js b/packages/snap-preact-demo/tests/cypress/e2e/recommendation/recommendation.cy.js index c436085ed..2f6b2cac8 100644 --- a/packages/snap-preact-demo/tests/cypress/e2e/recommendation/recommendation.cy.js +++ b/packages/snap-preact-demo/tests/cypress/e2e/recommendation/recommendation.cy.js @@ -135,7 +135,7 @@ describe('Recommendations', () => { const newerActiveIndex = doc .querySelector(`${integration?.selectors?.recommendation.activeSlide}`) .getAttribute('data-swiper-slide-index'); - const storeTitle = store.results[parseInt(newerActiveIndex)].mappings.core.name; + const storeTitle = store.results[parseInt(newerActiveIndex)].display.mappings.core.name; //should have changed expect(newActive).to.not.equal(intialActive); diff --git a/packages/snap-preact-demo/tests/cypress/e2e/recommendation/recommendationBundle.cy.js b/packages/snap-preact-demo/tests/cypress/e2e/recommendation/recommendationBundle.cy.js index 27ff9b1dc..4e3ab359c 100644 --- a/packages/snap-preact-demo/tests/cypress/e2e/recommendation/recommendationBundle.cy.js +++ b/packages/snap-preact-demo/tests/cypress/e2e/recommendation/recommendationBundle.cy.js @@ -142,7 +142,7 @@ describe('BundledRecommendations', () => { //get the new active again const newerActiveIndex = doc.querySelector(`${config?.selectors?.recommendation.activeSlide}`).getAttribute('aria-label').split(' ')[0]; - const storeTitle = store.results[parseInt(newerActiveIndex)].mappings.core.name; + const storeTitle = store.results[parseInt(newerActiveIndex)].display.mappings.core.name; //should have changed expect(newActive).to.not.equal(intialActive); diff --git a/packages/snap-preact-demo/tests/cypress/e2e/templates/autocomplete/autocomplete.cy.js b/packages/snap-preact-demo/tests/cypress/e2e/templates/autocomplete/autocomplete.cy.js index 28ad06eab..3cb581a8e 100644 --- a/packages/snap-preact-demo/tests/cypress/e2e/templates/autocomplete/autocomplete.cy.js +++ b/packages/snap-preact-demo/tests/cypress/e2e/templates/autocomplete/autocomplete.cy.js @@ -184,7 +184,7 @@ describe('Autocomplete', () => { cy.get(`${config.selectors.autocomplete.result} a:first`) .should('have.length.greaterThan', 0) .each((result, index) => { - cy.get(result).should('have.attr', 'href', store.results[index].mappings.core.url); + cy.get(result).should('have.attr', 'href', store.results[index].display.mappings.core.url); }); }); }); diff --git a/packages/snap-preact-demo/tests/cypress/e2e/templates/recommendation/recommendation.cy.js b/packages/snap-preact-demo/tests/cypress/e2e/templates/recommendation/recommendation.cy.js index 8f903d064..a747644fa 100644 --- a/packages/snap-preact-demo/tests/cypress/e2e/templates/recommendation/recommendation.cy.js +++ b/packages/snap-preact-demo/tests/cypress/e2e/templates/recommendation/recommendation.cy.js @@ -169,7 +169,7 @@ describe('Recommendations', () => { const newerActiveIndex = doc .querySelector(`${integration?.selectors?.recommendation.activeSlide}`) .getAttribute('data-swiper-slide-index'); - const storeTitle = store.results[parseInt(newerActiveIndex)].mappings.core.name; + const storeTitle = store.results[parseInt(newerActiveIndex)].display.mappings.core.name; expect(newActive).to.not.equal(intialActive); expect(newActive).to.equal(storeTitle); diff --git a/packages/snap-preact-demo/tests/cypress/e2e/templates/recommendationBundle/recommendationBundle.cy.js b/packages/snap-preact-demo/tests/cypress/e2e/templates/recommendationBundle/recommendationBundle.cy.js index 9cd4c4f24..2eefc4ea2 100644 --- a/packages/snap-preact-demo/tests/cypress/e2e/templates/recommendationBundle/recommendationBundle.cy.js +++ b/packages/snap-preact-demo/tests/cypress/e2e/templates/recommendationBundle/recommendationBundle.cy.js @@ -170,7 +170,7 @@ describe('BundledRecommendations', () => { //get the new active again const newerActiveIndex = doc.querySelector(`${config?.selectors?.recommendation.activeSlide}`).getAttribute('aria-label').split(' ')[0]; - const storeTitle = store.results[parseInt(newerActiveIndex)].mappings.core.name; + const storeTitle = store.results[parseInt(newerActiveIndex)].display.mappings.core.name; //should have changed expect(newActive).to.not.equal(intialActive); diff --git a/packages/snap-preact-demo/tests/cypress/e2e/tracking/track.cy.js b/packages/snap-preact-demo/tests/cypress/e2e/tracking/track.cy.js index 3acb8f973..7fedba468 100644 --- a/packages/snap-preact-demo/tests/cypress/e2e/tracking/track.cy.js +++ b/packages/snap-preact-demo/tests/cypress/e2e/tracking/track.cy.js @@ -200,6 +200,42 @@ describe('Tracking Beacon 2.0', () => { }); }); + it('tracked autocomplete impression only once per unique search query', () => { + let counter = 0; + cy.intercept('POST', /beacon.searchspring.io\/beacon\/v2\/.*\/autocomplete\/impression/, (req) => { + counter++; + req.reply({ success: true }); + }).as('beacon2/autocomplete/impression-custom'); + + cy.visit('https://localhost:2222'); + cy.get('input[name="q"]').type('s'); + cy.wait(`@beacon2/autocomplete/impression-custom`).then(({ request, response }) => { + expect(response.body).to.have.property('success').to.equal(true); + + expect(counter).to.equal(1); + + cy.intercept('POST', /beacon.searchspring.io\/beacon\/v2\/.*\/autocomplete\/impression/, (req) => { + counter++; + req.reply({ success: true }); + }); + + cy.get('.ss__autocomplete__terms__option--active') + .first() + .invoke('text') + .then((activeTermText) => { + expect(activeTermText).to.be.a('string'); + expect(activeTermText.length).to.be.greaterThan(1); + + const { data } = JSON.parse(request.body); + expect(data).to.have.property('q').to.be.a('string').and.to.equal(activeTermText); + cy.get('input[name="q"]').type(activeTermText.substring(1, 2)); + cy.wait(2000).then(() => { + expect(counter).to.equal(1); + }); + }); + }); + }); + it('tracked recommendation render, impression, clickthrough', () => { cy.visit('https://localhost:2222/snap/product.html'); diff --git a/packages/snap-preact-demo/webpack.build.js b/packages/snap-preact-demo/webpack.build.js index 7c85d940f..d8f760771 100644 --- a/packages/snap-preact-demo/webpack.build.js +++ b/packages/snap-preact-demo/webpack.build.js @@ -1,6 +1,6 @@ -const modern = require('./snap/webpack.modern.js'); -const modernTemplates = require('./templates/webpack.modern.js'); -const universal = require('./snap/webpack.universal.js'); -const universalTemplates = require('./templates/webpack.universal.js'); +import modern from './snap/webpack.modern.js'; +import modernTemplates from './templates/webpack.modern.js'; +import universal from './snap/webpack.universal.js'; +import universalTemplates from './templates/webpack.universal.js'; -module.exports = [modern, modernTemplates, universal, universalTemplates]; +export default [modern, modernTemplates, universal, universalTemplates]; diff --git a/packages/snap-preact-demo/webpack.common.js b/packages/snap-preact-demo/webpack.common.js index d7628104c..87d6ebd35 100644 --- a/packages/snap-preact-demo/webpack.common.js +++ b/packages/snap-preact-demo/webpack.common.js @@ -1,5 +1,5 @@ -const webpack = require('webpack'); -const childProcess = require('child_process'); +import webpack from 'webpack'; +import childProcess from 'child_process'; // determine branch name for branch override usage const branchName = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim(); @@ -7,7 +7,7 @@ const branchName = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toSt // class name for for branch override usage const styleClass = 'ss-snap-bundle-styles'; -module.exports = { +export default { stats: { modulesSort: 'size', modulesSpace: 70, diff --git a/packages/snap-preact-demo/webpack.dev.js b/packages/snap-preact-demo/webpack.dev.js index 0bb3dcf94..1ebab46e0 100644 --- a/packages/snap-preact-demo/webpack.dev.js +++ b/packages/snap-preact-demo/webpack.dev.js @@ -1,5 +1,9 @@ -const { merge } = require('webpack-merge'); -const path = require('path'); +import { merge } from 'webpack-merge'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); // This is used to create the initial dist directory in the virtual dev server // Other builds require that the dist directory already exists before creating the subdirectories 'snap' and 'templates' @@ -36,7 +40,7 @@ const devServer = { }, }; -const modernProd = require('./snap/webpack.modern.js'); +import modernProd from './snap/webpack.modern.js'; const modern = { ...merge(modernProd, { module: { @@ -71,7 +75,7 @@ const modern = { devtool: 'source-map', }; -const modernTemplatesProd = require('./templates/webpack.modern.js'); +import modernTemplatesProd from './templates/webpack.modern.js'; const modernTemplates = { ...merge(modernTemplatesProd, { module: { @@ -106,14 +110,14 @@ const modernTemplates = { devtool: 'source-map', }; -const universalProd = require('./snap/webpack.universal.js'); +import universalProd from './snap/webpack.universal.js'; const universal = { ...merge(universalProd, { module: { rules: [ { test: /\.(js|jsx)$/, - include: [/node_modules\/@searchspring/, path.resolve(__dirname, 'src'), path.resolve(__dirname, '../')], + include: [/node_modules\/\@searchspring/, path.resolve(__dirname, 'src'), path.resolve(__dirname, '../')], use: { loader: 'babel-loader', options: { @@ -141,14 +145,14 @@ const universal = { devtool: 'source-map', }; -const universalTemplatesProd = require('./templates/webpack.universal.js'); +import universalTemplatesProd from './templates/webpack.universal.js'; const universalTemplates = { ...merge(universalTemplatesProd, { module: { rules: [ { test: /\.(js|jsx)$/, - include: [/node_modules\/@searchspring/, path.resolve(__dirname, 'src'), path.resolve(__dirname, '../')], + include: [/node_modules\/\@searchspring/, path.resolve(__dirname, 'src'), path.resolve(__dirname, '../')], use: { loader: 'babel-loader', options: { @@ -175,4 +179,4 @@ const universalTemplates = { devtool: 'source-map', }; -module.exports = [devServer, modern, modernTemplates, universal, universalTemplates]; +export default [devServer, modern, modernTemplates, universal, universalTemplates]; diff --git a/packages/snap-preact/CHANGELOG.md b/packages/snap-preact/CHANGELOG.md index a817bbc9e..3f9dfa950 100644 --- a/packages/snap-preact/CHANGELOG.md +++ b/packages/snap-preact/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [1.11.0](https://github.com/searchspring/snap/compare/v1.10.1...v1.11.0) (2026-01-15) + +### Bug Fixes + +- **preact/components/facet:** adjusting new facet input addition ([9823f76](https://github.com/searchspring/snap/commit/9823f7604db63fd894f9960b01fc94330bdb3626)) +- **recommmendationbundle:** fixing merge conflicts, and bugfix around cta slot not working with lang ([4dc4759](https://github.com/searchspring/snap/commit/4dc4759fb32d6216826b5d71e27683e5b593b8c3)) +- **snap-preact:** adding additional checks around all cookie/storage usage to ensure things work ([0f6d8e7](https://github.com/searchspring/snap/commit/0f6d8e735c3d3c7eea7cfba6cf59509fafb819bb)) +- **storybook:** update all broken stories that use old siteId to new athos siteid ([ec1ac5f](https://github.com/searchspring/snap/commit/ec1ac5fd97726a36e82f17086e449d1672576c79)) + +### Features + +- **preact/components/facet:** adding range facet inputs and the ability to use filterFormatValue for filters ([ef16169](https://github.com/searchspring/snap/commit/ef16169123f749d45cf4ab4c7d29375c06ab16da)) +- **snap.tsx:** adding support for configurable initiator ([f286c5c](https://github.com/searchspring/snap/commit/f286c5c5f416ddad5771002bf00a0dc2e570922f)) + ## [1.10.1](https://github.com/searchspring/snap/compare/v1.10.0...v1.10.1) (2025-10-06) **Note:** Version bump only for package @searchspring/snap-preact @@ -252,6 +266,50 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - support template themes in snap-preact ([df9d905](https://github.com/searchspring/snap/commit/df9d9057a1554d7eb62830002c3db990e0f8f272)) - theme reactivity initial interface ([c017aad](https://github.com/searchspring/snap/commit/c017aadd4add7591bd98fcfbdb8db8985bf2d498)) +## [0.72.1](https://github.com/searchspring/snap/compare/v0.72.0...v0.72.1) (2026-01-15) + +**Note:** Version bump only for package @searchspring/snap-preact + +# [0.72.0](https://github.com/searchspring/snap/compare/v0.71.0...v0.72.0) (2026-01-14) + +### Features + +- **snap.tsx:** adding support for configurable initiator ([f286c5c](https://github.com/searchspring/snap/commit/f286c5c5f416ddad5771002bf00a0dc2e570922f)) + +# [0.71.0](https://github.com/searchspring/snap/compare/v0.70.1...v0.71.0) (2025-11-26) + +**Note:** Version bump only for package @searchspring/snap-preact + +## [0.70.1](https://github.com/searchspring/snap/compare/v0.70.0...v0.70.1) (2025-11-17) + +**Note:** Version bump only for package @searchspring/snap-preact + +# [0.70.0](https://github.com/searchspring/snap/compare/v0.69.2...v0.70.0) (2025-11-13) + +**Note:** Version bump only for package @searchspring/snap-preact + +## [0.69.2](https://github.com/searchspring/snap/compare/v0.69.1...v0.69.2) (2025-11-04) + +**Note:** Version bump only for package @searchspring/snap-preact + +## [0.69.1](https://github.com/searchspring/snap/compare/v0.69.0...v0.69.1) (2025-10-23) + +**Note:** Version bump only for package @searchspring/snap-preact + +# [0.69.0](https://github.com/searchspring/snap/compare/v0.68.0...v0.69.0) (2025-10-16) + +### Bug Fixes + +- **snap-preact:** adding additional checks around all cookie/storage usage to ensure things work ([0f6d8e7](https://github.com/searchspring/snap/commit/0f6d8e735c3d3c7eea7cfba6cf59509fafb819bb)) + +# [0.68.0](https://github.com/searchspring/snap/compare/v0.67.5...v0.68.0) (2025-08-18) + +**Note:** Version bump only for package @searchspring/snap-preact + +## [0.67.5](https://github.com/searchspring/snap/compare/v0.67.4...v0.67.5) (2025-08-11) + +**Note:** Version bump only for package @searchspring/snap-preact + ## [0.67.4](https://github.com/searchspring/snap/compare/v0.67.3...v0.67.4) (2025-07-29) **Note:** Version bump only for package @searchspring/snap-preact diff --git a/packages/snap-preact/README.md b/packages/snap-preact/README.md index b1dfb0502..f3d8418a6 100644 --- a/packages/snap-preact/README.md +++ b/packages/snap-preact/README.md @@ -1,7 +1,5 @@ # Snap Preact -NPM Status - Snap Preact is an abstraction layer that provides a config based interface for creating a Searchspring integration quickly. @@ -15,7 +13,7 @@ npm install --save @searchspring/snap-preact ## Instantiation -```typescript +```js import { Snap } from '@searchspring/snap-preact'; const snap = new Snap(config); @@ -26,7 +24,7 @@ A configuration object provided to Snap will determine the services that will be Full example: -```typescript +```js const config = { context: globalContext, url: { @@ -38,7 +36,7 @@ const config = { }, client: { globals: { - siteId: 'xxxxxx', + siteId: 'REPLACE_WITH_YOUR_SITE_ID', }, }, controllers: { @@ -90,18 +88,20 @@ const config = { }; ``` -### `config.context` - optional `Context` object to be used to set the global context. If no context is provided, a default context taken from the integration script (`shopper` variable) will be used, otherwise the provided `config.context` is merged with the script context. This context becomes the `globalContext` that is passed to all controllers that are created. +### config.context + +Optional `Context` object to be used to set the global context. If no context is provided, a default context taken from the integration script (`shopper` variable) will be used, otherwise the provided `config.context` is merged with the script context. This context becomes the `globalContext` that is passed to all controllers that are created. ### config.client A single client instance will be created and shared across all services using the provided config. See [@searchspring/snap-client](https://github.com/searchspring/snap/tree/main/packages/snap-client) documentation for full client config options. -```typescript +```js const config = { client: { globals: { - siteId: 'xxxxxx' + siteId: 'REPLACE_WITH_YOUR_SITE_ID' } } } @@ -110,7 +110,7 @@ const config = { ### config.instantiators The `instantiators` object must be defined if any Recommendation controllers have also been defined via `config.controllers.recommendation` -```typescript +```js const config = { instantiators: { recommendation: { @@ -148,7 +148,7 @@ const config = { ### config.url The `url` object contains the config provided to each [`UrlTranslator`](https://github.com/searchspring/snap/tree/main/packages/snap-url-manager/src/Translators/Url) created by Snap Preact. -```typescript +```js const config = { url: { parameters: { @@ -171,7 +171,7 @@ Available controllers: - [FinderController](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Finder) - [RecommendationController](https://github.com/searchspring/snap/tree/main/packages/snap-controller/src/Recommendation) -```typescript +```js const config = { controllers: { search: [], @@ -188,7 +188,7 @@ Each array entry contains an object with the following properties: `targets` - optional array of Target objects. Targets that have been found will have the corresponding controller provided to the target component `controller` prop and the controller's `search` method invoked. -```typescript +```js type ExtendedTarget = { selector: string; inject?: { @@ -215,7 +215,7 @@ type ExtendedTarget = { An example creating a SearchController: -```typescript +```js const config = { controllers: { search: [ @@ -246,7 +246,7 @@ The controller config `id` will be the name of the controller that you will then For example, if using the `config` example above: -```typescript +```js const snap = new Snap(config); const { search } = snap.controllers; ``` @@ -281,7 +281,7 @@ A reference to `RecommendationInstantiator` instance if creating recommendation Snap Preact provides various polyfills to ensure legacy browser support. -```typescript +```js import { polyfills } from '@searchspring/snap-preact'; polyfills.then(() => { diff --git a/packages/snap-preact/components/.storybook/athos_icon.svg b/packages/snap-preact/components/.storybook/athos_icon.svg new file mode 100644 index 000000000..74d675461 --- /dev/null +++ b/packages/snap-preact/components/.storybook/athos_icon.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/snap-preact/components/.storybook/athos_logo.svg b/packages/snap-preact/components/.storybook/athos_logo.svg new file mode 100644 index 000000000..d7eef71c5 --- /dev/null +++ b/packages/snap-preact/components/.storybook/athos_logo.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/snap-preact/components/.storybook/main.ts b/packages/snap-preact/components/.storybook/main.ts index 86630f1f3..f6d705e08 100644 --- a/packages/snap-preact/components/.storybook/main.ts +++ b/packages/snap-preact/components/.storybook/main.ts @@ -14,12 +14,18 @@ const config: StorybookConfig = { // altering webpack config webpackFinal: async (config) => { - // typescript + // typescript - use esbuild-loader for much faster compilation config.module?.rules?.push({ test: /\.(ts|tsx)$/, use: [ { - loader: require.resolve('ts-loader'), + loader: require.resolve('esbuild-loader'), + options: { + loader: 'tsx', + target: 'es2020', + jsx: 'automatic', + jsxImportSource: '@emotion/react', + }, }, ], }); @@ -33,6 +39,32 @@ const config: StorybookConfig = { use: ['style-loader', 'css-loader', 'sass-loader'], }); + // optimize webpack for faster rebuilds + if (config.cache) { + config.cache = { + type: 'filesystem', + buildDependencies: { + config: [__filename], + }, + }; + } + + // optimize module resolution + if (config.resolve) { + config.resolve.symlinks = false; + } + + // use esbuild for minification (faster than terser) + if (config.optimization) { + const { EsbuildPlugin } = require('esbuild-loader'); + config.optimization.minimizer = [ + new EsbuildPlugin({ + target: 'es2020', + css: true, + }), + ]; + } + return config; }, }; diff --git a/packages/snap-preact/components/.storybook/searchspringTheme.ts b/packages/snap-preact/components/.storybook/searchspringTheme.ts index d41e31e70..c4f8283c2 100644 --- a/packages/snap-preact/components/.storybook/searchspringTheme.ts +++ b/packages/snap-preact/components/.storybook/searchspringTheme.ts @@ -1,17 +1,17 @@ import { create } from '@storybook/theming/create'; -import searchspringLogo from './searchspring-logo.svg'; +import athosLogo from './athos_logo.svg'; export default create({ base: 'light', - colorPrimary: '#3a23ad', - colorSecondary: '#00cee1', + colorPrimary: '#1D4990', + colorSecondary: '#00AEEF', appBg: '#fafafa', // Toolbar default and active colors - barSelectedColor: '#3a23ad', + barSelectedColor: '#1D4990', brandTitle: 'Searchspring Snap Preact', - brandImage: searchspringLogo, + brandImage: athosLogo, }); diff --git a/packages/snap-preact/components/src/assets/athos_icon.svg b/packages/snap-preact/components/src/assets/athos_icon.svg new file mode 100644 index 000000000..74d675461 --- /dev/null +++ b/packages/snap-preact/components/src/assets/athos_icon.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/snap-preact/components/src/assets/athos_logo.svg b/packages/snap-preact/components/src/assets/athos_logo.svg new file mode 100644 index 000000000..d7eef71c5 --- /dev/null +++ b/packages/snap-preact/components/src/assets/athos_logo.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/snap-preact/components/src/assets/searchspring-logo.svg b/packages/snap-preact/components/src/assets/searchspring-logo.svg new file mode 100644 index 000000000..bbebd35ba --- /dev/null +++ b/packages/snap-preact/components/src/assets/searchspring-logo.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/snap-preact/components/src/components/Atoms/BadgeImage/readme.md b/packages/snap-preact/components/src/components/Atoms/BadgeImage/readme.md index f4ddb644e..89c1eca54 100644 --- a/packages/snap-preact/components/src/components/Atoms/BadgeImage/readme.md +++ b/packages/snap-preact/components/src/components/Atoms/BadgeImage/readme.md @@ -4,6 +4,10 @@ Renders an image badge. It is expected to be used with `CalloutBadge` and `Overl ## Usage +```jsx +import { BadgeImage } from '@searchspring/snap-preact-components'; +``` + ### url The required `url` prop specifies the badge image `src` attribute. diff --git a/packages/snap-preact/components/src/components/Atoms/BadgePill/readme.md b/packages/snap-preact/components/src/components/Atoms/BadgePill/readme.md index 3b233fea0..c839e0967 100644 --- a/packages/snap-preact/components/src/components/Atoms/BadgePill/readme.md +++ b/packages/snap-preact/components/src/components/Atoms/BadgePill/readme.md @@ -4,6 +4,10 @@ Renders a text badge in the shape of a pill. It is expected to be used with `Cal ## Usage +```jsx +import { BadgePill } from '@searchspring/snap-preact-components'; +``` + ### value The required `value` prop specifies the badge text contents. diff --git a/packages/snap-preact/components/src/components/Atoms/BadgeRectangle/readme.md b/packages/snap-preact/components/src/components/Atoms/BadgeRectangle/readme.md index 8139da211..0f187bffd 100644 --- a/packages/snap-preact/components/src/components/Atoms/BadgeRectangle/readme.md +++ b/packages/snap-preact/components/src/components/Atoms/BadgeRectangle/readme.md @@ -3,6 +3,9 @@ Renders a text badge in the shape of a rectangle. It is expected to be used with `CalloutBadge` and `OverlayBadge` components. ## Usage +```jsx +import { BadgeRectangle } from '@searchspring/snap-preact-components'; +``` ### value The required `value` prop specifies the badge text contents. diff --git a/packages/snap-preact/components/src/components/Atoms/BadgeText/readme.md b/packages/snap-preact/components/src/components/Atoms/BadgeText/readme.md index f23b43344..a572b0aa3 100644 --- a/packages/snap-preact/components/src/components/Atoms/BadgeText/readme.md +++ b/packages/snap-preact/components/src/components/Atoms/BadgeText/readme.md @@ -3,6 +3,9 @@ Renders a text badge. It is expected to be used with `CalloutBadge` and `OverlayBadge` components. ## Usage +```jsx +import { BadgeText } from '@searchspring/snap-preact-components'; +``` ### value The required `value` prop specifies the badge text contents. diff --git a/packages/snap-preact/components/src/components/Atoms/Breadcrumbs/readme.md b/packages/snap-preact/components/src/components/Atoms/Breadcrumbs/readme.md index 3f2a33315..21a545105 100644 --- a/packages/snap-preact/components/src/components/Atoms/Breadcrumbs/readme.md +++ b/packages/snap-preact/components/src/components/Atoms/Breadcrumbs/readme.md @@ -3,6 +3,9 @@ Renders a list of breadcrumbs. ## Usage +```jsx +import { Breadcrumbs } from '@searchspring/snap-preact-components'; +``` ### data The `data` prop specifies an array of breadcrumb objects, or a function that returns an array of breadcrumb objects. The function is passed the controller if available. @@ -13,7 +16,7 @@ The `data` prop specifies an array of breadcrumb objects, or a function that ret `url` - optional, the URL of this breadcrumb -```typescript +```js const breadcrumbs = [ { url: '/', label: 'Home' }, { url: '/', label: 'Collections' }, diff --git a/packages/snap-preact/components/src/components/Atoms/Button/Button.tsx b/packages/snap-preact/components/src/components/Atoms/Button/Button.tsx index 9432899e3..879737a0a 100644 --- a/packages/snap-preact/components/src/components/Atoms/Button/Button.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Button/Button.tsx @@ -36,8 +36,7 @@ const defaultStyles: StyleScript = ({ native, color, backgroundColo backgroundColor: lightenedPrimaryColorObj.hex() || '#f8f8f8', }, '&.ss__button--disabled': { - opacity: 0.3, - backgroundColor: 'initial', + opacity: 0.7, '&:hover': { cursor: 'default', }, @@ -159,6 +158,7 @@ export interface ButtonProps extends ComponentProps { onClick?: (e: React.MouseEvent) => void; disableA11y?: boolean; lang?: Partial; + name?: ButtonNames; } export interface ButtonLang { @@ -172,6 +172,11 @@ export type ButtonNames = | 'slideout' | 'sidebar-toggle' | 'see-more' + | 'next' + | 'prev' + | 'pause' + | 'pagination' + | 'pagination-current' | 'close-search' | 'clear-search' | 'submit-search' diff --git a/packages/snap-preact/components/src/components/Atoms/Button/readme.md b/packages/snap-preact/components/src/components/Atoms/Button/readme.md index bb646c0e5..65bb9806d 100644 --- a/packages/snap-preact/components/src/components/Atoms/Button/readme.md +++ b/packages/snap-preact/components/src/components/Atoms/Button/readme.md @@ -3,6 +3,9 @@ Renders a native or custom button. ## Usage +```jsx +import { Button } from '@searchspring/snap-preact-components'; +``` ### content The `content` prop specifies the button text. This can be a string or a JSX element. diff --git a/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.stories.tsx b/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.stories.tsx index 4a4a2ab95..b1f19101e 100644 --- a/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.stories.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.stories.tsx @@ -145,6 +145,16 @@ export default { }, action: 'onMouseLeave', }, + usePortal: { + description: 'boolean to specify if the dropdown content should be rendered in a portal.', + table: { + type: { + summary: 'boolean', + }, + defaultValue: { summary: false }, + }, + control: { type: 'boolean' }, + }, onToggle: { description: 'Executes when the internal state changes, gets passed the event and the internal state - used with internal state only', table: { diff --git a/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.test.tsx b/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.test.tsx index 286cdb67d..2c9e9bfd1 100644 --- a/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.test.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.test.tsx @@ -1,3 +1,4 @@ +import '@testing-library/jest-dom'; import { h } from 'preact'; import { render, waitFor } from '@testing-library/preact'; @@ -361,6 +362,20 @@ describe('Dropdown Component', () => { expect(dropdown).toHaveClass('ss__dropdown--open'); }); + it('renders content in a portal when usePortal is true', async () => { + const contentText = 'portal content'; + const rendered = render(); + + const button = rendered.getByText('open me'); + await userEvent.click(button); + + const childContent = rendered.container.querySelector('.ss__dropdown .ss__dropdown__portal'); + const portalContent = document.body.querySelector('.ss__dropdown__portal'); + expect(portalContent).toBeInTheDocument(); + expect(childContent).not.toBeInTheDocument(); + expect(portalContent).toHaveTextContent(contentText); + }); + it('disables styles', () => { const buttonText = 'click me'; const contentText = 'this is the content'; diff --git a/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.tsx b/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.tsx index 825a284d5..dd7ccd933 100644 --- a/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Dropdown/Dropdown.tsx @@ -1,5 +1,6 @@ import { ComponentChildren, h } from 'preact'; -import { useState, StateUpdater, MutableRef } from 'preact/hooks'; +import { createPortal } from 'preact/compat'; +import { useState, StateUpdater, MutableRef, useRef, useEffect } from 'preact/hooks'; import { jsx, css } from '@emotion/react'; import classnames from 'classnames'; @@ -14,7 +15,7 @@ import { useA11y } from '../../../hooks/useA11y'; const defaultStyles: StyleScript = ({ disableOverlay }) => { return css({ position: 'relative', - '&.ss__dropdown--open': { + '&.ss__dropdown--open, &.ss__dropdown__portal--open': { '& .ss__dropdown__content': { position: disableOverlay ? 'relative' : undefined, visibility: 'visible', @@ -67,6 +68,7 @@ export const Dropdown = observer((properties: DropdownProps): JSX.Element => { className, internalClassName, treePath, + usePortal, } = props; let dropdownOpen: boolean | undefined, setDropdownOpen: undefined | StateUpdater; @@ -81,9 +83,16 @@ export const Dropdown = observer((properties: DropdownProps): JSX.Element => { // state to track touch interactions const [isTouchInteraction, setIsTouchInteraction] = useState(false); + const buttonRef = useRef(null); + const contentRef = useRef(null); + const [coords, setCoords] = useState({ top: 0, left: 0, width: 0 }); + let innerRef: MutableRef | undefined; if (!disableClickOutside) { innerRef = useClickOutside((e) => { + if (usePortal && contentRef.current && contentRef.current.contains(e.target as Node)) { + return; + } if (dropdownOpen) { if (!disabled) { stateful && setDropdownOpen && setDropdownOpen(false); @@ -93,6 +102,28 @@ export const Dropdown = observer((properties: DropdownProps): JSX.Element => { }); } + useEffect(() => { + if (usePortal && dropdownOpen) { + const updateCoords = () => { + if (buttonRef.current) { + const rect = buttonRef.current.getBoundingClientRect(); + setCoords({ + top: rect.bottom + window.scrollY, + left: rect.left + window.scrollX, + width: rect.width, + }); + } + }; + updateCoords(); + window.addEventListener('resize', updateCoords); + window.addEventListener('scroll', updateCoords, true); + return () => { + window.removeEventListener('resize', updateCoords); + window.removeEventListener('scroll', updateCoords, true); + }; + } + }, [usePortal, dropdownOpen]); + const toggleOpenDropdown = (e: React.MouseEvent, state?: boolean) => { if (stateful) { setDropdownOpen && @@ -136,6 +167,27 @@ export const Dropdown = observer((properties: DropdownProps): JSX.Element => { }), }; + const contentElement = ( +
{ + contentRef.current = e; + if (!disableA11y) { + useA11y(e, 0, Boolean(focusTrapContent), (e) => { + if (stateful) { + toggleOpenDropdown(e); + } else { + onClick && onClick(e); + } + }); + } + }} + > + {cloneWithProps(content, { open: dropdownOpen, toggleOpen: toggleOpenDropdown, treePath })} + {cloneWithProps(children, { open: dropdownOpen, toggleOpen: toggleOpenDropdown, treePath })} +
+ ); + return (
{ >
(!disableA11y ? useA11y(e) : null)} + ref={(e) => { + buttonRef.current = e; + if (!disableA11y) useA11y(e); + }} aria-expanded={dropdownOpen} role="button" onTouchStart={() => { @@ -169,25 +224,26 @@ export const Dropdown = observer((properties: DropdownProps): JSX.Element => { {cloneWithProps(button, { open: dropdownOpen, toggleOpen: toggleOpenDropdown, treePath })}
- {(content || children) && ( -
- !disableA11y - ? useA11y(e, 0, Boolean(focusTrapContent), (e) => { - if (stateful) { - toggleOpenDropdown(e); - } else { - onClick && onClick(e); - } - }) - : null - } - > - {cloneWithProps(content, { open: dropdownOpen, toggleOpen: toggleOpenDropdown, treePath })} - {cloneWithProps(children, { open: dropdownOpen, toggleOpen: toggleOpenDropdown, treePath })} -
- )} + {!usePortal + ? (content || children) && contentElement + : dropdownOpen && + (content || children) && + createPortal( +
+ {contentElement} +
, + document.body + )}
); @@ -210,4 +266,5 @@ export interface DropdownProps extends ComponentProps { disableClickOutside?: boolean; focusTrapContent?: boolean; disableA11y?: boolean; + usePortal?: boolean; } diff --git a/packages/snap-preact/components/src/components/Atoms/Dropdown/readme.md b/packages/snap-preact/components/src/components/Atoms/Dropdown/readme.md index d8b0e3c28..8ba67c911 100644 --- a/packages/snap-preact/components/src/components/Atoms/Dropdown/readme.md +++ b/packages/snap-preact/components/src/components/Atoms/Dropdown/readme.md @@ -3,6 +3,9 @@ Renders a button and content. Clicking the button toggles content visibility. Typically used as an alternative to a `` element. diff --git a/packages/snap-preact/components/src/components/Molecules/ErrorHandler/readme.md b/packages/snap-preact/components/src/components/Molecules/ErrorHandler/readme.md index 618f32a8f..347932d52 100644 --- a/packages/snap-preact/components/src/components/Molecules/ErrorHandler/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/ErrorHandler/readme.md @@ -7,6 +7,9 @@ Renders error messages. - Button ## Usage +```jsx +import { ErrorHandler } from '@searchspring/snap-preact-components'; +``` ### controller The `controller` prop specifies a reference to a Snap controller. This is the standard usage. @@ -55,7 +58,7 @@ const errorObject = { ## ErrorHandle Types -```typescript +```js import { ErrorType } from '@searchspring/snap-store-mobx'; export enum ErrorType { diff --git a/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.stories.tsx b/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.stories.tsx index 64b58f32a..b18a414dc 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.stories.tsx @@ -137,10 +137,10 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'FacetGridOptions', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'FacetGridOptions', globals: { siteId: 'atkzs2' } }); const ObservableFacetGridOptions = observer(({ args, controller }: { args: FacetGridOptionsProps; controller: SearchController }) => { - const sizeFacet = controller?.store?.facets.filter((facet) => facet.field == 'size_dress').pop(); + const sizeFacet = controller?.store?.facets.filter((facet) => facet.field == 'color').pop(); return ; }); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.test.tsx b/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.test.tsx index 702db9439..8202e1822 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.test.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.test.tsx @@ -6,7 +6,7 @@ import { ThemeProvider } from '../../../providers'; import { FacetGridOptions } from './FacetGridOptions'; import type { FacetValue } from '@searchspring/snap-store-mobx'; import { MockData } from '@searchspring/snap-shared'; -import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@searchspring/snapi-types'; +import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@athoscommerce/snapi-types'; const mockData = new MockData(); const gridFacetMock: SearchResponseModelFacet & SearchResponseModelFacetValueAllOf = mockData diff --git a/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/readme.md b/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/readme.md index 437494350..43c2ae182 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/readme.md @@ -3,6 +3,9 @@ Renders a grid of facet options. ## Usage +```jsx +import { FacetGridOptions } from '@searchspring/snap-preact-components'; +``` ### values The `values` prop specifies all facet values where the facet type is 'grid'. Overrides values passed via the facet prop. @@ -60,7 +63,7 @@ If using within Autocomplete, the `previewOnFocus` prop will invoke the `value.p ### valueProps The `valueProps` prop will be spread onto each value's `` element. Typical usage would be to provide custom callback functions when used within Autocomplete. -```typescript +```js const valueProps = { onMouseEnter: (e) => { clearTimeout(delayTimeout); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.stories.tsx b/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.stories.tsx index df6167978..28d3dfd6d 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.stories.tsx @@ -122,7 +122,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'FacetHierarchyOptions', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'FacetHierarchyOptions', globals: { siteId: 'atkzs2' } }); const ObservableFacetHierarchyOptions = observer(({ args, controller }: { args: FacetHierarchyOptionsProps; controller: SearchController }) => { const hierarchyValues = controller?.store?.facets diff --git a/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.test.tsx b/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.test.tsx index 72044f5a4..93dfe34b7 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.test.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.test.tsx @@ -7,7 +7,7 @@ import { FacetHierarchyOptions } from './FacetHierarchyOptions'; import { SearchFacetStore, StorageStore, type FacetHierarchyValue } from '@searchspring/snap-store-mobx'; import { MockData } from '@searchspring/snap-shared'; -import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@searchspring/snapi-types'; +import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@athoscommerce/snapi-types'; import { QueryStringTranslator, reactLinker, UrlManager } from '@searchspring/snap-url-manager'; const mockData = new MockData(); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/readme.md b/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/readme.md index 7c74c2331..cb751971b 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/readme.md @@ -6,6 +6,9 @@ Renders a list of hierarchy options. - Icon ## Usage +```jsx +import { FacetHierarchyOptions } from '@searchspring/snap-preact-components'; +``` ### values The `values` prop specifies all facet values where the facet type is 'hierarchy'. Overrides values passed via the facet prop. @@ -57,7 +60,7 @@ If using within Autocomplete, the `previewOnFocus` prop will invoke the `value.p ### valueProps The `valueProps` prop will be spread onto each value's `` element. Typical usage would be to provide custom callback functions when used within Autocomplete. -```typescript +```js const valueProps = { onMouseEnter: (e) => { clearTimeout(delayTimeout); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.stories.tsx b/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.stories.tsx index 7dbb6a180..fc25e9953 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.stories.tsx @@ -141,10 +141,10 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'FacetListOptions', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'FacetListOptions', globals: { siteId: 'atkzs2' } }); const ObservableFacetListOptions = observer(({ args, controller }: { args: FacetListOptionsProps; controller: SearchController }) => { - const brandFacet = controller?.store?.facets.filter((facet) => facet.field == 'brand').pop(); + const brandFacet = controller?.store?.facets.filter((facet) => facet.field == 'color').pop(); return ; }); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.test.tsx b/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.test.tsx index 877daf808..ac00cdd4e 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.test.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.test.tsx @@ -7,7 +7,7 @@ import { FacetListOptions } from './FacetListOptions'; import { SearchFacetStore, StorageStore, type FacetValue } from '@searchspring/snap-store-mobx'; import { MockData } from '@searchspring/snap-shared'; -import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@searchspring/snapi-types'; +import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@athoscommerce/snapi-types'; import { QueryStringTranslator, reactLinker, UrlManager } from '@searchspring/snap-url-manager'; const mockData = new MockData(); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetListOptions/readme.md b/packages/snap-preact/components/src/components/Molecules/FacetListOptions/readme.md index a8fc4f340..7f31e208a 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetListOptions/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/FacetListOptions/readme.md @@ -7,6 +7,9 @@ Renders a list of facet options. - Radio ## Usage +```jsx +import { FacetListOptions } from '@searchspring/snap-preact-components'; +``` ### values The `values` prop specifies all facet values where the facet type is 'list'. Overrides values passed via the facet prop. @@ -72,7 +75,7 @@ If using within Autocomplete, the `previewOnFocus` prop will invoke the `value.p ### valueProps The `valueProps` prop will be spread onto each value's `` element. Typical usage would be to provide custom callback functions when used within Autocomplete. -```typescript +```js const valueProps = { onMouseEnter: (e) => { clearTimeout(delayTimeout); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.stories.tsx b/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.stories.tsx index 15b07b22f..2b0bc792a 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.stories.tsx @@ -200,10 +200,10 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'FacetPaletteOptions', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'FacetPaletteOptions', globals: { siteId: 'atkzs2' } }); const ObservableFacetPaletteOptions = observer(({ args, controller }: { args: FacetPaletteOptionsProps; controller: SearchController }) => { - const sizeFacet = controller?.store?.facets.filter((facet) => facet.field == 'color_family').pop(); + const sizeFacet = controller?.store?.facets.filter((facet) => facet.field == 'color').pop(); return ; }); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.test.tsx b/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.test.tsx index 3f61e398a..cfb599c58 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.test.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.test.tsx @@ -7,7 +7,7 @@ import { ThemeProvider } from '../../../providers'; import type { FacetValue } from '@searchspring/snap-store-mobx'; import { MockData } from '@searchspring/snap-shared'; -import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@searchspring/snapi-types'; +import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@athoscommerce/snapi-types'; const mockData = new MockData(); const paletteFacetMock: SearchResponseModelFacet & SearchResponseModelFacetValueAllOf = mockData diff --git a/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/readme.md b/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/readme.md index 4d0e9f71c..6ccf8fbdb 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/readme.md @@ -6,6 +6,9 @@ Renders a grid of facet palette options. - Icon ## Usage +```jsx +import { FacetPaletteOptions } from '@searchspring/snap-preact-components'; +``` ### values The `values` prop specifies all facet values where the facet type is 'palette'. Overrides values passed via the facet prop. @@ -97,7 +100,7 @@ If using within Autocomplete, the `previewOnFocus` prop will invoke the `value.p ### valueProps The `valueProps` prop will be spread onto each value's `` element. Typical usage would be to provide custom callback functions when used within Autocomplete. -```typescript +```js const valueProps = { onMouseEnter: (e) => { clearTimeout(delayTimeout); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.stories.tsx b/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.stories.tsx index a09a016fa..a45eae4ce 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.stories.tsx @@ -144,6 +144,16 @@ export default { }, control: { type: 'boolean' }, }, + separateHandles: { + description: 'separates slider handles by one step value to prevent min and max from being equal', + table: { + type: { + summary: 'boolean', + }, + defaultValue: { summary: false }, + }, + control: { type: 'boolean' }, + }, handleDraggingColor: { description: 'Slider handle color when dragging', table: { @@ -164,7 +174,8 @@ export default { action: 'onDrag', }, onChange: { - description: 'Slider onChange event handler - fires after touchEnd (used to trigger search)', + description: + 'Slider onChange event handler - fires after touchEnd and before URL manager updates (used to trigger search and allows for value mutation)', table: { type: { summary: 'function', diff --git a/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.test.tsx b/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.test.tsx index 19dbbb6c9..f44673e68 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.test.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.test.tsx @@ -9,7 +9,7 @@ import { sprintf } from '../../../utilities'; import type { RangeFacet } from '@searchspring/snap-store-mobx'; import { MockData } from '@searchspring/snap-shared'; -import { SearchResponseModelFacet, SearchResponseModelFacetRangeAllOf } from '@searchspring/snapi-types'; +import { SearchResponseModelFacet, SearchResponseModelFacetRangeAllOf } from '@athoscommerce/snapi-types'; const mockData = new MockData(); const sliderFacetMock: SearchResponseModelFacet & SearchResponseModelFacetRangeAllOf = mockData @@ -17,6 +17,25 @@ const sliderFacetMock: SearchResponseModelFacet & SearchResponseModelFacetRangeA .facets!.filter((facet) => facet.type == 'range')! .pop()!; +// Mock react-ranger +jest.mock('react-ranger', () => ({ + useRanger: jest.fn((options) => { + const { values } = options; + return { + getTrackProps: () => ({}), + ticks: [], + segments: [{ getSegmentProps: () => ({}) }, { getSegmentProps: () => ({}) }, { getSegmentProps: () => ({}) }], + handles: values.map((value: number) => ({ + value, + active: false, + getHandleProps: () => ({}), + })), + }; + }), +})); + +import { useRanger } from 'react-ranger'; + describe('Slider Component', () => { const theme = { components: { @@ -31,6 +50,10 @@ describe('Slider Component', () => { facet: sliderFacetMock as RangeFacet, }; + beforeEach(() => { + jest.clearAllMocks(); + }); + it('renders', () => { const rendered = render(); const sliderElement = rendered.container.querySelector('.ss__facet-slider'); @@ -236,4 +259,197 @@ describe('Slider Component', () => { styles = getComputedStyle(handleElement); expect(styles.backgroundColor).toBe(themeOverride.components.facetSlider.handleColor); }); + + describe('separateHandles prop', () => { + it('separates handles when they overlap and separateHandles is true', () => { + const onChangeSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + const step = args.facet.step!; + + render(); + + // Get the options passed to useRanger + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + // Simulate dragging handles to same value + const val = [10, 10]; + onChange(val); + + // Expect separation by increasing max value + expect(onChangeSpy).toHaveBeenCalledWith([10, 10 + step]); + }); + + it('does not separate handles when separateHandles is false', () => { + const onChangeSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + + render(); + + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + const val = [10, 10]; + onChange(val); + + expect(onChangeSpy).toHaveBeenCalledWith([10, 10]); + }); + + it('does not separate handles when separateHandles is undefined', () => { + const onChangeSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + + render(); + + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + const val = [10, 10]; + onChange(val); + + expect(onChangeSpy).toHaveBeenCalledWith([10, 10]); + }); + + it('separates handles onDrag as well', () => { + const onDragSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + const step = args.facet.step!; + + render(); + + const { onDrag } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + const val = [10, 10]; + onDrag(val); + + expect(onDragSpy).toHaveBeenCalledWith([10, 10 + step]); + }); + + it('separates handles by increasing max value when there is room', () => { + const onChangeSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + const step = args.facet.step!; + const rangeMax = args.facet.range?.high!; + + render(); + + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + // Test value in middle of range where max can be increased + const testValue = Math.floor((rangeMax - step) / 2); + const val = [testValue, testValue]; + onChange(val); + + expect(onChangeSpy).toHaveBeenCalledWith([testValue, testValue + step]); + }); + + it('separates handles by decreasing min value when max is at range boundary', () => { + const onChangeSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + const step = args.facet.step!; + const rangeMax = args.facet.range?.high!; + const rangeMin = args.facet.range?.low!; + + render(); + + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + // Test when both handles are at max range (can't increase max, must decrease min) + const val = [rangeMax, rangeMax]; + onChange(val); + + if (rangeMax - step >= rangeMin) { + expect(onChangeSpy).toHaveBeenCalledWith([rangeMax - step, rangeMax]); + } else { + // If range is too small to separate, handles stay together + expect(onChangeSpy).toHaveBeenCalledWith([rangeMax, rangeMax]); + } + }); + + it('does not separate handles when range is too small (only one step)', () => { + const onChangeSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + + // Create a facet with minimal range (just one step) + const smallRangeFacet = { + ...args.facet, + range: { low: 0, high: args.facet.step }, + active: { low: 0, high: args.facet.step }, + } as RangeFacet; + + render(); + + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + // When both handles are at the same value in a minimal range + const val = [0, 0]; + onChange(val); + + // Should separate if possible + if (args.facet.step! <= smallRangeFacet.range!.high!) { + expect(onChangeSpy).toHaveBeenCalledWith([0, args.facet.step!]); + } + }); + + it('separates handles at minimum range boundary', () => { + const onChangeSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + const step = args.facet.step!; + const rangeMin = args.facet.range?.low!; + + render(); + + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + // Test when both handles are at min range + const val = [rangeMin, rangeMin]; + onChange(val); + + // Should increase max since min can't go lower + expect(onChangeSpy).toHaveBeenCalledWith([rangeMin, rangeMin + step]); + }); + + it('maintains separation when handles are already separate', () => { + const onChangeSpy = jest.fn(); + const useRangerMock = useRanger as jest.Mock; + const step = args.facet.step!; + + render(); + + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + // Test with already separated values + const val = [10, 10 + step * 3]; + onChange(val); + + // Should not modify already separated handles + expect(onChangeSpy).toHaveBeenCalledWith([10, 10 + step * 3]); + }); + + it('calls onChange callback before urlManager changes', () => { + const onChangeSpy = jest.fn(); + const mockUrlManager = { + remove: jest.fn().mockReturnThis(), + set: jest.fn().mockReturnThis(), + go: jest.fn(), + }; + + const facetWithUrlManager = { + ...args.facet, + services: { + urlManager: mockUrlManager, + }, + } as unknown as RangeFacet; + + const useRangerMock = useRanger as jest.Mock; + + render(); + + const { onChange } = useRangerMock.mock.calls[useRangerMock.mock.calls.length - 1][0]; + + const val = [15, 15]; + onChange(val); + + // onChange should be called before urlManager + expect(onChangeSpy).toHaveBeenCalled(); + expect(mockUrlManager.remove).toHaveBeenCalled(); + }); + }); }); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.tsx b/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.tsx index 48174c36b..60aff42fc 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.tsx @@ -158,7 +158,7 @@ export const FacetSlider = observer((properties: FacetSliderProps): JSX.Element const props = mergeProps('facetSlider', globalTheme, defaultProps, properties); - const { showTicks, facet, stickyHandleLabel, onChange, onDrag, className, internalClassName } = props; + const { showTicks, facet, stickyHandleLabel, separateHandles, onChange, onDrag, className, internalClassName } = props; let { tickSize } = props; @@ -172,6 +172,35 @@ export const FacetSlider = observer((properties: FacetSliderProps): JSX.Element const [values, setValues] = useState([facet.active?.low, facet.active?.high]); const [active, setActive] = useState([facet.active?.low, facet.active?.high]); + // Helper function to ensure min and max values are not equal + const ensureValueSeparation = (val: number[]): number[] => { + if (!separateHandles || !facet.step) { + return val; + } + + const [minVal, maxVal] = val; + const rangeMin = facet.range?.low!; + const rangeMax = facet.range?.high!; + const step = facet.step; + + // If values are equal, separate them by one step + if (minVal === maxVal) { + // Determine which value to adjust based on range boundaries + if (maxVal + step <= rangeMax) { + // Prefer to increase max if possible + return [minVal, maxVal + step]; + } else if (minVal - step >= rangeMin) { + // Otherwise, decrease min if possible + return [minVal - step, maxVal]; + } else { + // If neither is possible (range too small), return original + return val; + } + } + + return val; + }; + if (((facet.active?.low || facet.active?.low === 0) && facet.active?.high && values[0] != facet.active?.low) || values[1] != facet.active?.high) { setActive([facet.active?.low, facet.active?.high]); setValues([facet.active?.low, facet.active?.high]); @@ -180,19 +209,25 @@ export const FacetSlider = observer((properties: FacetSliderProps): JSX.Element const { getTrackProps, ticks, segments, handles } = useRanger({ values: active as number[], onChange: (val: number[]) => { - setActive(val); + const adjustedVal = ensureValueSeparation(val); + setActive(adjustedVal); + + // onChange called prior to urlManager changes to allow for mutation in function + onChange && onChange(adjustedVal); + if (facet?.services?.urlManager) { - if (val[0] == facet.range!.low && val[1] == facet.range!.high) { + if (adjustedVal[0] == facet.range!.low && adjustedVal[1] == facet.range!.high) { facet.services.urlManager.remove('page').remove(`filter.${facet.field}`).go(); } else { - facet.services.urlManager.remove('page').set(`filter.${facet.field}`, { low: val[0], high: val[1] }).go(); + facet.services.urlManager.remove('page').set(`filter.${facet.field}`, { low: adjustedVal[0], high: adjustedVal[1] }).go(); } } onChange && onChange(val); }, onDrag: (val: number[]) => { - setActive(val); - onDrag && onDrag(val); + const adjustedVal = ensureValueSeparation(val); + setActive(adjustedVal); + onDrag && onDrag(adjustedVal); }, min: facet.range?.low!, max: facet.range?.high!, @@ -294,6 +329,7 @@ export interface FacetSliderProps extends ComponentProps { tickSize?: number; tickTextColor?: string; stickyHandleLabel?: boolean; + separateHandles?: boolean; facet: RangeFacet; onChange?: (values: number[]) => void; onDrag?: (values: number[]) => void; diff --git a/packages/snap-preact/components/src/components/Molecules/FacetSlider/readme.md b/packages/snap-preact/components/src/components/Molecules/FacetSlider/readme.md index f4fc219ec..7ff6d3cfd 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetSlider/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/FacetSlider/readme.md @@ -3,6 +3,9 @@ Renders a slider to be used with any slider facet. Built using [react-ranger](https://github.com/tannerlinsley/react-ranger). ## Usage +```jsx +import { FacetSlider } from '@searchspring/snap-preact-components'; +``` ### facet The required `facet` prop specifies a reference to a facet within the facets store array. The facet must be a range facet (`display` type of `'slider'`). @@ -63,6 +66,16 @@ The `stickyHandleLabel` prop specifies if the handle value text should display a /> ``` +### separateHandles +The `separateHandles` prop prevents the minimum and maximum slider values from being equal. When enabled, if a user attempts to set both handles to the same value, they will be automatically separated by one step value. The component intelligently determines whether to adjust the min or max value based on the available range. + +```jsx + facet.display === 'slider').pop()} + separateHandles={true} +/> +``` + ### handleDraggingColor The `handleDraggingColor` prop specifies the handle color while dragging. @@ -106,7 +119,7 @@ The `railColor` prop specifies the slider rail (foreground) color. ### Events #### onChange -The `onChange` prop allows for a custom callback function for when a slider handle has been changed. +The `onChange` prop allows for a custom callback function for when a slider handle has been changed. This callback is invoked **before** the URL manager updates occur, allowing for mutation of values or other operations prior to API request. ```jsx { const sizeFacet = controller?.store?.facets.filter((facet) => facet.field == 'on_sale').pop(); diff --git a/packages/snap-preact/components/src/components/Molecules/FacetToggle/FacetToggle.test.tsx.nope b/packages/snap-preact/components/src/components/Molecules/FacetToggle/FacetToggle.test.tsx.nope index c3a106b4e..f2daa215a 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetToggle/FacetToggle.test.tsx.nope +++ b/packages/snap-preact/components/src/components/Molecules/FacetToggle/FacetToggle.test.tsx.nope @@ -5,7 +5,7 @@ import { ThemeProvider } from '../../../providers'; import { FacetToggle } from './FacetToggle'; import type { FacetValue, ValueFacet } from '@searchspring/snap-store-mobx'; import { MockData } from '@searchspring/snap-shared'; -import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@searchspring/snapi-types'; +import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@athoscommerce/snapi-types'; describe('FacetToggle Component', () => { let value: FacetValue; diff --git a/packages/snap-preact/components/src/components/Molecules/Filter/Filter.stories.tsx b/packages/snap-preact/components/src/components/Molecules/Filter/Filter.stories.tsx index a455803e1..194ee30ba 100644 --- a/packages/snap-preact/components/src/components/Molecules/Filter/Filter.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Filter/Filter.stories.tsx @@ -6,10 +6,9 @@ import { Filter, FilterProps } from './Filter'; import { iconPaths } from '../../Atoms/Icon/paths'; import { componentArgs, highlightedCode } from '../../../utilities'; import { Snapify } from '../../../utilities/snapify'; -import { FacetType } from '../../../types'; import Readme from '../Filter/readme.md'; -import type { SearchRequestModelFilterValue } from '@searchspring/snapi-types'; +import type { SearchRequestModelFilterValue } from '@athoscommerce/snapi-types'; import type { SearchController } from '@searchspring/snap-controller'; export default { @@ -121,7 +120,7 @@ export default { const snapInstance = Snapify.search({ id: 'Filter', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', filters: [ { type: 'value', @@ -135,10 +134,10 @@ const snapInstance = Snapify.search({ export const Default = (args: FilterProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => ( facet.type === FacetType.VALUE).shift().label} + facetLabel={controller?.store?.facets.filter((facet) => facet.type === 'value').shift().label} valueLabel={ controller?.store?.facets - .filter((facet) => facet.type === FacetType.VALUE) + .filter((facet) => facet.type === 'value') .shift() .values.shift().value } @@ -157,10 +156,10 @@ Default.loaders = [ export const NoFacetLabel = (args: FilterProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => ( facet.type === FacetType.VALUE).shift().label} + facetLabel={controller?.store?.facets.filter((facet) => facet.type === 'value').shift().label} valueLabel={ controller?.store?.facets - .filter((facet) => facet.type === FacetType.VALUE) + .filter((facet) => facet.type === 'value') .shift() .values.shift().value } diff --git a/packages/snap-preact/components/src/components/Molecules/Filter/Filter.test.tsx b/packages/snap-preact/components/src/components/Molecules/Filter/Filter.test.tsx index b61397d13..018ebe718 100644 --- a/packages/snap-preact/components/src/components/Molecules/Filter/Filter.test.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Filter/Filter.test.tsx @@ -98,7 +98,7 @@ describe('Filter Component', () => { }); it('can use a custom separator', () => { - const separator = 'gottakeepemseperated'; + const separator = 'gottakeepemseparated'; const rendered = render(); const separatorTextElement = rendered.container.querySelector('.ss__filter__label__separator'); diff --git a/packages/snap-preact/components/src/components/Molecules/Filter/readme.md b/packages/snap-preact/components/src/components/Molecules/Filter/readme.md index adda90455..cf76f1708 100644 --- a/packages/snap-preact/components/src/components/Molecules/Filter/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/Filter/readme.md @@ -7,6 +7,9 @@ Renders a facet filter. - Button ## Usage +```jsx +import { Filter } from '@searchspring/snap-preact-components'; +``` ### facetLabel The `facetLabel` prop specifies the filter label. Typically set to the facet label. diff --git a/packages/snap-preact/components/src/components/Molecules/Grid/readme.md b/packages/snap-preact/components/src/components/Molecules/Grid/readme.md index 7a6ab8603..8ed581cb2 100644 --- a/packages/snap-preact/components/src/components/Molecules/Grid/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/Grid/readme.md @@ -6,6 +6,9 @@ Renders an Grid of options - image ## Usage +```jsx +import { Grid } from '@searchspring/snap-preact-components'; +``` ### options The required `options` prop specifies an array of options to render. diff --git a/packages/snap-preact/components/src/components/Molecules/List/List.stories.tsx b/packages/snap-preact/components/src/components/Molecules/List/List.stories.tsx index c12f803e3..9c2afaac9 100644 --- a/packages/snap-preact/components/src/components/Molecules/List/List.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/List/List.stories.tsx @@ -165,7 +165,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'List', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'List', globals: { siteId: 'atkzs2' } }); export const Default = (args: ListProps) => ; Default.args = { diff --git a/packages/snap-preact/components/src/components/Molecules/List/readme.md b/packages/snap-preact/components/src/components/Molecules/List/readme.md index 8813f178a..0217f9c95 100644 --- a/packages/snap-preact/components/src/components/Molecules/List/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/List/readme.md @@ -6,6 +6,9 @@ Renders a list of options. - Checkbox ## Usage +```jsx +import { List } from '@searchspring/snap-preact-components'; +``` ### options The required `options` prop specifies an array of Option Objects to be rendered. diff --git a/packages/snap-preact/components/src/components/Molecules/LoadMore/LoadMore.stories.tsx b/packages/snap-preact/components/src/components/Molecules/LoadMore/LoadMore.stories.tsx index 0dc87205d..9aee6767d 100644 --- a/packages/snap-preact/components/src/components/Molecules/LoadMore/LoadMore.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/LoadMore/LoadMore.stories.tsx @@ -8,7 +8,7 @@ import { iconPaths } from '../../Atoms/Icon'; import { Snapify } from '../../../utilities/snapify'; import Readme from '../LoadMore/readme.md'; import type { SearchController } from '@searchspring/snap-controller'; -import type { SearchRequestModelFilterTypeEnum } from '@searchspring/snapi-types'; +import type { SearchRequestModelFilterTypeEnum } from '@athoscommerce/snapi-types'; export default { title: 'Molecules/LoadMore', @@ -197,7 +197,7 @@ export default { const snapInstance = Snapify.search({ id: 'LoadMore', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', filters: [ { type: 'value' as SearchRequestModelFilterTypeEnum, diff --git a/packages/snap-preact/components/src/components/Molecules/OverlayBadge/OverlayBadge.stories.tsx b/packages/snap-preact/components/src/components/Molecules/OverlayBadge/OverlayBadge.stories.tsx index 720a4b077..bb64a61a7 100644 --- a/packages/snap-preact/components/src/components/Molecules/OverlayBadge/OverlayBadge.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/OverlayBadge/OverlayBadge.stories.tsx @@ -117,7 +117,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'OverlayBadge', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'OverlayBadge', globals: { siteId: 'atkzs2' } }); const ObservableOverlayBadge = observer(({ args, controller }: { args: OverlayBadgeProps; controller: SearchController }) => { return ( diff --git a/packages/snap-preact/components/src/components/Molecules/OverlayBadge/OverlayBadge.tsx b/packages/snap-preact/components/src/components/Molecules/OverlayBadge/OverlayBadge.tsx index d19030331..7935b24fc 100644 --- a/packages/snap-preact/components/src/components/Molecules/OverlayBadge/OverlayBadge.tsx +++ b/packages/snap-preact/components/src/components/Molecules/OverlayBadge/OverlayBadge.tsx @@ -108,9 +108,9 @@ export const OverlayBadge = observer((properties: OverlayBadgeProps): JSX.Elemen name: slot.name, top: index == 0, bottom: index == sectionSlots.length - 1, - badges: result?.badges?.atLocation(`${section}/${slot.tag}`).slice(0, limit), + badges: result?.display.badges?.atLocation(`${section}/${slot.tag}`).slice(0, limit), })) - .filter((slot) => slot.badges.length); + .filter((slot) => slot.badges?.length); return { section, diff --git a/packages/snap-preact/components/src/components/Molecules/OverlayBadge/readme.md b/packages/snap-preact/components/src/components/Molecules/OverlayBadge/readme.md index 36ec211f1..d60a7bc23 100644 --- a/packages/snap-preact/components/src/components/Molecules/OverlayBadge/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/OverlayBadge/readme.md @@ -3,6 +3,9 @@ Renders overlay badges configured in the Searchspring Management Console and returned from the API. This component is intended to be used within a `Result` component to wrap elements (children) that should have overlay badges. ## Usage +```jsx +import { OverlayBadge } from '@searchspring/snap-preact-components'; +``` ### children The required children provided to the component will be wrapped and rendered in a relative div to allow badges to be positioned absolutely. @@ -38,7 +41,7 @@ The required `result` prop specifies a reference to a product object from the `r ``` ### componentMap -The `componentMap` prop allows for custom badge components. This functionallity requires the component and accompanying files to be synced to the Searchspring Management Console using Snapfu. +The `componentMap` prop allows for custom badge components. This functionality requires the component and accompanying files to be synced to the Searchspring Management Console using Snapfu. ```jsx import { CustomOnSale } from './components/Badges/CustomOnSale'; diff --git a/packages/snap-preact/components/src/components/Molecules/Pagination/Pagination.stories.tsx b/packages/snap-preact/components/src/components/Molecules/Pagination/Pagination.stories.tsx index 1d8e01f6a..c114dc247 100644 --- a/packages/snap-preact/components/src/components/Molecules/Pagination/Pagination.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Pagination/Pagination.stories.tsx @@ -192,7 +192,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'Pagination', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'Pagination', globals: { siteId: 'atkzs2' } }); const ObservablePagination = observer(({ args, controller }: { args: PaginationProps; controller: SearchController }) => { return ; diff --git a/packages/snap-preact/components/src/components/Molecules/Pagination/readme.md b/packages/snap-preact/components/src/components/Molecules/Pagination/readme.md index 143f54523..38ee099bb 100644 --- a/packages/snap-preact/components/src/components/Molecules/Pagination/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/Pagination/readme.md @@ -6,6 +6,9 @@ Renders pagination page links for the given search response. - Icon ## Usage +```jsx +import { Pagination } from '@searchspring/snap-preact-components'; +``` ### pagination The required `pagination` prop specifies a reference to the pagination store object. diff --git a/packages/snap-preact/components/src/components/Molecules/PerPage/PerPage.stories.tsx b/packages/snap-preact/components/src/components/Molecules/PerPage/PerPage.stories.tsx index 41dd6cf2a..91749603a 100644 --- a/packages/snap-preact/components/src/components/Molecules/PerPage/PerPage.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/PerPage/PerPage.stories.tsx @@ -76,7 +76,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'PerPage', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'PerPage', globals: { siteId: 'atkzs2' } }); export const Default = (args: PerPageProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => { return ; @@ -96,7 +96,7 @@ Default.args = { const snapInstanceList = Snapify.search({ id: 'PerPage-list', - globals: { siteId: '8uyt2m' }, + globals: { siteId: 'atkzs2' }, settings: { pagination: { pageSizeOptions: [ diff --git a/packages/snap-preact/components/src/components/Molecules/RadioList/RadioList.stories.tsx b/packages/snap-preact/components/src/components/Molecules/RadioList/RadioList.stories.tsx index d6a110f86..711cbcb25 100644 --- a/packages/snap-preact/components/src/components/Molecules/RadioList/RadioList.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/RadioList/RadioList.stories.tsx @@ -134,7 +134,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'RadioList', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'RadioList', globals: { siteId: 'atkzs2' } }); export const Default = (args: RadioListProps) => ; Default.args = { diff --git a/packages/snap-preact/components/src/components/Molecules/Result/Result.stories.tsx b/packages/snap-preact/components/src/components/Molecules/Result/Result.stories.tsx index 276a71d64..d0da0e8d7 100644 --- a/packages/snap-preact/components/src/components/Molecules/Result/Result.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Result/Result.stories.tsx @@ -70,6 +70,16 @@ export default { }, control: { type: 'boolean' }, }, + hideVariantSelections: { + description: 'Hide variant Selections', + table: { + type: { + summary: 'boolean', + }, + defaultValue: { summary: false }, + }, + control: { type: 'boolean' }, + }, hideTitle: { description: 'Hide title', table: { @@ -217,7 +227,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'Result', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'Result', globals: { siteId: 'atkzs2' } }); export const Default = (args: ResultProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => ( diff --git a/packages/snap-preact/components/src/components/Molecules/Result/Result.test.tsx b/packages/snap-preact/components/src/components/Molecules/Result/Result.test.tsx index 081766f55..06dee2980 100644 --- a/packages/snap-preact/components/src/components/Molecules/Result/Result.test.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Result/Result.test.tsx @@ -9,6 +9,7 @@ import { SearchResultStore } from '@searchspring/snap-store-mobx'; import { MockData } from '@searchspring/snap-shared'; import type { Product } from '@searchspring/snap-store-mobx'; +import { SearchResponseModelResultVariants } from '@athoscommerce/snapi-types'; const wait = (time?: number) => { return new Promise((resolve) => { @@ -291,6 +292,231 @@ describe('Result Component', () => { expect(resultElement?.classList).toHaveLength(3); }); + + it('can show variant selections with hideVariantSelections false', () => { + const mockVariantData: SearchResponseModelResultVariants = { + data: [ + { + mappings: { + core: { + uid: 'variant1', + available: true, + name: 'Blue Variant', + imageUrl: 'https://example.com/blue.jpg', + }, + }, + badges: [], + options: { + color: { + value: 'blue', + }, + size: { + value: 'large', + }, + }, + }, + { + mappings: { + core: { + uid: 'variant2', + available: true, + name: 'Red Variant', + imageUrl: 'https://example.com/red.jpg', + }, + }, + badges: [], + options: { + color: { + value: 'red', + }, + size: { + value: 'medium', + }, + }, + }, + ], + optionConfig: { + color: { + id: 123, + }, + size: { + id: 456, + }, + }, + }; + const searchResponseClone = { ...searchResponse }; + const resultWithVariants = searchResponseClone.search.results![0]; + resultWithVariants.variants = mockVariantData; + + const mockResultsWithVariants = new SearchResultStore({ + config: { id: 'test' }, + state: { loaded: false }, + data: { + search: searchResponse.search, + meta: searchResponse.meta, + }, + }); + + const rendered = render(); + + const resultElement = rendered.container.querySelector('.ss__result'); + // By default, hideVariantSelections should be undefined, which means variant selections are shown + const variantSelectionContainer = rendered.container.querySelector('.ss__result__details__variant-selection'); + const variantSelectionElements = rendered.container.querySelectorAll('.ss__result__details__variant-selection .ss__variant-selection'); + + expect(resultElement).toBeInTheDocument(); + expect(variantSelectionContainer).toBeInTheDocument(); + expect(variantSelectionElements).toHaveLength(2); + }); + + it('renders variant selections by default', () => { + const mockVariantData: SearchResponseModelResultVariants = { + data: [ + { + mappings: { + core: { + uid: 'variant1', + available: true, + name: 'Blue Variant', + imageUrl: 'https://example.com/blue.jpg', + }, + }, + badges: [], + options: { + color: { + value: 'blue', + }, + size: { + value: 'large', + }, + }, + }, + { + mappings: { + core: { + uid: 'variant2', + available: true, + name: 'Red Variant', + imageUrl: 'https://example.com/red.jpg', + }, + }, + badges: [], + options: { + color: { + value: 'red', + }, + size: { + value: 'medium', + }, + }, + }, + ], + optionConfig: { + color: { + id: 123, + }, + size: { + id: 456, + }, + }, + }; + const searchResponseClone = { ...searchResponse }; + const resultWithVariants = searchResponseClone.search.results![0]; + resultWithVariants.variants = mockVariantData; + + const mockResultsWithVariants = new SearchResultStore({ + config: { id: 'test' }, + state: { loaded: false }, + data: { + search: searchResponse.search, + meta: searchResponse.meta, + }, + }); + + const rendered = render(); + const resultElement = rendered.container.querySelector('.ss__result'); + // By default, hideVariantSelections should be undefined, which means variant selections are shown + const variantSelectionContainer = rendered.container.querySelector('.ss__result__details__variant-selection'); + const variantSelectionElements = rendered.container.querySelectorAll('.ss__result__details__variant-selection .ss__variant-selection'); + + expect(resultElement).toBeInTheDocument(); + expect(variantSelectionContainer).toBeInTheDocument(); + expect(variantSelectionElements).toHaveLength(2); + }); + + it('hides variant selections when hideVariantSelections is true', () => { + const mockVariantData: SearchResponseModelResultVariants = { + data: [ + { + mappings: { + core: { + uid: 'variant1', + available: true, + name: 'Blue Variant', + imageUrl: 'https://example.com/blue.jpg', + }, + }, + badges: [], + options: { + color: { + value: 'blue', + }, + size: { + value: 'large', + }, + }, + }, + { + mappings: { + core: { + uid: 'variant2', + available: true, + name: 'Red Variant', + imageUrl: 'https://example.com/red.jpg', + }, + }, + badges: [], + options: { + color: { + value: 'red', + }, + size: { + value: 'medium', + }, + }, + }, + ], + optionConfig: { + color: { + id: 123, + }, + size: { + id: 456, + }, + }, + }; + const searchResponseClone = { ...searchResponse }; + const resultWithVariants = searchResponseClone.search.results![0]; + resultWithVariants.variants = mockVariantData; + + const mockResultsWithVariants = new SearchResultStore({ + config: { id: 'test' }, + state: { loaded: false }, + data: { + search: searchResponse.search, + meta: searchResponse.meta, + }, + }); + + const rendered = render(); + + const resultElement = rendered.container.querySelector('.ss__result'); + const variantSelectionDropdownElement = rendered.container.querySelector('.ss__result__details__variant-selection'); + + expect(resultElement).toBeInTheDocument(); + // Variant container might still exist but selections should be hidden + expect(variantSelectionDropdownElement).not.toBeInTheDocument(); + }); }); describe('Result lang works', () => { diff --git a/packages/snap-preact/components/src/components/Molecules/Result/Result.tsx b/packages/snap-preact/components/src/components/Molecules/Result/Result.tsx index 21872fc17..2085f6e93 100644 --- a/packages/snap-preact/components/src/components/Molecules/Result/Result.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Result/Result.tsx @@ -18,6 +18,7 @@ import { Rating, RatingProps } from '../Rating'; import { Button, ButtonProps } from '../../Atoms/Button'; import deepmerge from 'deepmerge'; import { Lang, useLang } from '../../../hooks'; +import { VariantSelection, VariantSelectionProps } from '../VariantSelection'; const defaultStyles: StyleScript = () => { return css({ @@ -52,7 +53,7 @@ const defaultStyles: StyleScript = () => { }, }, - '& .ss__result__rating-wrapper': { + '& .ss__result__details__rating-wrapper': { display: 'flex', justifyContent: 'center', }, @@ -107,6 +108,7 @@ export const Result = observer((properties: ResultProps): JSX.Element => { layout, onClick, controller, + hideVariantSelections, hideAddToCartButton, onAddToCartClick, addToCartButtonText, @@ -122,6 +124,16 @@ export const Result = observer((properties: ResultProps): JSX.Element => { const [addedToCart, setAddedToCart] = useState(false); const subProps: ResultSubProps = { + variantSelection: { + // global theme + internalClassName: 'ss__result__variant-selection', + ...defined({ + disableStyles, + }), + // component theme overrides + theme: props.theme, + treePath, + }, price: { // global theme internalClassName: 'ss__result__price', @@ -281,7 +293,7 @@ export const Result = observer((properties: ResultProps): JSX.Element => {
)} {!hideRating && ( -
+
)} @@ -299,8 +311,17 @@ export const Result = observer((properties: ResultProps): JSX.Element => { )}
)} + {cloneWithProps(detailSlot, { result, treePath })} + {!hideVariantSelections && ( +
+ {result.variants?.selections.map((selection) => { + return ; + })} +
+ )} + {!hideAddToCartButton && (
+
+
+ + )} + + {showPagination && totalSlides > visibleSlides && ( +
+ {slideGroups.map((_, index: number) => { + const defaultLang: Partial = { + paginationButton: { + attributes: { + 'aria-label': `Go to slide group ${index + 1} of ${totalDots}`, + }, + }, + }; + + //deep merge with props.lang + const paginationLang = deepmerge(defaultLang, props.lang || {}); + const paginationLangObj = useLang(paginationLang as any, { + index, + totalDots, + }); + const selected = currentDotIndex === index; + const subpropsToUse = selected ? subProps.PaginationCurrentButton : subProps.PaginationButton; + return ( + + ); + })} +
+ )} + + {autoPlay && ( +
+ + ); +} + +export interface SlideshowLang { + pauseButton: Lang<{ + isPlaying: boolean; + isNextDisabled: boolean; + isPrevDisabled: boolean; + }>; + paginationButton: LangAttributes<{ + index: number; + totalDots: number; + }>; + nextButton: Lang<{ + isPlaying: boolean; + isNextDisabled: boolean; + isPrevDisabled: boolean; + }>; + prevButton: Lang<{ + isPlaying: boolean; + isNextDisabled: boolean; + isPrevDisabled: boolean; + }>; + slide: LangAttributes<{ + hasClickHandler: boolean; + imageAlt: string; + index: number; + slidesLength: number; + }>; + srInstructions: Lang<{ + touchDragging: boolean; + }>; +} + +export interface SlideshowSlide { + src?: string; + alt?: string; + onClick?: (slide: SlideshowSlide, index: number) => void; + content?: JSX.Element; +} + +export interface SlideshowProps extends ComponentProps { + slides: (string | SlideshowSlide)[]; + fallbackImage?: string; + autoPlay?: boolean; + autoPlayInterval?: number; + showNavigation?: boolean; + overlayNavigation?: boolean; + alwaysShowNavigation?: boolean; + centerInsufficientSlides?: boolean; + showPagination?: boolean; + loop?: boolean; + slidesToShow?: number; + slidesToMove?: number; + gap?: number; + slideImageAlt?: string; + ariaLabel?: string; + ariaLabelledBy?: string; + touchDragging?: boolean; + dragThreshold?: number; + lang?: Partial; +} + +interface SlideshowSubProps { + Image: Partial; + PrevButton: Partial; + NextButton: Partial; + PaginationButton: Partial; + PaginationCurrentButton: Partial; + PauseButton: Partial; +} diff --git a/packages/snap-preact/components/src/components/Molecules/Slideshow/index.ts b/packages/snap-preact/components/src/components/Molecules/Slideshow/index.ts new file mode 100644 index 000000000..a5c0c46ef --- /dev/null +++ b/packages/snap-preact/components/src/components/Molecules/Slideshow/index.ts @@ -0,0 +1 @@ +export * from './Slideshow'; diff --git a/packages/snap-preact/components/src/components/Molecules/Slideshow/readme.md b/packages/snap-preact/components/src/components/Molecules/Slideshow/readme.md new file mode 100644 index 000000000..4424fcda5 --- /dev/null +++ b/packages/snap-preact/components/src/components/Molecules/Slideshow/readme.md @@ -0,0 +1,160 @@ +# Slideshow + +Renders an accessible, customizable slideshow component that displays multiple images with navigation controls, pagination, and optional click interactions. + +## Sub-components +- Image +- Button + +## Usage + +```jsx + +``` + +### slides +The `slides` prop accepts an array of image URLs (strings) or slides objects with callbacks and metadata. slides objects allow for individual click handlers and custom properties. + +```jsx + +``` + +or + +```jsx +const slidesObjects = [ + { + src: 'product1.jpg', + alt: 'Premium Headphones', + onClick: (slide, index) => openProductDetails(slide) + }, + { + onClick: (slide, index) => addToCart(slide), + content:
slide 2
+ } +]; +``` + +```jsx + +``` + +### slidesToShow +The `slidesToShow` prop specifies the number of slides visible at once in the slideshow. + +```jsx + +``` + +### slidesToMove +The `slidesToMove` prop specifies the number of slides to advance when using navigation controls. + +```jsx + +``` + +### gap +The `gap` prop sets the spacing between slides in pixels. + +```jsx + +``` + +### autoPlay +The `autoPlay` prop enables automatic progression through the slideshow. + +```jsx + +``` + +### autoPlayInterval +The `autoPlayInterval` prop sets the time in milliseconds between automatic slide transitions. + +```jsx + +``` + +### showNavigation +The `showNavigation` prop controls the visibility of previous/next navigation buttons. + +```jsx + +``` + +### showPagination +The `showPagination` prop controls the visibility of pagination dots at the bottom of the slideshow. + +```jsx + +``` + +### overlayNavigation +The `overlayNavigation` prop controls if the previous/next navigation buttons should overlay the images or render outside of them. + +```jsx + +``` + +### touchDragging +The `touchDragging` prop controls if the slideshow should have drag navigation enabled. + +```jsx + +``` + +### dragThreshold +The `dragThreshold` prop sets the drag sensitivity. + +```jsx + +``` + +### loop +The `loop` prop enables continuous looping through the slideshow when reaching the end. + +```jsx + +``` + +### fallbackImage +The `fallbackImage` prop specifies a default image URL to display when an image fails to load. + +```jsx + +``` + +### slideImageAlt +The `slideImageAlt` prop provides default alt text for images, which will be appended with the image number for accessibility. + +```jsx + +``` + +### ariaLabel +The `ariaLabel` prop provides an accessible label for the entire slideshow component. + +```jsx + +``` + +### ariaLabelledBy +The `ariaLabelledBy` prop references the ID of an element that labels the slideshow. + +```jsx +

Our Products

+ +``` + +### alwaysShowNavigation +The `alwaysShowNavigation` prop ensures navigation buttons are always displayed and take up space, even when the number of slides is below the `slidesPerView` threshold. This is particularly useful when using the slideshow in a grid of product cards to maintain consistent layout and height across all cards. + +```jsx + +``` + +### centerInsufficientSlides +The `centerInsufficientSlides` prop centers slides when there are fewer slides than the `slidesToShow` setting. This is enabled by default. + +```jsx + +``` \ No newline at end of file diff --git a/packages/snap-preact/components/src/components/Molecules/SortBy/SortBy.stories.tsx b/packages/snap-preact/components/src/components/Molecules/SortBy/SortBy.stories.tsx index 9225fa960..7d8dd7a8e 100644 --- a/packages/snap-preact/components/src/components/Molecules/SortBy/SortBy.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/SortBy/SortBy.stories.tsx @@ -85,7 +85,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'SortBy', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'SortBy', globals: { siteId: 'atkzs2' } }); export const Default = (args: SortByProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => { return ; diff --git a/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.stories.tsx b/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.stories.tsx index 3be1f3cf3..d7cfc9723 100644 --- a/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.stories.tsx @@ -165,7 +165,7 @@ Disabled.args = { { value: 'Black', label: 'Black', disabled: false }, { value: 'White', label: 'White', disabled: true }, ], - type: 'carousel', + type: 'slideshow', } as SwatchesProps; GradientBackground.args = { diff --git a/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.tsx b/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.tsx index c6b0de0ef..d81073a34 100644 --- a/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.tsx @@ -1,24 +1,21 @@ import { Fragment, h } from 'preact'; - -import { useState } from 'preact/hooks'; -import { jsx, css } from '@emotion/react'; import classnames from 'classnames'; - +import { css } from '@emotion/react'; import { Theme, useTheme, CacheProvider, useTreePath } from '../../../providers'; import { ComponentProps, SwatchOption, BreakpointsProps, StyleScript } from '../../../types'; import { useA11y, useDisplaySettings } from '../../../hooks'; -import { Carousel, CarouselProps } from '../Carousel'; import { defined, mergeProps, mergeStyles } from '../../../utilities'; import { Grid, GridProps } from '../Grid'; import { ImageProps, Image } from '../../Atoms/Image'; import deepmerge from 'deepmerge'; import { filters } from '@searchspring/snap-toolbox'; import Color from 'color'; +import { Slideshow, SlideshowSlide, SlideshowProps } from '../Slideshow'; +import { useState } from 'preact/hooks'; const defaultStyles: StyleScript = ({ theme }) => { return css({ - marginTop: '10px', - '.ss__swatches__carousel__swatch': { + '.ss__swatches__slideshow__swatch': { boxSizing: 'content-box', cursor: 'pointer', backgroundRepeat: 'no-repeat', @@ -27,23 +24,21 @@ const defaultStyles: StyleScript = ({ theme }) => { alignItems: 'center', border: `1px solid ${theme?.variables?.colors?.primary || '#333'}`, aspectRatio: '1/1', - margin: 'auto', flexDirection: 'column', - '.ss__swatches__carousel__swatch__inner': { + '.ss__swatches__slideshow__swatch__inner': { aspectRatio: '1/1', display: 'flex', justifyContent: 'center', alignItems: 'center', - margin: 'auto', - height: '100%', + width: '100%', }, - '&.ss__swatches__carousel__swatch--selected': { + '&.ss__swatches__slideshow__swatch--selected': { border: `2px solid ${theme?.variables?.colors?.primary || '#333'}`, }, - '&.ss__swatches__carousel__swatch--disabled:before, &.ss__swatches__carousel__swatch--unavailable:before': { + '&.ss__swatches__slideshow__swatch--disabled:before, &.ss__swatches__slideshow__swatch--unavailable:before': { content: '""', display: 'block', position: 'absolute', @@ -55,19 +50,19 @@ const defaultStyles: StyleScript = ({ theme }) => { transform: 'rotate(-45deg)', }, - '&.ss__swatches__carousel__swatch--disabled': { + '&.ss__swatches__slideshow__swatch--disabled': { position: 'relative', cursor: 'none', pointerEvents: 'none', opacity: 0.5, }, - '&.ss__swatches__carousel__swatch--unavailable': { + '&.ss__swatches__slideshow__swatch--unavailable': { cursor: 'pointer', opacity: 0.5, }, - '&.ss__swatches__carousel__swatch--dark': { + '&.ss__swatches__slideshow__swatch--dark': { color: '#fff', }, }, @@ -104,14 +99,14 @@ export function Swatches(properties: SwatchesProps): JSX.Element { const defaultProps: Partial = { // default props - type: 'carousel', + type: 'slideshow', hideLabels: true, treePath: globalTreePath, }; let props = mergeProps('swatches', globalTheme, defaultProps, properties); - const breakpoints = props.breakpoints || (props.type == 'carousel' ? defaultCarouselBreakpoints : {}); + const breakpoints = props.breakpoints || (props.type == 'slideshow' ? defaultCarouselBreakpoints : {}); const displaySettings = useDisplaySettings(breakpoints); if (displaySettings && Object.keys(displaySettings).length) { @@ -123,14 +118,20 @@ export function Swatches(properties: SwatchesProps): JSX.Element { }; } - const { onSelect, disabled, options, hideLabels, disableStyles, className, internalClassName, type, carousel, grid, treePath } = props; + const { onSelect, disabled, options, hideLabels, disableStyles, className, internalClassName, type, slideshow, grid, treePath } = props; const subProps: SwatchesSubProps = { - carousel: { + slideshow: { // default props - internalClassName: 'ss__swatches__carousel', + internalClassName: 'ss__swatches__slideshow', loop: false, - ...carousel, + slidesToShow: 6, // Add explicit slidesToShow + slidesToMove: 1, // Add explicit slidesToMove + gap: 8, // Add smaller gap + showNavigation: true, // Enable navigation + showPagination: false, // Disable pagination for swatches + autoPlay: false, // Disable autoplay + ...slideshow, // inherited props ...defined({ breakpoints, @@ -184,55 +185,63 @@ export function Swatches(properties: SwatchesProps): JSX.Element { setSelection(option); }; + const slidesArray: SlideshowSlide[] = []; + + if (type == 'slideshow') { + { + options.forEach((option) => { + const label = option.label; + const selected = selection?.value == option.value; + let isDark = false; + try { + const color = new Color( + option.background ? option.background.toLowerCase() : option.backgroundImageUrl ? `` : option.value.toString().toLowerCase() + ); + isDark = color.isDark(); + } catch (err) {} + + slidesArray.push({ + onClick: (e) => !disabled && !option?.disabled && makeSelection(e as any, option), + content: ( +
useA11y(e)} + aria-disabled={option.disabled || option?.available === false} + role="option" + aria-selected={selected} + > +
+ {!option.background && option.backgroundImageUrl ? ( + {option.label + ) : ( + + )} + {!hideLabels && {label || option.value}} +
+
+ ), + }); + }); + } + } + return typeof options == 'object' && options?.length ? (
- {type == 'carousel' ? ( - - {options.map((option) => { - const label = option.label; - const selected = selection?.value == option.value; - let isDark = false; - try { - const color = new Color( - option.background ? option.background.toLowerCase() : option.backgroundImageUrl ? `` : option.value.toString().toLowerCase() - ); - isDark = color.isDark(); - } catch (err) {} - - return ( -
!disabled && !option?.disabled && makeSelection(e as any, option)} - ref={(e) => useA11y(e)} - aria-disabled={option.disabled || option?.available === false} - role="option" - aria-selected={selected} - > -
- {!option.background && option.backgroundImageUrl ? ( - {option.label - ) : ( - - )} - {!hideLabels && {label || option.value}} -
-
- ); - })} -
+ {type == 'slideshow' ? ( + ) : ( ; + slideshow?: Partial; grid?: Partial; - type?: 'carousel' | 'grid'; + type?: 'slideshow' | 'grid'; } & ComponentProps; interface SwatchesSubProps { - carousel: Partial; + slideshow: Partial; grid: Partial; image: Partial; } diff --git a/packages/snap-preact/components/src/components/Molecules/Swatches/readme.md b/packages/snap-preact/components/src/components/Molecules/Swatches/readme.md index a6a10382c..77c6c3eea 100644 --- a/packages/snap-preact/components/src/components/Molecules/Swatches/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/Swatches/readme.md @@ -3,11 +3,14 @@ Renders a swatch component. ## Components Used -- carousel +- Slideshow - Swatches -- image +- Image ## Usage +```jsx +import { Swatches } from '@searchspring/snap-preact-components'; +``` ### options The required `options` prop specifies an array of options to render. Each option requires a value, but can optionally provide label, background, backgroundImageUrl, and disabled properties. @@ -116,7 +119,7 @@ The object key specified the viewport for when the parameters will be applied. Depending on the `type` prop passed, the default configuration contains the following properties, however **`any swatches props`**, can be specified. -```typescript +```js const defaultCarouselBreakpoints = { 0: { carousel: { diff --git a/packages/snap-preact/components/src/components/Molecules/Terms/Terms.stories.tsx b/packages/snap-preact/components/src/components/Molecules/Terms/Terms.stories.tsx index ab92bd5da..e3b26516c 100644 --- a/packages/snap-preact/components/src/components/Molecules/Terms/Terms.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Terms/Terms.stories.tsx @@ -132,7 +132,7 @@ const snapInstance = Snapify.autocomplete({ id: 'Autocomplete-Terms', selector: '#searchInput2', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', }, settings: { trending: { diff --git a/packages/snap-preact/components/src/components/Molecules/VariantSelection/VariantSelection.stories.tsx b/packages/snap-preact/components/src/components/Molecules/VariantSelection/VariantSelection.stories.tsx index 468c64f24..201b943a6 100644 --- a/packages/snap-preact/components/src/components/Molecules/VariantSelection/VariantSelection.stories.tsx +++ b/packages/snap-preact/components/src/components/Molecules/VariantSelection/VariantSelection.stories.tsx @@ -8,7 +8,7 @@ import { componentArgs, highlightedCode } from '../../../utilities'; import Readme from '../VariantSelection/readme.md'; import { Snapify } from '../../../utilities/snapify'; import { Next } from '@searchspring/snap-event-manager'; -import { SearchResponseModel } from '@searchspring/snapi-types'; +import { SearchResponseModel } from '@athoscommerce/snapi-types'; export default { title: 'Molecules/VariantSelection', @@ -63,6 +63,15 @@ export default { // options: ['dropdown', 'list', 'swatches'], // }, }, + onSelect: { + description: 'onSelect callback', + table: { + type: { + summary: 'function(e: React.MouseEvent, option: ListOption)', + }, + }, + action: 'onSelect', + }, ...componentArgs, }, }; @@ -116,12 +125,7 @@ const values = [ const config: SearchControllerConfig = { id: 'searchVariants', globals: { - siteId: '8uyt2m', - }, - settings: { - variants: { - field: 'ss_variants', - }, + siteId: 'atkzs2', }, }; diff --git a/packages/snap-preact/components/src/components/Molecules/VariantSelection/VariantSelection.tsx b/packages/snap-preact/components/src/components/Molecules/VariantSelection/VariantSelection.tsx index a6bb72e25..8a0c7ddc2 100644 --- a/packages/snap-preact/components/src/components/Molecules/VariantSelection/VariantSelection.tsx +++ b/packages/snap-preact/components/src/components/Molecules/VariantSelection/VariantSelection.tsx @@ -4,13 +4,14 @@ import { jsx, css } from '@emotion/react'; import classnames from 'classnames'; import { Theme, useTheme, CacheProvider, useTreePath } from '../../../providers'; import { defined, mergeProps, mergeStyles } from '../../../utilities'; -import { ComponentProps, StyleScript } from '../../../types'; +import { ComponentProps, ListOption, StyleScript } from '../../../types'; import type { VariantSelection as VariantSelectionType } from '@searchspring/snap-store-mobx'; import { List, ListProps } from '../List'; import { Swatches, SwatchesProps } from '../Swatches'; import { Dropdown, DropdownProps } from '../../Atoms/Dropdown'; import { Icon, IconProps } from '../../Atoms/Icon'; import { useA11y } from '../../../hooks'; +import { fieldNameToComponentName } from '@searchspring/snap-toolbox'; const defaultStyles: StyleScript = () => { return css({ @@ -25,35 +26,40 @@ const defaultStyles: StyleScript = () => { gap: '5px', }, }, + }, + }); +}; - '.ss__dropdown__content': { - minWidth: 'auto', - left: '0', - right: '0', +const dropdownContentStyles: StyleScript = () => { + return css({ + margin: '0px', + padding: '5px', + background: 'white', + zIndex: 10, + border: '1px solid black', - '.ss__variant-selection__option': { - cursor: 'pointer', - position: 'relative', - }, + '.ss__variant-selection__option': { + cursor: 'pointer', + position: 'relative', + }, - '.ss__variant-selection__option:hover': { - fontWeight: 'bold', - }, + '.ss__variant-selection__option:hover': { + fontWeight: 'bold', + }, - '.ss__variant-selection__option--selected': { - fontWeight: 'bold', - }, + '.ss__variant-selection__option--selected': { + fontWeight: 'bold', + }, - '.ss__variant-selection__option--disabled': { - pointerEvents: 'none', - cursor: 'initial', - }, + '.ss__variant-selection__option--disabled': { + pointerEvents: 'none', + cursor: 'initial', + color: 'red', + }, - '.ss__variant-selection__option--disabled, .ss__variant-selection__option--unavailable': { - textDecoration: 'line-through', - opacity: 0.5, - }, - }, + '.ss__variant-selection__option--disabled, .ss__variant-selection__option--unavailable': { + textDecoration: 'line-through', + opacity: 0.5, }, }); }; @@ -64,17 +70,35 @@ export const VariantSelection = observer((properties: VariantSelectionProps): JS const defaultProps: Partial = { // default props - type: 'dropdown', + name: fieldNameToComponentName(properties.selection.field), treePath: globalTreePath, }; const props = mergeProps('variantSelection', globalTheme, defaultProps, properties); - const { type, selection, disableStyles, className, internalClassName, treePath } = props; + const { selection, onSelect, disableStyles, className, internalClassName, treePath } = props; + + let type = props.type; + if (!type) { + if (selection.type == 'swatch') { + type = 'swatches'; + } else { + type = 'dropdown'; + } + } + + const onSelectHandler = (e: React.MouseEvent, option: ListOption) => { + if (onSelect) { + onSelect(e, option); + } + + selection.select(option.value as string); + }; const subProps: VariantSelectionSubProps = { dropdown: { internalClassName: 'ss__variant-selection__dropdown', + usePortal: true, // TODO: label doesnt exist on dropdown? // label: selection.label || selection.field, // inherited props @@ -102,7 +126,7 @@ export const VariantSelection = observer((properties: VariantSelectionProps): JS internalClassName: 'ss__variant-selection__list', multiSelect: false, hideOptionCheckboxes: true, - onSelect: (e, option) => selection.select(option.value), + onSelect: (e, option) => onSelectHandler(e, option), selected: selection.selected, // inherited props ...defined({ @@ -114,7 +138,7 @@ export const VariantSelection = observer((properties: VariantSelectionProps): JS }, swatches: { internalClassName: 'ss__variant-selection__swatches', - onSelect: (e, option) => selection.select(option.value), + onSelect: (e, option) => onSelectHandler(e, option), selected: selection.selected, // inherited props ...defined({ @@ -127,11 +151,13 @@ export const VariantSelection = observer((properties: VariantSelectionProps): JS }; const styling = mergeStyles(props, defaultStyles); + //since the dropdown is rendered in a portal, it needs its own emotion styles + const dropdownStyles = mergeStyles(props, dropdownContentStyles); const DropdownContent = (props: any) => { const { toggleOpen } = props; return ( -
    useA11y(e, -1, true, () => toggleOpen())}> +
      useA11y(e, -1, true, () => toggleOpen())}> {selection.values.map((val: any) => { const selected = selection.selected?.value == val.value; return ( @@ -141,7 +167,7 @@ export const VariantSelection = observer((properties: VariantSelectionProps): JS 'ss__variant-selection__option--disabled': val.disabled, 'ss__variant-selection__option--unavailable': val.available === false, })} - onClick={() => !val.disabled && selection.select(val.value)} + onClick={(e) => !val.disabled && onSelectHandler(e, val)} ref={(e) => useA11y(e)} aria-selected={selected} aria-disabled={val.disabled || val.available === false} @@ -190,7 +216,7 @@ export const VariantSelection = observer((properties: VariantSelectionProps): JS ); }; - return } {...subProps.dropdown} content={} />; + return } {...subProps.dropdown} content={} />; })()} ); @@ -229,4 +255,5 @@ interface VariantSelectionSubProps { export interface VariantSelectionProps extends ComponentProps { selection: VariantSelectionType; type?: 'dropdown' | 'swatches' | 'list'; + onSelect?: (e: React.MouseEvent, option: ListOption) => void; } diff --git a/packages/snap-preact/components/src/components/Molecules/VariantSelection/readme.md b/packages/snap-preact/components/src/components/Molecules/VariantSelection/readme.md index 139bce76a..2b1b465b2 100644 --- a/packages/snap-preact/components/src/components/Molecules/VariantSelection/readme.md +++ b/packages/snap-preact/components/src/components/Molecules/VariantSelection/readme.md @@ -10,6 +10,9 @@ The variantSelection can be rendered as 3 different types, Dropdown, List, or Sw - Swatches ## Usage +```jsx +import { VariantSelection } from '@searchspring/snap-preact-components'; +``` ### selection The required `selection` prop specifies a reference to the searchResultStores VariantSelection. @@ -23,4 +26,11 @@ The optional `type` prop specifies what type of selection you wish to render. Op ```jsx +``` + +### onSelect +The `onSelect` prop allows you to provide a callback function that is triggered when a variant option is selected. The selected option will be passed as an argument to this function, as well as the event. + +```jsx + console.log(e, val)}/> ``` \ No newline at end of file diff --git a/packages/snap-preact/components/src/components/Organisms/Autocomplete/Autocomplete.stories.tsx b/packages/snap-preact/components/src/components/Organisms/Autocomplete/Autocomplete.stories.tsx index e4da3be89..3044e446d 100644 --- a/packages/snap-preact/components/src/components/Organisms/Autocomplete/Autocomplete.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Autocomplete/Autocomplete.stories.tsx @@ -384,7 +384,7 @@ const snapInstance = Snapify.autocomplete({ id: 'Autocomplete', selector: '#searchInput', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', }, settings: { trending: { diff --git a/packages/snap-preact/components/src/components/Organisms/Autocomplete/Autocomplete.tsx b/packages/snap-preact/components/src/components/Organisms/Autocomplete/Autocomplete.tsx index dc7df3b00..c0673874a 100644 --- a/packages/snap-preact/components/src/components/Organisms/Autocomplete/Autocomplete.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Autocomplete/Autocomplete.tsx @@ -296,6 +296,7 @@ export const Autocomplete = observer((properties: AutocompleteProps): JSX.Elemen }, result: { hideBadge: true, + hideVariantSelections: true, }, }, }; diff --git a/packages/snap-preact/components/src/components/Organisms/Autocomplete/readme.md b/packages/snap-preact/components/src/components/Organisms/Autocomplete/readme.md index 8d952a659..3c4bb8e7d 100644 --- a/packages/snap-preact/components/src/components/Organisms/Autocomplete/readme.md +++ b/packages/snap-preact/components/src/components/Organisms/Autocomplete/readme.md @@ -11,6 +11,9 @@ The autocomplete layout renders terms, facets, banners, and results. - Icon ## Usage +```jsx +import { Autocomplete } from '@searchspring/snap-preact-components'; +``` ### input The required `input` prop expects either: @@ -252,7 +255,7 @@ When the viewport is between the Object's key value, those props will be merged Default Autocomplete `breakpoints` object: -```typescript +```js const breakpoints = { 0: { columns: 2, @@ -283,7 +286,7 @@ See `` component documentation for further details. The `onFacetOptionClick` prop contains a custom onClick event handler for facet options. Function is passed the click event. -```typescript +```js const CustomOnClickFunc = (e) => { console.log('You Clicked a Facet Option!' , e) }; @@ -299,7 +302,7 @@ const CustomOnClickFunc = (e) => { The `onTermClick` prop contains a custom onClick event handler for Suggested & Trending Terms. Function is passed the click event. -```typescript +```js const customOnClickFunc = (e) => { console.log('You Clicked a term!' , e) }; diff --git a/packages/snap-preact/components/src/components/Organisms/AutocompleteLayout/AutocompleteLayout.stories.tsx b/packages/snap-preact/components/src/components/Organisms/AutocompleteLayout/AutocompleteLayout.stories.tsx index c5ac20e8e..d6953c041 100644 --- a/packages/snap-preact/components/src/components/Organisms/AutocompleteLayout/AutocompleteLayout.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/AutocompleteLayout/AutocompleteLayout.stories.tsx @@ -208,7 +208,7 @@ const snapInstance = Snapify.autocomplete({ id: 'Autocomplete', selector: '#searchInput', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', }, settings: { trending: { diff --git a/packages/snap-preact/components/src/components/Organisms/AutocompleteLayout/AutocompleteLayout.tsx b/packages/snap-preact/components/src/components/Organisms/AutocompleteLayout/AutocompleteLayout.tsx index 5ca6247d5..480914c30 100644 --- a/packages/snap-preact/components/src/components/Organisms/AutocompleteLayout/AutocompleteLayout.tsx +++ b/packages/snap-preact/components/src/components/Organisms/AutocompleteLayout/AutocompleteLayout.tsx @@ -286,6 +286,7 @@ export const AutocompleteLayout = observer((properties: AutocompleteLayoutProps) }, result: { hideBadge: true, + hideVariantSelections: true, }, }, }; diff --git a/packages/snap-preact/components/src/components/Organisms/BranchOverride/readme.md b/packages/snap-preact/components/src/components/Organisms/BranchOverride/readme.md index b413dd4b7..0da02e320 100644 --- a/packages/snap-preact/components/src/components/Organisms/BranchOverride/readme.md +++ b/packages/snap-preact/components/src/components/Organisms/BranchOverride/readme.md @@ -8,6 +8,9 @@ Must have `name` and either `details` or `error` props to render. - Icon ## Usage +```jsx +import { BranchOverride } from '@searchspring/snap-preact-components'; +``` ### name The required `name` prop expects a string containing the name of the override branch. diff --git a/packages/snap-preact/components/src/components/Organisms/Facet/Facet.stories.tsx b/packages/snap-preact/components/src/components/Organisms/Facet/Facet.stories.tsx index 454f23ae9..45d3a5ac4 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facet/Facet.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Facet/Facet.stories.tsx @@ -84,6 +84,58 @@ export default { }, control: { type: 'boolean' }, }, + rangeInputs: { + defaultValue: false, + description: 'Enables facet range inputs', + table: { + type: { + summary: 'boolean', + }, + defaultValue: { summary: false }, + }, + control: { type: 'boolean' }, + }, + rangeInputsSubmitButtonText: { + defaultValue: 'Submit', + description: 'Range input submit button text', + table: { + type: { + summary: 'string', + }, + defaultValue: { summary: 'Submit' }, + }, + control: { type: 'text' }, + }, + rangeInputsPrefix: { + description: 'Range inputs prefix text', + table: { + type: { + summary: 'string', + }, + }, + control: { type: 'text' }, + }, + rangeInputsInheritDefaultValues: { + defaultValue: false, + description: 'Enables facet range input values to default to the facet low and high limits', + table: { + type: { + summary: 'boolean', + }, + defaultValue: { summary: false }, + }, + control: { type: 'boolean' }, + }, + rangeInputsSeparatorText: { + description: 'Range inputs separator text', + table: { + type: { + summary: 'string', + }, + defaultValue: { summary: ' - ' }, + }, + control: { type: 'text' }, + }, color: { description: 'Select color', table: { @@ -343,7 +395,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'Facet', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'Facet', globals: { siteId: 'atkzs2' } }); // List Facet @@ -392,7 +444,13 @@ Slider.loaders = [ // Palette Facet const ObservablePaletteFacet = observer(({ args, controller }: { args: FacetProps; controller: SearchController }) => { - return facet.display === FacetDisplay.PALETTE).shift()} />; + const facet = controller?.store?.facets.filter((facet) => facet.display === FacetDisplay.PALETTE).shift(); + if (facet) { + return ; + } + + // prevent error when no facet is found... + return
      ; }); export const Palette = (args: FacetProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => ( @@ -411,7 +469,13 @@ Palette.loaders = [ // Grid Facet const ObservableGridFacet = observer(({ args, controller }: { args: FacetProps; controller: SearchController }) => { - return facet.field === 'size_dress').pop()} />; + const facet = controller?.store?.facets.filter((facet) => facet.field === 'collection_handle').pop(); + if (facet) { + return ; + } + + // prevent error when no facet is found... + return
      ; }); export const Grid = (args: FacetProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => ( @@ -430,7 +494,11 @@ Grid.loaders = [ const ObservableHierarchyFacet = observer(({ args, controller }: { args: FacetProps; controller: SearchController }) => { const facet = controller?.store?.facets.filter((facet) => facet.display === FacetDisplay.HIERARCHY).shift(); - return ; + if (facet) { + return ; + } + // prevent error when no facet is found... + return
      ; }); export const Hierarchy = (args: FacetProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => ( diff --git a/packages/snap-preact/components/src/components/Organisms/Facet/Facet.test.tsx b/packages/snap-preact/components/src/components/Organisms/Facet/Facet.test.tsx index 8826480ab..640e6ecf9 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facet/Facet.test.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Facet/Facet.test.tsx @@ -5,7 +5,7 @@ import { ThemeProvider } from '../../../providers'; import userEvent from '@testing-library/user-event'; import { ValueFacet, RangeFacet, SearchFacetStore, StorageStore } from '@searchspring/snap-store-mobx'; -import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@searchspring/snapi-types'; +import { SearchResponseModelFacet, SearchResponseModelFacetValueAllOf } from '@athoscommerce/snapi-types'; import { MockData } from '@searchspring/snap-shared'; import { QueryStringTranslator, reactLinker, UrlManager } from '@searchspring/snap-url-manager'; import { IconType } from '../../Atoms/Icon'; @@ -390,6 +390,75 @@ describe('Facet Component', () => { const optionsElement = rendered.container.querySelector('.ss__facet__options'); expect(optionsElement).toHaveTextContent('Summer'); }); + + it('rangeInputs, rangeInputSubmitButtonText & rangeInputsPrefix props', () => { + const args = { + //@ts-ignore + facet: { ...sliderFacetMock, display: 'slider', type: 'range' } as RangeFacet, + rangeInputs: true, + rangeInputsSubmitButtonText: 'Go', + rangeInputsPrefix: '$', + rangeInputsSeparatorText: '- to -', + }; + args.facet.collapsed = false; + + const rendered = render(); + const facetElement = rendered.container.querySelector('.ss__facet__options')!; + expect(facetElement).toBeInTheDocument(); + + const rangeInputsElement = rendered.container.querySelector('.ss__facet__range-inputs'); + expect(rangeInputsElement).toBeInTheDocument(); + + const inputs = rangeInputsElement?.querySelectorAll('.ss__facet__range-input'); + expect(inputs?.length).toBe(2); + + const prefixes = rangeInputsElement?.querySelectorAll('.ss__facet__range-input__prefix'); + expect(prefixes?.length).toBe(2); + expect(prefixes?.[0]).toHaveTextContent(args.rangeInputsPrefix); + + const submitButton = rangeInputsElement?.querySelector('.ss__facet__range-input__button--submit'); + expect(submitButton).toBeInTheDocument(); + expect(submitButton).toHaveTextContent(args.rangeInputsSubmitButtonText); + + const sepElem = rangeInputsElement?.querySelector('.ss__facet__range-inputs__separator'); + expect(sepElem).toBeInTheDocument(); + expect(sepElem).toHaveTextContent(args.rangeInputsSeparatorText); + }); + + it('rangeInputInheritDefaultValues prop initializes inputs with facet range values', () => { + const args = { + facet: { + ...sliderFacetMock, + display: 'slider', + collapsed: false, + type: 'range', + range: { + low: 10, + high: 100, + }, + active: { + low: undefined, + high: undefined, + }, + } as RangeFacet, + rangeInputs: true, + rangeInputsInheritDefaultValues: true, + }; + + const rendered = render(); + const rangeInputsElement = rendered.container.querySelector('.ss__facet__range-inputs'); + expect(rangeInputsElement).toBeInTheDocument(); + + const lowInput = rangeInputsElement?.querySelector('.ss__facet__range-input--low .ss__facet__range-input__input') as HTMLInputElement; + const highInput = rangeInputsElement?.querySelector('.ss__facet__range-input--high .ss__facet__range-input__input') as HTMLInputElement; + + expect(lowInput).toBeInTheDocument(); + expect(highInput).toBeInTheDocument(); + + // Check that inputs are initialized with the facet's range values + expect(lowInput.value).toBe(args.facet.range?.low?.toString()); + expect(highInput.value).toBe(args.facet.range?.high?.toString()); + }); }); it('renders with classname', () => { @@ -414,7 +483,7 @@ describe('Facet Component', () => { const facetElement = rendered.container.querySelector('.ss__facet'); - expect(facetElement?.classList).toHaveLength(3); + expect(facetElement?.classList).toHaveLength(2); }); describe('Facet lang works', () => { @@ -662,7 +731,7 @@ describe('Facet Component', () => { const propTheme = { components: { facet: { - className: 'classy', + className: 'test-class', }, }, }; diff --git a/packages/snap-preact/components/src/components/Organisms/Facet/Facet.tsx b/packages/snap-preact/components/src/components/Organisms/Facet/Facet.tsx index 2f186842e..dee2c10f2 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facet/Facet.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Facet/Facet.tsx @@ -1,4 +1,5 @@ import { h, Fragment } from 'preact'; +import { MutableRef, useRef, useState, useEffect } from 'preact/hooks'; import { jsx, css } from '@emotion/react'; import classnames from 'classnames'; @@ -22,8 +23,8 @@ import { useA11y } from '../../../hooks/useA11y'; import { Lang, useLang } from '../../../hooks'; import deepmerge from 'deepmerge'; import { Button, ButtonProps } from '../../Atoms/Button'; -import { useState } from 'preact/hooks'; -import { useEffect } from 'react'; +import { fieldNameToComponentName } from '@searchspring/snap-toolbox'; +import { LangAttributesObj } from '../../../hooks/useLang'; const defaultStyles: StyleScript = ({ disableCollapse, color, theme }) => { return css({ @@ -80,12 +81,51 @@ const defaultStyles: StyleScript = ({ disableCollapse, color, theme '& .ss__facet__header__selected-count': { margin: '0px 5px', }, + + '.ss__facet__range-inputs': { + display: 'flex', + flexDirection: 'column', + + '.ss__facet__range-inputs__separator': { + margin: '5px', + }, + }, + + '.ss__facet__range-inputs__row': { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + '&.ss__facet__range-inputs__row--button-wrapper': { + justifyContent: 'center', + + '.ss__facet__range-input__button--submit': { + margin: '10px', + }, + }, + }, + + '.ss__facet__range-input': { + flexDirection: 'row', + display: 'flex', + border: `1px solid ${theme?.variables?.colors?.secondary || '#ccc'}`, + backgroundColor: 'white', + alignItems: 'center', + '.ss__facet__range-input__prefix': { + padding: '0 5px', + }, + '.ss__facet__range-input__input': { + width: '100%', + border: 'none', + minHeight: '35px', + }, + }, }); }; export const Facet = observer((properties: FacetProps): JSX.Element => { const globalTheme: Theme = useTheme(); const globalTreePath = useTreePath(); + const defaultProps: Partial = { limit: 12, disableOverflow: false, @@ -96,8 +136,11 @@ export const Facet = observer((properties: FacetProps): JSX.Element => { iconOverflowMore: 'plus', iconOverflowLess: 'minus', clearAllText: 'Clear All', + rangeInputsSubmitButtonText: 'Submit', + rangeInputsSeparatorText: ' - ', searchable: false, treePath: globalTreePath, + name: fieldNameToComponentName(properties.facet.field), }; let props = mergeProps('facet', globalTheme, defaultProps, properties); @@ -428,6 +471,9 @@ export const Facet = observer((properties: FacetProps): JSX.Element => { clearAllText: { value: facetContentProps.clearAllText, }, + submitRangeButton: { + value: facetContentProps.rangeInputsSubmitButtonText, + }, }; //deep merge with props.lang @@ -435,7 +481,6 @@ export const Facet = observer((properties: FacetProps): JSX.Element => { const mergedLang = useLang(lang as any, { facet, }); - facetContentProps.lang = mergedLang; const selectedCount = (facet as ValueFacet)?.values?.filter((value) => value?.filtered).length; @@ -454,7 +499,7 @@ export const Facet = observer((properties: FacetProps): JSX.Element => { )} > {justContent ? ( - + ) : ( {
} > - + )} @@ -514,7 +559,17 @@ export const Facet = observer((properties: FacetProps): JSX.Element => { ); }); -const FacetContent = (props: any) => { +const FacetContent = ( + props: FacetProps & { + limitedValues: (FacetHierarchyValue | FacetValue | FacetRangeValue | undefined)[]; + searchableFacet: { + allowableTypes: string[]; + searchFilter: (e: React.ChangeEvent) => void; + }; + subProps: FacetSubProps; + mergedLang: LangAttributesObj; + } +) => { const { searchableFacet, subProps, @@ -524,20 +579,55 @@ const FacetContent = (props: any) => { facet, limit, overflowSlot, - overflowState, optionsSlot, searchable, iconOverflowMore, iconOverflowLess, disableOverflow, previewOnFocus, + rangeInputs, + rangeInputsPrefix, + rangeInputsInheritDefaultValues, + rangeInputsSeparatorText, justContent, valueProps, hideShowMoreLessText, treePath, - lang, + mergedLang, } = props; + const [low, setLow] = useState( + rangeInputsInheritDefaultValues && facet.type === 'range' ? (facet as RangeFacet)?.range?.low : undefined + ); + const [high, setHigh] = useState( + rangeInputsInheritDefaultValues && facet.type === 'range' ? (facet as RangeFacet)?.range?.high : undefined + ); + + useEffect(() => { + if (rangeInputsInheritDefaultValues && facet.type === 'range' && (facet as RangeFacet)?.active?.high !== high) { + setHigh((facet as RangeFacet)?.active?.high); + } + + if (rangeInputsInheritDefaultValues && facet.type === 'range' && (facet as RangeFacet)?.active?.low !== low) { + setLow((facet as RangeFacet)?.active?.low); + } + }, [facet]); + + const onDragcb = (vals: number[]) => { + setLow(vals[0]); + setHigh(vals[1]); + }; + + const onKeyUp = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + if (typeof low == 'number' && typeof high == 'number') { + submitButtonRef.current?.base?.click(); + } + } + }; + + const submitButtonRef: MutableRef = useRef(); + return ( {searchable && searchableFacet.allowableTypes.includes(facet.display) && ( @@ -558,7 +648,7 @@ const FacetContent = (props: any) => { // case FacetDisplay.TOGGLE: // return ; case FacetDisplay.SLIDER: - return ; + return ; case FacetDisplay.GRID: return ( { })()} - {!disableOverflow && overflowState?.enabled && ( -
overflowState?.toggle()} ref={(e) => useA11y(e)}> + {rangeInputs && (facet.type === 'range' || facet.type === 'range-buckets') && ( +
+
+
+ {rangeInputsPrefix && {rangeInputsPrefix}} + (e.currentTarget.value ? setLow(Number(e.currentTarget.value)) : setLow(undefined))} + onKeyUp={onKeyUp} + /> +
+ + {rangeInputsSeparatorText} + +
+ {rangeInputsPrefix && {rangeInputsPrefix}} + (e.currentTarget.value ? setHigh(Number(e.currentTarget.value)) : setHigh(undefined))} + onKeyUp={onKeyUp} + /> +
+
+
+ +
+
+ )} + + {!disableOverflow && (facet as ValueFacet)?.overflow?.enabled && ( +
(facet as ValueFacet).overflow?.toggle()} + ref={(e) => useA11y(e)} + > {overflowSlot ? ( cloneWithProps(overflowSlot, { facet, treePath }) ) : ( @@ -609,11 +777,15 @@ const FacetContent = (props: any) => { 0 + {...(((facet as ValueFacet).overflow?.remaining || 0) > 0 ? { ...(typeof iconOverflowMore == 'string' ? { icon: iconOverflowMore } : (iconOverflowMore as Partial)) } : { ...(typeof iconOverflowLess == 'string' ? { icon: iconOverflowLess } : (iconOverflowLess as Partial)) })} /> - {!hideShowMoreLessText && 0 ? lang.showMoreText?.all : lang.showLessText?.all)}>} + {!hideShowMoreLessText && ( + 0 ? mergedLang!.showMoreText?.all : mergedLang!.showLessText?.all)} + > + )} )}
@@ -666,6 +838,11 @@ interface OptionalFacetProps extends ComponentProps { fields?: FieldProps; display?: FieldProps; searchable?: boolean; + rangeInputs?: boolean; + rangeInputsSubmitButtonText?: string; + rangeInputsPrefix?: string; + rangeInputsInheritDefaultValues?: boolean; + rangeInputsSeparatorText?: string; justContent?: boolean; horizontal?: boolean; lang?: Partial; @@ -684,6 +861,9 @@ export interface FacetLang { clearAllText: Lang<{ facet: ValueFacet | RangeFacet; }>; + submitRangeButton: Lang<{ + facet: ValueFacet | RangeFacet; + }>; } type FieldProps = { diff --git a/packages/snap-preact/components/src/components/Organisms/Facet/readme.md b/packages/snap-preact/components/src/components/Organisms/Facet/readme.md index d97e75ae2..070eff7e8 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facet/readme.md +++ b/packages/snap-preact/components/src/components/Organisms/Facet/readme.md @@ -16,6 +16,9 @@ Renders a single complete facet. This includes determining the correct options t ## Usage +```jsx +import { Facet } from '@searchspring/snap-preact-components'; +``` ### facet The required `facet` prop specifies a reference to any single facet object within the facets store array. @@ -55,7 +58,7 @@ If using within Autocomplete, the `previewOnFocus` prop will invoke the `value.p ### valueProps The `valueProps` prop will be spread onto each value's `
` element. Typical usage would be to provide custom callback functions when used within Autocomplete. -```typescript +```js const valueProps = { onMouseEnter: (e) => { clearTimeout(delayTimeout); @@ -123,6 +126,41 @@ The `hideSelectedCountParenthesis` prop specifies if the parenthesis should rend ``` +### rangeInputs +The `rangeInputs` prop specifies if the range inputs should render. + +```jsx + +``` + +### rangeInputsSubmitButtonText +The `rangeInputsSubmitButtonText` prop specifies the text to be rendered in the range input submit button. + +```jsx + +``` + +### rangeInputsPrefix +The `rangeInputsPrefix` prop specifies the prefix to render next to the range inputs. + +```jsx + +``` + +### rangeInputsSeparatorText +The `rangeInputsSeparatorText` prop specifies the separator text to render between the range inputs. + +```jsx + +``` + +### rangeInputsInheritDefaultValues +The `rangeInputsInheritDefaultValues` prop enables the facet range input values to default to the facet low and high limits. + +```jsx + +``` + ### showClearAllText The `showClearAllText` prop specifies if the clear all text should render. @@ -164,7 +202,7 @@ The `iconOverflowLess` prop contains the icon name of the facet overflow button ### overflowSlot The `overflowSlot` prop is a JSX element used to change the display of the show more/less toggle. -```typescript +```js const Overflow = (props) => { const facet = props.facet; return ( @@ -181,7 +219,7 @@ const Overflow = (props) => { ### fields The `fields` prop allows you to manually change prop values on a per-facet level, sorted by the facet field. -```typescript +```js const fieldsProp = { Color: { limit: 6, @@ -227,7 +265,7 @@ const displayProp = { ### optionsSlot The `optionsSlot` prop is a JSX element used to manually set the options component used, regardless of the facet.display type. Returns the facet,valueProps, limit, & previewOnFocus prop values. -```typescript +```js const CustomFacetOptions = (props) => { const facet = props.facet; return ( diff --git a/packages/snap-preact/components/src/components/Organisms/Facets/Facets.stories.tsx b/packages/snap-preact/components/src/components/Organisms/Facets/Facets.stories.tsx index a6037ea48..87a11c5af 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facets/Facets.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Facets/Facets.stories.tsx @@ -81,7 +81,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'Facets', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'Facets', globals: { siteId: 'atkzs2' } }); export const Default = (args: FacetsProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => { return ; diff --git a/packages/snap-preact/components/src/components/Organisms/Facets/Facets.test.tsx b/packages/snap-preact/components/src/components/Organisms/Facets/Facets.test.tsx index 902a759ce..7703f6243 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facets/Facets.test.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Facets/Facets.test.tsx @@ -5,7 +5,7 @@ import { ThemeProvider } from '../../../providers'; import userEvent from '@testing-library/user-event'; import { MockData } from '@searchspring/snap-shared'; -import { SearchResponseModel } from '@searchspring/snapi-types'; +import { SearchResponseModel } from '@athoscommerce/snapi-types'; const mockData = new MockData(); const searchResponse: SearchResponseModel = mockData.search(); diff --git a/packages/snap-preact/components/src/components/Organisms/Facets/Facets.tsx b/packages/snap-preact/components/src/components/Organisms/Facets/Facets.tsx index ce697348d..f633c7927 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facets/Facets.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Facets/Facets.tsx @@ -82,16 +82,11 @@ export const Facets = observer((properties: FacetsProps): JSX.Element => { const styling = mergeStyles(props, defaultStyles); - const fieldNameToComponentName = (fieldName: string) => { - // eg. color_family -> color-family - return fieldName.replace(/_/g, '-').toLowerCase(); - }; - return facets && facets?.length > 0 ? (
{facets.map((facet) => ( - + ))}
diff --git a/packages/snap-preact/components/src/components/Organisms/Facets/readme.md b/packages/snap-preact/components/src/components/Organisms/Facets/readme.md index d7c2d1a94..741a33e0b 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facets/readme.md +++ b/packages/snap-preact/components/src/components/Organisms/Facets/readme.md @@ -6,6 +6,9 @@ Renders all facets utilizing the `` component. - Facet ## Usage +```jsx +import { Facets } from '@searchspring/snap-preact-components'; +``` ### controller The `controller` prop specifies a reference to the search controller. diff --git a/packages/snap-preact/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.stories.tsx b/packages/snap-preact/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.stories.tsx index 1ba332944..cc8f0947b 100644 --- a/packages/snap-preact/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.stories.tsx @@ -135,7 +135,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'FacetsHorizontal', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'FacetsHorizontal', globals: { siteId: 'atkzs2' } }); export const Default = (args: FacetsHorizontalProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => { return ; diff --git a/packages/snap-preact/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.test.tsx b/packages/snap-preact/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.test.tsx index acd81a770..abe44a15a 100644 --- a/packages/snap-preact/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.test.tsx +++ b/packages/snap-preact/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.test.tsx @@ -6,7 +6,7 @@ import { ThemeProvider } from '../../../providers'; import userEvent from '@testing-library/user-event'; import { MockData } from '@searchspring/snap-shared'; -import { SearchResponseModel } from '@searchspring/snapi-types'; +import { SearchResponseModel } from '@athoscommerce/snapi-types'; const mockData = new MockData(); const searchResponse: SearchResponseModel = mockData.search(); diff --git a/packages/snap-preact/components/src/components/Organisms/FilterSummary/FilterSummary.stories.tsx b/packages/snap-preact/components/src/components/Organisms/FilterSummary/FilterSummary.stories.tsx index 448ffe0be..51d1777dc 100644 --- a/packages/snap-preact/components/src/components/Organisms/FilterSummary/FilterSummary.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/FilterSummary/FilterSummary.stories.tsx @@ -8,7 +8,7 @@ import { componentArgs, highlightedCode } from '../../../utilities'; import { Snapify } from '../../../utilities/snapify'; import Readme from '../FilterSummary/readme.md'; import type { SearchController } from '@searchspring/snap-controller'; -import type { SearchRequestModelFilterValue } from '@searchspring/snapi-types'; +import type { SearchRequestModelFilterValue } from '@athoscommerce/snapi-types'; export default { title: 'Organisms/FilterSummary', @@ -183,7 +183,7 @@ export default { const snapInstance = Snapify.search({ id: 'FilterSummary', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', filters: [ { type: 'value', diff --git a/packages/snap-preact/components/src/components/Organisms/FilterSummary/FilterSummary.test.tsx b/packages/snap-preact/components/src/components/Organisms/FilterSummary/FilterSummary.test.tsx index 5635cd8ce..b532b1b87 100644 --- a/packages/snap-preact/components/src/components/Organisms/FilterSummary/FilterSummary.test.tsx +++ b/packages/snap-preact/components/src/components/Organisms/FilterSummary/FilterSummary.test.tsx @@ -165,7 +165,7 @@ describe('FilterSummary Component', () => { const rendered = render(); const facetsElement = rendered.container.querySelector('.ss__filter-summary'); - expect(facetsElement?.classList).toHaveLength(1); + expect(facetsElement?.classList).toHaveLength(2); }); }); diff --git a/packages/snap-preact/components/src/components/Organisms/FilterSummary/readme.md b/packages/snap-preact/components/src/components/Organisms/FilterSummary/readme.md index e0983d5ac..bd6710fcf 100644 --- a/packages/snap-preact/components/src/components/Organisms/FilterSummary/readme.md +++ b/packages/snap-preact/components/src/components/Organisms/FilterSummary/readme.md @@ -5,7 +5,9 @@ Renders all selected filters including a wrapper with a title and a 'clear all' ## Components Used - Filter ## Usage - +```jsx +import { FilterSummary } from '@searchspring/snap-preact-components'; +``` ### controller The `controller` prop specifies a reference to the search controller. diff --git a/packages/snap-preact/components/src/components/Organisms/Layout/Layout.tsx b/packages/snap-preact/components/src/components/Organisms/Layout/Layout.tsx index 481ac4fac..c83a7b1bd 100644 --- a/packages/snap-preact/components/src/components/Organisms/Layout/Layout.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Layout/Layout.tsx @@ -228,7 +228,6 @@ export const Layout = observer((properties: LayoutProps): JSX.Element => { const ToggleSideBarButton = toggleSideBarButton; const hasResults = controller.store.pagination.totalResults > 0; - function renderModule(module: ModuleNames) { switch (module) { case 'mobileSidebar': @@ -326,7 +325,6 @@ export const Layout = observer((properties: LayoutProps): JSX.Element => { let separatorIndex = 0; useCleanUpEmptyDivs(['.ss__layout__row'], '.ss__layout__separator'); - return hasChildrenToRender ? (
diff --git a/packages/snap-preact/components/src/components/Organisms/MobileSidebar/MobileSidebar.stories.tsx b/packages/snap-preact/components/src/components/Organisms/MobileSidebar/MobileSidebar.stories.tsx index 012800c26..d58b4288c 100644 --- a/packages/snap-preact/components/src/components/Organisms/MobileSidebar/MobileSidebar.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/MobileSidebar/MobileSidebar.stories.tsx @@ -303,7 +303,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'MobileSidebar', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'MobileSidebar', globals: { siteId: 'atkzs2' } }); export const Default = (args: MobileSidebarProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => { return ; diff --git a/packages/snap-preact/components/src/components/Organisms/Results/Results.stories.tsx b/packages/snap-preact/components/src/components/Organisms/Results/Results.stories.tsx index 5d9646670..11e0af36a 100644 --- a/packages/snap-preact/components/src/components/Organisms/Results/Results.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Results/Results.stories.tsx @@ -128,7 +128,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'Results', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'Results', globals: { siteId: 'atkzs2' } }); export const Grid = (args: ResultsProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => { return ; diff --git a/packages/snap-preact/components/src/components/Organisms/Results/readme.md b/packages/snap-preact/components/src/components/Organisms/Results/readme.md index 297fd5e97..832252f64 100644 --- a/packages/snap-preact/components/src/components/Organisms/Results/readme.md +++ b/packages/snap-preact/components/src/components/Organisms/Results/readme.md @@ -10,6 +10,9 @@ Renders a page of results utilizing `` components. - ResultTracker ## Usage +```jsx +import { Results } from '@searchspring/snap-preact-components'; +``` ### controller The `controller` prop specifies a reference to the search controller. @@ -79,7 +82,7 @@ Typically used to adjust the layout and how many products are shown at any scree Default Results `breakpoints` object: -```typescript +```js const breakpoints = { 0: { columns: 1, diff --git a/packages/snap-preact/components/src/components/Organisms/Sidebar/Sidebar.stories.tsx b/packages/snap-preact/components/src/components/Organisms/Sidebar/Sidebar.stories.tsx index 31def09d2..2bae33b8f 100644 --- a/packages/snap-preact/components/src/components/Organisms/Sidebar/Sidebar.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Sidebar/Sidebar.stories.tsx @@ -98,7 +98,7 @@ export default { }, }; -const snapInstance = Snapify.search({ id: 'Sidebar', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.search({ id: 'Sidebar', globals: { siteId: 'atkzs2' } }); export const Default = (args: SidebarProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => { return ; diff --git a/packages/snap-preact/components/src/components/Organisms/TermsList/TermsList.stories.tsx b/packages/snap-preact/components/src/components/Organisms/TermsList/TermsList.stories.tsx index 4c9157d01..4f6cf1ecd 100644 --- a/packages/snap-preact/components/src/components/Organisms/TermsList/TermsList.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/TermsList/TermsList.stories.tsx @@ -145,7 +145,7 @@ const snapInstance = Snapify.autocomplete({ id: 'Autocomplete-TermsList', selector: '#searchInput', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', }, settings: { trending: { diff --git a/packages/snap-preact/components/src/components/Organisms/Toolbar/Toolbar.stories.tsx b/packages/snap-preact/components/src/components/Organisms/Toolbar/Toolbar.stories.tsx index e5043bdaf..2bca87837 100644 --- a/packages/snap-preact/components/src/components/Organisms/Toolbar/Toolbar.stories.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Toolbar/Toolbar.stories.tsx @@ -7,7 +7,6 @@ import { Snapify } from '../../../utilities/snapify'; import Readme from './readme.md'; import type { SearchController } from '@searchspring/snap-controller'; import { Toolbar, ToolbarProps } from './Toolbar'; -import { SearchRequestModelFilterValue } from '@searchspring/snapi-types'; export default { title: 'Organisms/Toolbar', @@ -81,19 +80,7 @@ export default { const snapInstance = Snapify.search({ id: 'Toolbar', globals: { - siteId: '8uyt2m', - filters: [ - { - type: 'value', - field: 'color_family', - value: 'Blue', - } as SearchRequestModelFilterValue, - { - type: 'value', - field: 'size', - value: 'Small', - } as SearchRequestModelFilterValue, - ], + siteId: 'atkzs2', }, }); diff --git a/packages/snap-preact/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.stories.tsx b/packages/snap-preact/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.stories.tsx index 0eab7bec4..b1df1cf3f 100644 --- a/packages/snap-preact/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.stories.tsx +++ b/packages/snap-preact/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.stories.tsx @@ -51,6 +51,9 @@ export default { border: '1px solid #3a23ad', }} /> + + Storybook bug: if no autocomplete renders. please refresh the page.{' '} +
), @@ -247,7 +250,7 @@ const snapInstance = Snapify.autocomplete({ id: 'AutocompleteModal', selector: '#searchInput', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', }, settings: { trending: { diff --git a/packages/snap-preact/components/src/components/Templates/AutocompleteModal/AutocompleteModal.stories.tsx b/packages/snap-preact/components/src/components/Templates/AutocompleteModal/AutocompleteModal.stories.tsx index 7cfdb86c2..9b8fdbace 100644 --- a/packages/snap-preact/components/src/components/Templates/AutocompleteModal/AutocompleteModal.stories.tsx +++ b/packages/snap-preact/components/src/components/Templates/AutocompleteModal/AutocompleteModal.stories.tsx @@ -51,6 +51,9 @@ export default { border: '1px solid #3a23ad', }} /> + + Storybook bug: if no autocomplete renders. please refresh the page.{' '} +
), @@ -235,7 +238,7 @@ const snapInstance = Snapify.autocomplete({ id: 'AutocompleteModal', selector: '#searchInput', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', }, settings: { trending: { diff --git a/packages/snap-preact/components/src/components/Templates/AutocompleteModal/AutocompleteModal.test.tsx b/packages/snap-preact/components/src/components/Templates/AutocompleteModal/AutocompleteModal.test.tsx index 573e56ff6..527fc13ab 100644 --- a/packages/snap-preact/components/src/components/Templates/AutocompleteModal/AutocompleteModal.test.tsx +++ b/packages/snap-preact/components/src/components/Templates/AutocompleteModal/AutocompleteModal.test.tsx @@ -157,10 +157,13 @@ describe('AutocompleteModal Component', () => { await waitFor(() => { const renderedInput = document.querySelector(renderedInputSelector) as HTMLInputElement; renderedInput.value = 'dress'; + + const leftBanner = rendered.container.querySelector('.ss__autocomplete__facets .ss__banner--left'); const bannerBanner = rendered.container.querySelector('.ss__autocomplete__content .ss__banner--banner'); const headerBanner = rendered.container.querySelector('.ss__autocomplete__content .ss__banner--header'); const footerBanner = rendered.container.querySelector('.ss__autocomplete__content .ss__banner--footer'); + expect(leftBanner).toBeInTheDocument(); expect(bannerBanner).toBeInTheDocument(); expect(headerBanner).toBeInTheDocument(); expect(footerBanner).toBeInTheDocument(); diff --git a/packages/snap-preact/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.stories.tsx b/packages/snap-preact/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.stories.tsx index 562cc1021..9fa9df27d 100644 --- a/packages/snap-preact/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.stories.tsx +++ b/packages/snap-preact/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.stories.tsx @@ -51,6 +51,9 @@ export default { border: '1px solid #3a23ad', }} /> + + Storybook bug: if no autocomplete renders. please refresh the page.{' '} + ), @@ -239,7 +242,7 @@ const snapInstance = Snapify.autocomplete({ id: 'AutocompleteSlideout', selector: '#searchInput', globals: { - siteId: '8uyt2m', + siteId: 'atkzs2', }, settings: { trending: { diff --git a/packages/snap-preact/components/src/components/Templates/Recommendation/Recommendation.stories.tsx b/packages/snap-preact/components/src/components/Templates/Recommendation/Recommendation.stories.tsx index c52512bfc..61a3be7f1 100644 --- a/packages/snap-preact/components/src/components/Templates/Recommendation/Recommendation.stories.tsx +++ b/packages/snap-preact/components/src/components/Templates/Recommendation/Recommendation.stories.tsx @@ -77,6 +77,16 @@ export default { }, control: { type: 'boolean' }, }, + description: { + description: 'recommendation description', + table: { + type: { + summary: 'string | JSX Element', + }, + defaultValue: { summary: '' }, + }, + control: { type: 'text' }, + }, loop: { defaultValue: true, description: 'Recommendation pagination loops', @@ -207,7 +217,7 @@ export default { }, }; -const snapInstance = Snapify.recommendation({ id: 'Recommendation', tag: 'trending', globals: { siteId: '8uyt2m' } }); +const snapInstance = Snapify.recommendation({ id: 'Recommendation', tag: 'trending', globals: { siteId: 'atkzs2' } }); export const Default = (props: RecommendationProps, { loaded: { controller } }: { loaded: { controller: RecommendationController } }) => { return ; diff --git a/packages/snap-preact/components/src/components/Templates/Recommendation/Recommendation.tsx b/packages/snap-preact/components/src/components/Templates/Recommendation/Recommendation.tsx index a209716a0..d7769e2a6 100644 --- a/packages/snap-preact/components/src/components/Templates/Recommendation/Recommendation.tsx +++ b/packages/snap-preact/components/src/components/Templates/Recommendation/Recommendation.tsx @@ -40,6 +40,7 @@ export const Recommendation = observer((properties: RecommendationProps): JSX.El pagination: false, loop: true, title: properties.controller?.store?.profile?.display?.templateParameters?.title, + description: properties.controller?.store?.profile?.display?.templateParameters?.description, }; //mergeprops only uses names that are passed via properties, so this cannot be put in the defaultProps @@ -68,6 +69,7 @@ export const Recommendation = observer((properties: RecommendationProps): JSX.El const { title, + description, controller, children, loop, @@ -167,6 +169,8 @@ export const Recommendation = observer((properties: RecommendationProps): JSX.El {title} )} + {description &&

{description}

} + ``` +### description +The `description` prop specifies the carousel description + +```jsx + +``` + ### pagination The `pagination` prop specifies if the carousel should display pagination dots. @@ -136,7 +146,7 @@ The default configuration contains the following properties, however **`any Reco `spaceBetween` - spacing between each product -```typescript +```js const defaultRecommendationBreakpoints = { 0: { slidesPerView: 1, diff --git a/packages/snap-preact/components/src/components/Templates/RecommendationBundle/BundleCTA.tsx b/packages/snap-preact/components/src/components/Templates/RecommendationBundle/BundleCTA.tsx index fc6fe47c3..dd99bfb3f 100644 --- a/packages/snap-preact/components/src/components/Templates/RecommendationBundle/BundleCTA.tsx +++ b/packages/snap-preact/components/src/components/Templates/RecommendationBundle/BundleCTA.tsx @@ -67,7 +67,9 @@ export const BundledCTA = observer((properties: BundledCTAProps): JSX.Element => //deep merge with props.lang const lang = deepmerge({}, props.lang || {}); - const mergedLang = useLang(lang as any, {}); + const mergedLang = useLang(lang as any, { + cartStore, + }); return (
{ctaSlot ? ( @@ -82,7 +84,7 @@ export const BundledCTA = observer((properties: BundledCTAProps): JSX.Element => ) : ( )} - {`Subtotal for ${cartStore.count} items`} +
{cartStore.msrp && cartStore.msrp !== cartStore.price ? (