diff --git a/packages/snap-preact-demo/templates/src/index.ts b/packages/snap-preact-demo/templates/src/index.ts index b93edb234..cf2ac7a9f 100644 --- a/packages/snap-preact-demo/templates/src/index.ts +++ b/packages/snap-preact-demo/templates/src/index.ts @@ -30,7 +30,7 @@ new SnapTemplates({ global: { extends: 'bocachica', variables: { - color: { + colors: { primary: 'red', secondary: 'blue', }, 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 e8563c60c..d805560ce 100644 --- a/packages/snap-preact/components/src/components/Atoms/Button/Button.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Button/Button.tsx @@ -18,13 +18,13 @@ const CSS = { display: 'inline-flex', padding: '5px 10px', position: 'relative', - color: color || theme?.variables?.color?.primary, + color: color || theme?.variables?.colors?.primary, outline: 0, backgroundColor: backgroundColor || '#fff', - border: `1px solid ${borderColor || color || theme?.variables?.color?.primary || '#333'}`, + border: `1px solid ${borderColor || color || theme?.variables?.colors?.primary || '#333'}`, '&:hover': { cursor: 'pointer', - backgroundColor: theme?.variables?.color?.hover?.background || '#f8f8f8', + backgroundColor: theme?.variables?.colors?.hover?.background || '#f8f8f8', }, '&.ss__button--disabled': { opacity: 0.7, diff --git a/packages/snap-preact/components/src/components/Atoms/Icon/Icon.tsx b/packages/snap-preact/components/src/components/Atoms/Icon/Icon.tsx index 8fceed6d3..71d6f8fc4 100644 --- a/packages/snap-preact/components/src/components/Atoms/Icon/Icon.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Icon/Icon.tsx @@ -11,8 +11,8 @@ import { mergeProps } from '../../../utilities'; const CSS = { icon: ({ color, height, width, size, theme }: Partial) => css({ - fill: color || theme?.variables?.color?.primary || '#333', - stroke: color || theme?.variables?.color?.primary || '#333', + fill: color || theme?.variables?.colors?.primary || '#333', + stroke: color || theme?.variables?.colors?.primary || '#333', width: isNaN(Number(width || size)) ? width || size : `${width || size}px`, height: isNaN(Number(height || size)) ? height || size : `${height || size}px`, position: 'relative', diff --git a/packages/snap-preact/components/src/components/Atoms/Loading/LoadingBar.tsx b/packages/snap-preact/components/src/components/Atoms/Loading/LoadingBar.tsx index 2fe7a0b3a..5f75503c5 100644 --- a/packages/snap-preact/components/src/components/Atoms/Loading/LoadingBar.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Loading/LoadingBar.tsx @@ -20,14 +20,14 @@ const CSS = { opacity: '1', visibility: 'visible', zIndex: '10000', - background: backgroundColor || theme?.variables?.color?.secondary || '#f8f8f8', + background: backgroundColor || theme?.variables?.colors?.secondary || '#f8f8f8', '& .ss__loading-bar__bar': { position: 'absolute', top: '0', left: '-200px', height: '100%', - background: `${color || theme?.variables?.color?.primary || '#ccc'}`, + background: `${color || theme?.variables?.colors?.primary || '#ccc'}`, animation: `${animation} 2s linear infinite`, }, }), diff --git a/packages/snap-preact/components/src/components/Atoms/Merchandising/InlineBanner/InlineBanner.stories.tsx b/packages/snap-preact/components/src/components/Atoms/Merchandising/InlineBanner/InlineBanner.stories.tsx index b53311c7f..af748381e 100644 --- a/packages/snap-preact/components/src/components/Atoms/Merchandising/InlineBanner/InlineBanner.stories.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Merchandising/InlineBanner/InlineBanner.stories.tsx @@ -46,13 +46,13 @@ export default { }, layout: { description: 'Banner layout', - defaultValue: ResultsLayout.GRID, + defaultValue: ResultsLayout.grid, table: { type: { summary: 'string', }, }, - options: [ResultsLayout.GRID, ResultsLayout.LIST], + options: [ResultsLayout.grid, ResultsLayout.list], control: { type: 'select', }, diff --git a/packages/snap-preact/components/src/components/Atoms/Merchandising/InlineBanner/InlineBanner.tsx b/packages/snap-preact/components/src/components/Atoms/Merchandising/InlineBanner/InlineBanner.tsx index eedd672cd..ececfbce8 100644 --- a/packages/snap-preact/components/src/components/Atoms/Merchandising/InlineBanner/InlineBanner.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Merchandising/InlineBanner/InlineBanner.tsx @@ -3,7 +3,7 @@ import { Fragment, h } from 'preact'; import { jsx, css } from '@emotion/react'; import classnames from 'classnames'; import { Theme, useTheme, CacheProvider } from '../../../../providers'; -import { ComponentProps, ResultsLayout, ResultsLayoutType, RootNodeProperties } from '../../../../types'; +import { ComponentProps, ResultsLayout, RootNodeProperties } from '../../../../types'; import { mergeProps } from '../../../../utilities'; import type { Banner } from '@searchspring/snap-store-mobx'; @@ -34,7 +34,7 @@ const CSS = { export function InlineBanner(properties: InlineBannerProps): JSX.Element { const globalTheme: Theme = useTheme(); const defaultProps: Partial = { - layout: ResultsLayout.GRID, + layout: ResultsLayout.grid, width: 'auto', }; @@ -74,6 +74,6 @@ export function InlineBanner(properties: InlineBannerProps): JSX.Element { export interface InlineBannerProps extends ComponentProps { banner: Banner; width?: string; - layout?: ResultsLayoutType; + layout?: keyof typeof ResultsLayout | ResultsLayout; onClick?: (e: React.MouseEvent, banner: Banner) => void; } diff --git a/packages/snap-preact/components/src/components/Atoms/Price/Price.tsx b/packages/snap-preact/components/src/components/Atoms/Price/Price.tsx index 5d5fba85d..682765403 100644 --- a/packages/snap-preact/components/src/components/Atoms/Price/Price.tsx +++ b/packages/snap-preact/components/src/components/Atoms/Price/Price.tsx @@ -12,7 +12,7 @@ import { mergeProps } from '../../../utilities'; const CSS = { price: ({ theme }: Partial) => css({ - color: theme?.variables?.color?.primary, + color: theme?.variables?.colors?.primary, '&.ss__price--strike': { textDecoration: 'line-through', color: 'initial', diff --git a/packages/snap-preact/components/src/components/Molecules/Carousel/Carousel.tsx b/packages/snap-preact/components/src/components/Molecules/Carousel/Carousel.tsx index 90841c5d6..a25520d03 100644 --- a/packages/snap-preact/components/src/components/Molecules/Carousel/Carousel.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Carousel/Carousel.tsx @@ -59,7 +59,7 @@ const CSS = { }, }, '.swiper-pagination-bullet-active': { - background: theme?.variables?.color?.primary || 'inherit', + background: theme?.variables?.colors?.primary || 'inherit', }, '.ss__carousel__next-wrapper, .ss__carousel__prev-wrapper': { display: 'flex', @@ -130,7 +130,7 @@ const CSS = { margin: '0 4px', '&.swiper-pagination-bullet-active': { opacity: '0.8', - background: theme?.variables?.color?.primary || '#000', + background: theme?.variables?.colors?.primary || '#000', }, }, '.swiper-slide-invisible-blank': { diff --git a/packages/snap-preact/components/src/components/Molecules/Checkbox/Checkbox.tsx b/packages/snap-preact/components/src/components/Molecules/Checkbox/Checkbox.tsx index 62b300519..0ed0a1e84 100644 --- a/packages/snap-preact/components/src/components/Molecules/Checkbox/Checkbox.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Checkbox/Checkbox.tsx @@ -22,7 +22,7 @@ const CSS = { justifyContent: 'center', height: pixelSize, width: pixelSize, - border: `1px solid ${color || theme?.variables?.color?.primary || '#333'}`, + border: `1px solid ${color || theme?.variables?.colors?.primary || '#333'}`, '&.ss__checkbox--disabled': { opacity: 0.7, }, @@ -76,7 +76,7 @@ export const Checkbox = observer((properties: CheckboxProps): JSX.Element => { ...globalTheme?.components?.icon, // inherited props ...defined({ - color: iconColor || color || theme?.variables?.color?.primary, + color: iconColor || color || theme?.variables?.colors?.primary, disableStyles, icon, size: size && `calc(${pixelSize} - 30%)`, diff --git a/packages/snap-preact/components/src/components/Molecules/ErrorHandler/ErrorHandler.tsx b/packages/snap-preact/components/src/components/Molecules/ErrorHandler/ErrorHandler.tsx index 054b2e8ea..91250e4d4 100644 --- a/packages/snap-preact/components/src/components/Molecules/ErrorHandler/ErrorHandler.tsx +++ b/packages/snap-preact/components/src/components/Molecules/ErrorHandler/ErrorHandler.tsx @@ -38,7 +38,7 @@ const CSS = { '& .ss__error-handler__button': { backgroundColor: 'white', color: 'inherit', - borderColor: theme?.variables?.color?.primary, + borderColor: theme?.variables?.colors?.primary, width: ['150px', 'fit-content'], margin: '5px 10px', diff --git a/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.tsx b/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.tsx index d1d4e032d..629b24604 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetGridOptions/FacetGridOptions.tsx @@ -26,7 +26,7 @@ const CSS = { justifyContent: 'center', alignItems: 'center', flex: '0 1 auto', - border: `1px solid ${theme?.variables?.color?.primary || '#333'}`, + border: `1px solid ${theme?.variables?.colors?.primary || '#333'}`, textAlign: 'center', wordBreak: 'break-all', boxSizing: 'border-box', @@ -38,12 +38,12 @@ const CSS = { marginRight: '0', }, '&.ss__facet-grid-options__option--filtered': { - background: theme?.variables?.color?.primary || '#ccc', - color: theme?.variables?.color?.secondary, + background: theme?.variables?.colors?.primary || '#ccc', + color: theme?.variables?.colors?.secondary, }, '&:hover:not(.ss__facet-grid-options__option--filtered)': { cursor: 'pointer', - background: theme?.variables?.color?.hover?.background || '#f8f8f8', + background: theme?.variables?.colors?.hover?.background || '#f8f8f8', }, '& .ss__facet-grid-options__option__value': { '&.ss__facet-grid-options__option__value--smaller': { diff --git a/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.tsx b/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.tsx index 59d206d34..0d0506d38 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetHierarchyOptions/FacetHierarchyOptions.tsx @@ -22,11 +22,11 @@ const CSS = { alignItems: 'center', '&:hover': { cursor: 'pointer', - background: theme?.variables?.color?.hover?.background, + background: theme?.variables?.colors?.hover?.background, }, '&.ss__facet-hierarchy-options__option--filtered': { fontWeight: 'bold', - color: theme?.variables?.color?.primary, + color: theme?.variables?.colors?.primary, '&:hover': { cursor: 'default', background: 'unset', @@ -39,7 +39,7 @@ const CSS = { '&:before': { content: `'\\0000ab'`, padding: '0 2px 0 0', - color: theme?.variables?.color?.primary, + color: theme?.variables?.colors?.primary, }, }, '& .ss__facet-hierarchy-options__option__value': { @@ -63,11 +63,11 @@ const CSS = { '&:hover': { cursor: 'pointer', - background: theme?.variables?.color?.hover?.background, + background: theme?.variables?.colors?.hover?.background, }, '&.ss__facet-hierarchy-options__option--filtered': { fontWeight: 'bold', - color: theme?.variables?.color?.primary, + color: theme?.variables?.colors?.primary, marginRight: '2em', '&:hover': { cursor: 'default', @@ -78,7 +78,7 @@ const CSS = { '&:before': { content: `'\\0000ab'`, padding: '0 2px 0 0', - color: theme?.variables?.color?.primary, + color: theme?.variables?.colors?.primary, }, }, '& .ss__facet-hierarchy-options__option__value': { diff --git a/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.tsx b/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.tsx index bafb3bd1a..5a45f2080 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetListOptions/FacetListOptions.tsx @@ -27,11 +27,11 @@ const CSS = { textDecoration: 'none', '&:hover': { cursor: 'pointer', - background: theme?.variables?.color?.hover?.background, + background: theme?.variables?.colors?.hover?.background, }, '&.ss__facet-list-options__option--filtered': { fontWeight: 'bold', - color: theme?.variables?.color?.primary, + color: theme?.variables?.colors?.primary, }, '& .ss__facet-list-options__option__value': { marginLeft: hideCheckbox ? '' : '8px', diff --git a/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.tsx b/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.tsx index bc2cd4f43..7b4011dec 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetPaletteOptions/FacetPaletteOptions.tsx @@ -47,7 +47,7 @@ const CSS = { }, '&.ss__facet-palette-options__option--filtered': { '& .ss__facet-palette-options__option__wrapper': { - borderColor: theme?.variables?.color?.primary || '#333', + borderColor: theme?.variables?.colors?.primary || '#333', padding: '0px', borderWidth: '4px', }, @@ -129,7 +129,7 @@ const CSS = { '.ss__facet-palette-options__option--filtered': { '& .ss__facet-palette-options__option__wrapper': { - borderColor: theme?.variables?.color?.primary || '#333' + ' !important', + borderColor: theme?.variables?.colors?.primary || '#333' + ' !important', padding: '0px', borderWidth: '4px', }, 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 7c53c58f4..042971205 100644 --- a/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.tsx +++ b/packages/snap-preact/components/src/components/Molecules/FacetSlider/FacetSlider.tsx @@ -61,18 +61,18 @@ const CSS = { }, }, '& .ss__facet-slider__rail': { - background: railColor || theme?.variables?.color?.primary || '#333', + background: railColor || theme?.variables?.colors?.primary || '#333', height: '100%', }, '& .ss__facet-slider__segment': { - background: trackColor || theme?.variables?.color?.secondary || '#ccc', + background: trackColor || theme?.variables?.colors?.secondary || '#ccc', height: '100%', }, '& .ss__facet-slider__handles': { textAlign: 'center', '& button': { '& .ss__facet-slider__handle': { - background: handleColor || theme?.variables?.color?.primary || '#333', + background: handleColor || theme?.variables?.colors?.primary || '#333', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -104,7 +104,7 @@ const CSS = { }, '&.ss__facet-slider__handle--active': { - background: handleDraggingColor || handleColor || theme?.variables?.color?.primary || '#000', + background: handleDraggingColor || handleColor || theme?.variables?.colors?.primary || '#000', '& label.ss__facet-slider__handle__label': { background: '#fff', padding: '0 5px', diff --git a/packages/snap-preact/components/src/components/Molecules/Grid/Grid.tsx b/packages/snap-preact/components/src/components/Molecules/Grid/Grid.tsx index 21cc7b74b..5d3143a7a 100644 --- a/packages/snap-preact/components/src/components/Molecules/Grid/Grid.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Grid/Grid.tsx @@ -32,7 +32,7 @@ const CSS = { justifyContent: 'center', alignItems: 'center', flex: '0 1 auto', - border: `1px solid ${theme?.variables?.color?.primary || '#333'}`, + border: `1px solid ${theme?.variables?.colors?.primary || '#333'}`, textAlign: 'center', wordBreak: 'break-all', padding: '1em 0', @@ -47,7 +47,7 @@ const CSS = { marginRight: '0', }, '&.ss__grid__option--selected': { - border: `2px solid ${theme?.variables?.color?.primary || '#333'}`, + border: `2px solid ${theme?.variables?.colors?.primary || '#333'}`, }, '&.ss__grid__option--disabled': { @@ -76,7 +76,7 @@ const CSS = { '&:hover:not(.ss__grid__option--selected)': { cursor: 'pointer', - background: theme?.variables?.color?.hover?.background || '#f8f8f8', + background: theme?.variables?.colors?.hover?.background || '#f8f8f8', }, }, diff --git a/packages/snap-preact/components/src/components/Molecules/LoadMore/LoadMore.tsx b/packages/snap-preact/components/src/components/Molecules/LoadMore/LoadMore.tsx index a98fbbbe4..d9e1f2856 100644 --- a/packages/snap-preact/components/src/components/Molecules/LoadMore/LoadMore.tsx +++ b/packages/snap-preact/components/src/components/Molecules/LoadMore/LoadMore.tsx @@ -53,11 +53,11 @@ const CSS = { gap: '5px', '& .ss__load-more__progress__indicator': { width: progressIndicatorWidth, - background: backgroundColor || theme?.variables?.color?.secondary || '#f8f8f8', + background: backgroundColor || theme?.variables?.colors?.secondary || '#f8f8f8', borderRadius: progressIndicatorSize, '& .ss__load-more__progress__indicator__bar': { width: pagination ? `${(pagination.end / pagination.totalResults) * 100}%` : '', - background: color || theme?.variables?.color?.primary || '#ccc', + background: color || theme?.variables?.colors?.primary || '#ccc', borderRadius: progressIndicatorSize, height: progressIndicatorSize, }, @@ -80,7 +80,7 @@ const CSS = { '& .ss__load-more__progress__indicator': { '& .ss__load-more__progress__indicator__radial': { - background: backgroundColor || theme?.variables?.color?.secondary || '#f8f8f8', + background: backgroundColor || theme?.variables?.colors?.secondary || '#f8f8f8', height: progressIndicatorWidth, width: progressIndicatorWidth, borderRadius: '50%', @@ -113,7 +113,7 @@ const CSS = { '& .ss__load-more__progress__indicator__radial__mask': { '& .ss__load-more__progress__indicator__radial__mask__fill': { clipPath: `inset(0px calc((${progressIndicatorWidth} / 2)) 0px 0px)`, - backgroundColor: color || theme?.variables?.color?.primary || '#ccc', + backgroundColor: color || theme?.variables?.colors?.primary || '#ccc', }, '&.ss__load-more__progress__indicator__radial__mask--full': { transform: `rotate(${radialAngle}deg)`, diff --git a/packages/snap-preact/components/src/components/Molecules/Pagination/Pagination.tsx b/packages/snap-preact/components/src/components/Molecules/Pagination/Pagination.tsx index ceba03a84..a304159cf 100644 --- a/packages/snap-preact/components/src/components/Molecules/Pagination/Pagination.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Pagination/Pagination.tsx @@ -26,7 +26,7 @@ const CSS = { fontWeight: 'bold', }, '&:hover:not(.ss__pagination__page--active)': { - backgroundColor: theme?.variables?.color?.hover?.background || '#f8f8f8', + backgroundColor: theme?.variables?.colors?.hover?.background || '#f8f8f8', }, }, }), 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 01577ac4b..3aa07358a 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 @@ -134,13 +134,13 @@ export default { }, layout: { description: 'Results layout', - defaultValue: ResultsLayout.GRID, + defaultValue: ResultsLayout.grid, table: { type: { summary: 'string', }, }, - options: [ResultsLayout.GRID, ResultsLayout.LIST], + options: [ResultsLayout.grid, ResultsLayout.list], control: { type: 'select', }, 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 ec687c60d..37508c45b 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 @@ -97,9 +97,9 @@ describe('Result Component', () => { }); it('should can change the layout', () => { - const rendered = render(); + const rendered = render(); const Element = rendered.container.querySelector('.ss__result'); - expect(Element).toHaveClass(`ss__result--${ResultsLayout.LIST}`); + expect(Element).toHaveClass(`ss__result--${ResultsLayout.list}`); }); it('can truncate the title', () => { 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 4de03f470..886e6ac01 100644 --- a/packages/snap-preact/components/src/components/Molecules/Result/Result.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Result/Result.tsx @@ -9,7 +9,7 @@ import { Price, PriceProps } from '../../Atoms/Price'; import { Theme, useTheme, CacheProvider } from '../../../providers'; import { defined, cloneWithProps, mergeProps } from '../../../utilities'; import { filters } from '@searchspring/snap-toolbox'; -import { ComponentProps, ResultsLayoutType, ResultsLayout, RootNodeProperties } from '../../../types'; +import { ComponentProps, ResultsLayout, RootNodeProperties } from '../../../types'; import { CalloutBadge, CalloutBadgeProps } from '../../Molecules/CalloutBadge'; import { OverlayBadge, OverlayBadgeProps } from '../../Molecules/OverlayBadge'; import type { SearchController, AutocompleteController, RecommendationController } from '@searchspring/snap-controller'; @@ -77,7 +77,7 @@ export const Result = observer((properties: ResultProps): JSX.Element => { const globalTheme: Theme = useTheme(); const defaultProps: Partial = { - layout: ResultsLayout.GRID, + layout: ResultsLayout.grid, }; const props = mergeProps('result', globalTheme, defaultProps, properties); @@ -265,7 +265,7 @@ export interface ResultProps extends ComponentProps { hidePricing?: boolean; detailSlot?: JSX.Element | JSX.Element[]; fallback?: string; - layout?: ResultsLayoutType; + layout?: keyof typeof ResultsLayout | ResultsLayout; truncateTitle?: TruncateTitleProps; onClick?: (e: React.MouseEvent) => void; controller?: SearchController | AutocompleteController | RecommendationController; diff --git a/packages/snap-preact/components/src/components/Molecules/SearchInput/SearchInput.tsx b/packages/snap-preact/components/src/components/Molecules/SearchInput/SearchInput.tsx index b3d781ba3..fc1a7165e 100644 --- a/packages/snap-preact/components/src/components/Molecules/SearchInput/SearchInput.tsx +++ b/packages/snap-preact/components/src/components/Molecules/SearchInput/SearchInput.tsx @@ -15,7 +15,7 @@ const CSS = { display: 'flex', alignItems: 'center', justifyContent: 'center', - border: `1px solid ${theme?.variables?.color?.primary || '#ccc'}`, + border: `1px solid ${theme?.variables?.colors?.primary || '#ccc'}`, '& .ss__icon': { padding: '5px', diff --git a/packages/snap-preact/components/src/components/Molecules/Select/Select.tsx b/packages/snap-preact/components/src/components/Molecules/Select/Select.tsx index a6e91f1c7..b13a46567 100644 --- a/packages/snap-preact/components/src/components/Molecules/Select/Select.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Select/Select.tsx @@ -41,7 +41,7 @@ const CSS = { listStyle: 'none', padding: '0', marginTop: '-1px', - border: `1px solid ${borderColor || color || theme?.variables?.color?.primary || '#333'}`, + border: `1px solid ${borderColor || color || theme?.variables?.colors?.primary || '#333'}`, '.ss__select__dropdown__button': { alignItems: 'center', @@ -62,7 +62,7 @@ const CSS = { fontWeight: 'bold', }, '&:hover': { - backgroundColor: theme?.variables?.color?.hover?.background || '#f8f8f8', + backgroundColor: theme?.variables?.colors?.hover?.background || '#f8f8f8', }, }, }, 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 4b5a48644..7568f16e9 100644 --- a/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.tsx +++ b/packages/snap-preact/components/src/components/Molecules/Swatches/Swatches.tsx @@ -25,13 +25,13 @@ const CSS = { display: 'flex', justifyContent: 'center', alignItems: 'center', - border: `1px solid ${theme?.variables?.color?.primary || '#333'}`, + border: `1px solid ${theme?.variables?.colors?.primary || '#333'}`, aspectRatio: '1/1', margin: 'auto', flexDirection: 'column', '&.ss__swatches__carousel__swatch--selected': { - border: `2px solid ${theme?.variables?.color?.primary || '#333'}`, + border: `2px solid ${theme?.variables?.colors?.primary || '#333'}`, }, '&.ss__swatches__carousel__swatch--disabled:before, &.ss__swatches__carousel__swatch--unavailable:before': { @@ -247,8 +247,7 @@ export type SwatchesProps = { carousel?: Partial; grid?: Partial; type?: 'carousel' | 'grid'; -} & // | { // } // carousel?: Partial; // type?: 'carousel'; // | { // & ( -// type?: 'grid'; +} & // type?: 'grid'; // | { // } // carousel?: Partial; // type?: 'carousel'; // | { // & ( // grid?: Partial; // } // ) 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 4cd6a0b7e..ea8c12f75 100644 --- a/packages/snap-preact/components/src/components/Organisms/Facet/Facet.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Facet/Facet.tsx @@ -33,7 +33,7 @@ const CSS = { alignItems: 'center', color: color, border: 'none', - borderBottom: `2px solid ${theme?.variables?.color?.primary || '#ccc'}`, + borderBottom: `2px solid ${theme?.variables?.colors?.primary || '#ccc'}`, padding: '6px 0', }, '& .ss__facet__options': { diff --git a/packages/snap-preact/components/src/components/Organisms/HorizontalFacets/HorizontalFacets.tsx b/packages/snap-preact/components/src/components/Organisms/HorizontalFacets/HorizontalFacets.tsx index 3572d8d05..1ac223e6f 100644 --- a/packages/snap-preact/components/src/components/Organisms/HorizontalFacets/HorizontalFacets.tsx +++ b/packages/snap-preact/components/src/components/Organisms/HorizontalFacets/HorizontalFacets.tsx @@ -44,7 +44,7 @@ const CSS = { '&.ss__dropdown--open': { '& .ss__dropdown__button__heading': { '& .ss__icon': { - fill: theme?.variables?.color?.active?.accent, + fill: theme?.variables?.colors?.active?.accent, }, }, '& .ss__dropdown__content': { @@ -62,7 +62,7 @@ const CSS = { '& .ss__horizontal-facets__header__dropdown': { '&.ss__dropdown--open': { '& .ss__dropdown__content': { - border: `1px solid ${theme?.variables?.color?.active?.background || '#ccc'}`, + border: `1px solid ${theme?.variables?.colors?.active?.background || '#ccc'}`, }, }, }, 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 851ea486f..ebfe85a05 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 @@ -61,7 +61,7 @@ export default { summary: 'string', }, }, - options: [ResultsLayout.GRID, ResultsLayout.LIST], + options: [ResultsLayout.grid, ResultsLayout.list], control: { type: 'select', }, @@ -136,7 +136,7 @@ Grid.loaders = [ ]; export const List = (args: ResultsProps, { loaded: { controller } }: { loaded: { controller: SearchController } }) => { - return ; + return ; }; List.loaders = [ diff --git a/packages/snap-preact/components/src/components/Organisms/Results/Results.test.tsx b/packages/snap-preact/components/src/components/Organisms/Results/Results.test.tsx index 80821633b..c37f1f73c 100644 --- a/packages/snap-preact/components/src/components/Organisms/Results/Results.test.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Results/Results.test.tsx @@ -40,7 +40,7 @@ describe('Results Component', () => { }; it('renders grid view', () => { - const rendered = render(); + const rendered = render(); const resultElement = rendered.getByText(mockResults[0].mappings.core?.name!); expect(resultElement).toBeInTheDocument(); @@ -50,7 +50,7 @@ describe('Results Component', () => { }); it('renders list view', () => { - const rendered = render(); + const rendered = render(); const resultElement = rendered.getByText(mockResults[0].mappings.core?.name!); expect(resultElement).toBeInTheDocument(); @@ -64,7 +64,7 @@ describe('Results Component', () => { }); it('renders all', () => { - const rendered = render(); + const rendered = render(); const results = rendered.container.querySelectorAll('.ss__results__result'); expect(results.length).toBe(mockResults.length); }); @@ -75,7 +75,7 @@ describe('Results Component', () => { columns: 3, }; - const rendered = render(); + const rendered = render(); const results = rendered.container.querySelectorAll('.ss__result'); expect(results.length).toBe(args.columns * args.rows); }); @@ -86,7 +86,7 @@ describe('Results Component', () => { gapSize: '40px', }; - const rendered = render(); + const rendered = render(); const resultsElement = rendered.container.querySelector('.ss__results')!; const resultsElementStyles = getComputedStyle(resultsElement); @@ -103,10 +103,10 @@ describe('Results Component', () => { it('can use breakpoints', async () => { const customBreakpoints = { 0: { - layout: Layout.GRID, + layout: Layout.grid, }, 700: { - layout: Layout.LIST, + layout: Layout.list, }, }; @@ -162,7 +162,7 @@ describe('Results Component', () => { }; const args = { - layout: Layout.GRID, + layout: Layout.grid, results: mockResults, }; const rendered = render( @@ -180,7 +180,7 @@ describe('Results Component', () => { it('is themeable with ThemeProvider', () => { const args = { - layout: Layout.GRID, + layout: Layout.grid, results: mockResults, }; const rendered = render( @@ -195,7 +195,7 @@ describe('Results Component', () => { it('is themeable with theme prop', () => { const args = { - layout: Layout.GRID, + layout: Layout.grid, results: mockResults, }; const rendered = render(); @@ -206,7 +206,7 @@ describe('Results Component', () => { it('is themeable with theme prop overrides ThemeProvider', () => { const args = { - layout: Layout.GRID, + layout: Layout.grid, results: mockResults, }; @@ -297,7 +297,7 @@ describe('Results Component', () => { }; const args = { - layout: Layout.GRID, + layout: Layout.grid, results: mockResults, breakpoints: customBreakpoints, theme: componentTheme, diff --git a/packages/snap-preact/components/src/components/Organisms/Results/Results.tsx b/packages/snap-preact/components/src/components/Organisms/Results/Results.tsx index c2196170e..430c8390e 100644 --- a/packages/snap-preact/components/src/components/Organisms/Results/Results.tsx +++ b/packages/snap-preact/components/src/components/Organisms/Results/Results.tsx @@ -10,7 +10,7 @@ import type { SearchResultStore, Product, Banner } from '@searchspring/snap-stor import { ContentType } from '@searchspring/snap-store-mobx'; import { InlineBanner, InlineBannerProps } from '../../Atoms/Merchandising/InlineBanner'; import { Result, ResultProps } from '../../Molecules/Result'; -import { ComponentProps, ResultsLayout, ResultsLayoutType, BreakpointsProps, RootNodeProperties, ResultComponent } from '../../../types'; +import { ComponentProps, ResultsLayout, BreakpointsProps, RootNodeProperties, ResultComponent } from '../../../types'; import { defined, mergeProps } from '../../../utilities'; import { Theme, useTheme, CacheProvider } from '../../../providers'; import { useDisplaySettings } from '../../../hooks/useDisplaySettings'; @@ -73,7 +73,7 @@ export const Results = observer((properties: ResultsProps): JSX.Element => { results: properties.controller?.store?.results, columns: 4, gapSize: '20px', - layout: ResultsLayout.GRID, + layout: ResultsLayout.grid, breakpoints: defaultBreakpointsProps, }; @@ -127,7 +127,7 @@ export const Results = observer((properties: ResultsProps): JSX.Element => { } const styling: RootNodeProperties = { 'ss-name': props.name }; - const stylingProps = { ...props, columns: layout == ResultsLayout.LIST ? 1 : props.columns, gapSize: props.gapSize, theme }; + const stylingProps = { ...props, columns: layout == ResultsLayout.list ? 1 : props.columns, gapSize: props.gapSize, theme }; if (styleScript && !disableStyles) { styling.css = [styleScript(stylingProps), style]; @@ -178,7 +178,7 @@ export interface ResultsProps extends ComponentProps { columns?: number; rows?: number; gapSize?: string; - layout?: ResultsLayoutType; + layout?: keyof typeof ResultsLayout | ResultsLayout; breakpoints?: BreakpointsProps; controller?: SearchController | AutocompleteController | RecommendationController; resultComponent?: ResultComponent; diff --git a/packages/snap-preact/components/src/components/Templates/Autocomplete/Autocomplete.tsx b/packages/snap-preact/components/src/components/Templates/Autocomplete/Autocomplete.tsx index d0e3183a0..fdb6d66a0 100644 --- a/packages/snap-preact/components/src/components/Templates/Autocomplete/Autocomplete.tsx +++ b/packages/snap-preact/components/src/components/Templates/Autocomplete/Autocomplete.tsx @@ -120,7 +120,7 @@ const CSS = { '& a': { fontWeight: 'bold', - color: theme?.variables?.color?.primary, + color: theme?.variables?.colors?.primary, }, }, }, @@ -174,7 +174,7 @@ const CSS = { '& a': { fontWeight: 'bold', - color: theme?.variables?.color?.primary, + color: theme?.variables?.colors?.primary, '& .ss__icon': { marginLeft: '5px', diff --git a/packages/snap-preact/components/src/components/Templates/HorizontalSearch/readme.md b/packages/snap-preact/components/src/components/Templates/HorizontalSearch/readme.md index 7b98f131a..e51aaa896 100644 --- a/packages/snap-preact/components/src/components/Templates/HorizontalSearch/readme.md +++ b/packages/snap-preact/components/src/components/Templates/HorizontalSearch/readme.md @@ -112,7 +112,7 @@ const layoutConfig = { icon: { icon: 'layout-list', }, - component: (props) => , + component: (props) => , columns:1, } }, diff --git a/packages/snap-preact/components/src/components/Templates/Search/readme.md b/packages/snap-preact/components/src/components/Templates/Search/readme.md index fca7a62dc..b622d9bef 100644 --- a/packages/snap-preact/components/src/components/Templates/Search/readme.md +++ b/packages/snap-preact/components/src/components/Templates/Search/readme.md @@ -134,7 +134,7 @@ const layoutConfig = { icon: { icon: 'layout-list', }, - component: (props) => , + component: (props) => , columns:1, } }, diff --git a/packages/snap-preact/components/src/providers/theme.ts b/packages/snap-preact/components/src/providers/theme.ts index c2b5869fe..486f67ee7 100644 --- a/packages/snap-preact/components/src/providers/theme.ts +++ b/packages/snap-preact/components/src/providers/theme.ts @@ -1,12 +1,13 @@ import { ThemeComponents } from './themeComponents'; import { ListOption } from '../types'; +import type { DeepPartial } from '../../../src/types'; export { css, useTheme, withTheme, ThemeProvider } from '@emotion/react'; export const defaultTheme: Theme = { variables: { breakpoints: [0, 540, 767, 1200], - color: { + colors: { primary: '#3A23AD', secondary: '#00cee1', accent: '#4c3ce2', @@ -22,32 +23,44 @@ export const defaultTheme: Theme = { }, }, }, - components: {}, +}; + +type ThemeVariableBreakpoints = [number, number, number, number]; +type ThemeVaraibleColors = { + primary: string; // (search header text, regular text, result title) + secondary: string; // (headings, dropdown button text) + accent: string; // (icons, borders) + active: { + foreground: string; // (active state text) + background: string; // (active state) + accent: string; // (icons, borders) + }; + hover: { + foreground: string; // (active state text) + background: string; // (active state) + accent: string; // (icons, borders) + }; +}; + +export type ThemeVariables = { + breakpoints: ThemeVariableBreakpoints; + colors: ThemeVaraibleColors; +}; + +export type ThemeVariablesPartial = { + breakpoints?: ThemeVariableBreakpoints; + colors?: DeepPartial; }; export type Theme = { name?: string; // Used as a flag in components to provide backwards compatability variables?: ThemeVariables; - responsive?: [Theme, Theme, Theme, Theme]; + responsive?: [ThemeResponsive, ThemeResponsive, ThemeResponsive, ThemeResponsive]; components?: ThemeComponents; layoutOptions?: ListOption[]; }; -export type ThemeVariables = { - breakpoints: [number, number, number, number]; - color: { - primary: string; // (search header text, regular text, result title) - secondary: string; // (headings, dropdown button text) - accent: string; // (icons, borders) - active: { - foreground: string; // (active state text) - background: string; // (active state) - accent: string; // (icons, borders) - }; - hover: { - foreground: string; // (active state text) - background: string; // (active state) - accent: string; // (icons, borders) - }; - }; -}; +type ThemeResponsive = Pick; +export type ThemePartial = Omit & { variables?: ThemeVariablesPartial }; +export type ThemeOverrides = Pick; +export type ThemeMinimal = Pick; diff --git a/packages/snap-preact/components/src/themes/bocachica/index.ts b/packages/snap-preact/components/src/themes/bocachica/index.ts index 2e9c5b15f..c7617ea51 100644 --- a/packages/snap-preact/components/src/themes/bocachica/index.ts +++ b/packages/snap-preact/components/src/themes/bocachica/index.ts @@ -3,7 +3,7 @@ import * as style from './styles/styles'; const bocachicaVariables: ThemeVariables = { breakpoints: [0, 767, 999, 1299], - color: { + colors: { primary: '#202223', secondary: '#6d7175', accent: '#6d7175', diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/button.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/button.ts index 3ef8fb08e..3c4091e50 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/button.ts @@ -9,16 +9,16 @@ const buttonStyleScript = ({ color, backgroundColor, borderColor, theme }: Butto display: 'inline-flex', padding: '5px 10px', position: 'relative', - color: color || variables?.color?.secondary, + color: color || variables?.colors?.secondary, outline: 0, backgroundColor: backgroundColor, - border: `1px solid ${borderColor || variables?.color?.accent || '#333'}`, + border: `1px solid ${borderColor || variables?.colors?.accent || '#333'}`, borderRadius: '3px', '&:hover': { cursor: 'pointer', - backgroundColor: variables?.color?.hover?.background, - color: variables?.color?.hover?.foreground, - borderColor: borderColor || variables?.color?.hover?.accent, + backgroundColor: variables?.colors?.hover?.background, + color: variables?.colors?.hover?.foreground, + borderColor: borderColor || variables?.colors?.hover?.accent, }, '&.ss__button--disabled': { opacity: 0.7, diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/icon.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/icon.ts index 79e8ee2d9..1b371f712 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/icon.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/icon.ts @@ -6,8 +6,8 @@ const iconStyleScript = ({ color, height, width, size, theme }: IconProps) => { const variables = theme?.variables; return css({ - fill: color || variables?.color?.accent, - stroke: color || variables?.color?.accent, + fill: color || variables?.colors?.accent, + stroke: color || variables?.colors?.accent, width: width || size, height: height || size, position: 'relative', diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/loadingbar.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/loadingbar.ts index 7554c45d9..11a508d74 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/loadingbar.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/loadingbar.ts @@ -30,7 +30,7 @@ const loadingBarStyleScript = ({ color, height, backgroundColor, theme }: Loadin top: '0', left: '-200px', height: '100%', - background: `${color || variables?.color?.accent || '#ccc'}`, + background: `${color || variables?.colors?.accent || '#ccc'}`, animation: `${animation} 2s linear infinite`, }, }); diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/noresults.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/noresults.ts index 43b347dc7..d16641304 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/noresults.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/noresults.ts @@ -6,9 +6,9 @@ const noResultsStyleScript = ({ theme }: NoResultsProps) => { const variables = theme?.variables; return css({ - color: variables?.color?.secondary, + color: variables?.colors?.secondary, ' .ss__title': { - color: variables?.color?.secondary, + color: variables?.colors?.secondary, }, }); }; diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/price.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/price.ts index 5e8c33a2f..db30bce07 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/price.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/price.ts @@ -6,12 +6,12 @@ const priceStyleScript = ({ theme }: PriceProps) => { const variables = theme?.variables; return css({ - color: variables?.color?.secondary, + color: variables?.colors?.secondary, margin: '0 0.5rem 0 0', '&.ss__price--strike': { textDecoration: 'line-through', - color: variables?.color?.secondary || 'inherit', + color: variables?.colors?.secondary || 'inherit', opacity: 0.5, }, }); diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/searchheader.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/searchheader.ts index 7bd5cdab5..a388b91b9 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/searchheader.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/atoms/searchheader.ts @@ -8,7 +8,7 @@ const searchHeaderStyleScript = ({ theme }: SearchHeaderProps) => { return css({ textAlign: 'center', ' .ss__search-header__title': { - color: variables?.color?.primary, + color: variables?.colors?.primary, }, }); }; diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/carousel.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/carousel.ts index 6b10c7ac6..f768088d4 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/carousel.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/carousel.ts @@ -2,7 +2,7 @@ import { css } from '@emotion/react'; import type { CarouselProps } from '../../../../../components/Molecules/Carousel'; // CSS in JS style script for the Carousel component -const carouselStyleScript = ({ vertical, theme }: any) => { +const carouselStyleScript = ({ vertical, theme }: CarouselProps) => { const variables = theme?.variables; return css({ @@ -44,7 +44,7 @@ const carouselStyleScript = ({ vertical, theme }: any) => { }, }, '.swiper-pagination-bullet-active': { - background: variables?.color?.accent || 'inherit', + background: variables?.colors?.accent || 'inherit', }, '.ss__carousel__next-wrapper, .ss__carousel__prev-wrapper': { display: 'flex', @@ -110,13 +110,13 @@ const carouselStyleScript = ({ vertical, theme }: any) => { height: '8px', display: 'inline-block', borderRadius: '50%', - background: variables?.color?.accent || '#000', + background: variables?.colors?.accent || '#000', opacity: '.5', cursor: 'pointer', margin: '0 4px', '&.swiper-pagination-bullet-active': { opacity: '1', - background: variables?.color?.active?.accent || '#000', + background: variables?.colors?.active?.accent || '#000', }, }, '.swiper-container-pointer-events': { diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/checkbox.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/checkbox.ts index cb3ec7f56..eb0fd9ee6 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/checkbox.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/checkbox.ts @@ -11,7 +11,7 @@ const checkboxStyleScript = ({ size, color, theme }: CheckboxProps) => { justifyContent: 'center', height: size, width: size, - border: `1px solid ${color || variables?.color?.secondary || '#333'}`, + border: `1px solid ${color || variables?.colors?.secondary || '#333'}`, borderRadius: '3px', '&.ss__checkbox--disabled': { opacity: 0.7, diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetgridoptions.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetgridoptions.ts index 1fd176452..1ee50e495 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetgridoptions.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetgridoptions.ts @@ -9,7 +9,7 @@ const facetGridOptionsStyleScript = ({ columns, gapSize, gridSize, theme }: Face display: 'flex', flexFlow: 'row wrap', gridTemplateColumns: columns ? `repeat(${columns}, 1fr)` : `repeat(auto-fill, minmax(${gridSize}, 1fr))`, - color: variables?.color?.secondary, + color: variables?.colors?.secondary, gap: gapSize, gridAutoRows: `1fr`, @@ -18,7 +18,7 @@ const facetGridOptionsStyleScript = ({ columns, gapSize, gridSize, theme }: Face justifyContent: 'center', alignItems: 'center', flex: '0 1 auto', - border: `1px solid ${variables?.color?.secondary || '#333'}`, + border: `1px solid ${variables?.colors?.secondary || '#333'}`, borderRadius: '3px', textAlign: 'center', wordBreak: 'break-all', @@ -26,14 +26,14 @@ const facetGridOptionsStyleScript = ({ columns, gapSize, gridSize, theme }: Face padding: '1em 0', width: `calc(100% / ${columns} - ${2 * Math.round((columns! + 2) / 2)}px)`, margin: `0 ${gapSize} ${gapSize} 0`, - color: variables?.color?.secondary, + color: variables?.colors?.secondary, [`:nth-of-type(${columns}n)`]: { marginRight: '0', }, '&.ss__facet-grid-options__option--filtered': { - background: variables?.color?.active?.background || '#ccc', - color: variables?.color?.active?.foreground, + background: variables?.colors?.active?.background || '#ccc', + color: variables?.colors?.active?.foreground, }, '&:hover:not(.ss__facet-grid-options__option--filtered)': { cursor: 'pointer', diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facethierarchyoptions.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facethierarchyoptions.ts index aa180e041..31074be1c 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facethierarchyoptions.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facethierarchyoptions.ts @@ -11,16 +11,16 @@ const facetHierarchyOptionsStyleScript = ({ horizontal, theme }: FacetHierarchyO flexWrap: 'wrap', '& .ss__facet-hierarchy-options__option': { margin: '0 5px 5px 0', - color: variables?.color?.secondary, + color: variables?.colors?.secondary, flex: '0 1 auto', - border: `1px solid ${variables?.color?.secondary || '#333'}`, + border: `1px solid ${variables?.colors?.secondary || '#333'}`, padding: '0.5em 0.5em', textDecoration: 'none', '&.ss__facet-hierarchy-options__option--filtered': { fontWeight: 'bold', marginRight: '2em', - backgroundColor: variables?.color?.active?.background, - color: variables?.color?.active?.foreground, + backgroundColor: variables?.colors?.active?.background, + color: variables?.colors?.active?.foreground, '&:hover': { cursor: 'default', }, @@ -29,7 +29,7 @@ const facetHierarchyOptionsStyleScript = ({ horizontal, theme }: FacetHierarchyO '&:before': { content: `'\\0000ab'`, padding: '0 2px 0 0', - color: variables?.color?.accent, + color: variables?.colors?.accent, }, }, '& .ss__facet-hierarchy-options__option__value': { @@ -48,7 +48,7 @@ const facetHierarchyOptionsStyleScript = ({ horizontal, theme }: FacetHierarchyO padding: '6px 0', textDecoration: 'none', alignItems: 'center', - color: variables?.color?.secondary, + color: variables?.colors?.secondary, '&:hover': { cursor: 'pointer', @@ -66,7 +66,7 @@ const facetHierarchyOptionsStyleScript = ({ horizontal, theme }: FacetHierarchyO '&:before': { content: `'\\0000ab'`, padding: '0 2px 0 0', - color: variables?.color?.accent, + color: variables?.colors?.accent, }, }, '& .ss__facet-hierarchy-options__option__value': { diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetlistoptions.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetlistoptions.ts index 058cfd453..b37db560a 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetlistoptions.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetlistoptions.ts @@ -13,9 +13,9 @@ const facetListOptionsStyleScript = ({ hideCheckbox, horizontal, theme }: FacetL display: horizontal ? undefined : 'flex', alignItems: horizontal ? undefined : 'center', margin: horizontal ? '0 5px 5px 0' : '0 0 5px 0', - color: variables?.color?.secondary, + color: variables?.colors?.secondary, flex: horizontal ? '0 1 auto' : undefined, - border: horizontal ? `1px solid ${variables?.color?.secondary || '#333'}` : undefined, + border: horizontal ? `1px solid ${variables?.colors?.secondary || '#333'}` : undefined, padding: horizontal ? '0.5em 0.5em' : undefined, textDecoration: 'none', diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetpaletteoptions.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetpaletteoptions.ts index bd484a869..27717cdf7 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetpaletteoptions.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetpaletteoptions.ts @@ -15,7 +15,7 @@ const facetPaletteStyleScript = ({ columns, gapSize, theme }: FacetPaletteOption width: `calc(100% / ${columns} - ${2 * Math.round((columns! + 2) / 2)}px )`, marginRight: gapSize, marginBottom: gapSize, - color: variables?.color?.secondary, + color: variables?.colors?.secondary, [`:nth-of-type(${columns}n)`]: { marginRight: '0', @@ -38,7 +38,7 @@ const facetPaletteStyleScript = ({ columns, gapSize, theme }: FacetPaletteOption }, '&.ss__facet-palette-options__option--filtered': { '& .ss__facet-palette-options__option__wrapper': { - borderColor: variables?.color?.secondary || '#333', + borderColor: variables?.colors?.secondary || '#333', padding: '2px', borderWidth: '2px', }, diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetslider.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetslider.ts index bcb6ae683..95097488f 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetslider.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/facetslider.ts @@ -20,7 +20,7 @@ const facetSliderStyleScript = ({ flexDirection: 'column', marginTop: '5px', marginBottom: showTicks && stickyHandleLabel ? '22px' : showTicks || stickyHandleLabel ? '10px' : '5px', - color: variables?.color?.secondary, + color: variables?.colors?.secondary, '& .ss__facet-slider__slider': { position: 'relative', @@ -51,7 +51,7 @@ const facetSliderStyleScript = ({ }, }, '& .ss__facet-slider__rail': { - background: railColor || variables?.color?.secondary || '#333', + background: railColor || variables?.colors?.secondary || '#333', height: '100%', }, '& .ss__facet-slider__segment': { @@ -62,7 +62,7 @@ const facetSliderStyleScript = ({ textAlign: 'center', '& button': { '& .ss__facet-slider__handle': { - background: handleColor || variables?.color?.secondary || '#333', + background: handleColor || variables?.colors?.secondary || '#333', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -71,7 +71,7 @@ const facetSliderStyleScript = ({ borderRadius: '100%', fontSize: '0.7rem', whiteSpace: 'nowrap', - color: valueTextColor || variables?.color?.secondary || 'initial', + color: valueTextColor || variables?.colors?.secondary || 'initial', fontWeight: 'normal', transform: 'translateY(0) scale(0.9)', transition: 'all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)', @@ -79,7 +79,7 @@ const facetSliderStyleScript = ({ cursor: 'pointer', '&:after': { - backgroundColor: variables?.color?.accent || '#ffffff', + backgroundColor: variables?.colors?.accent || '#ffffff', width: '30%', height: '30%', top: '0', @@ -94,13 +94,13 @@ const facetSliderStyleScript = ({ }, '&.ss__facet-slider__handle--active': { - background: handleDraggingColor || handleColor || variables?.color?.active?.background || '#000', + background: handleDraggingColor || handleColor || variables?.colors?.active?.background || '#000', '& label.ss__facet-slider__handle__label': { background: '#fff', padding: '0 5px', }, '&:after': { - backgroundColor: variables?.color?.active?.foreground || '#ffffff', + backgroundColor: variables?.colors?.active?.foreground || '#ffffff', }, }, @@ -129,7 +129,7 @@ const facetSliderStyleScript = ({ '& .ss__facet-slider__labels': { textAlign: 'center', marginTop: showTicks && !stickyHandleLabel ? '40px' : '20px', - color: variables?.color?.secondary || valueTextColor, + color: variables?.colors?.secondary || valueTextColor, '& .ss__facet-slider__label--0': { '&:after': { diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/loadmore.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/loadmore.ts index cd575fe7e..e77ac2550 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/loadmore.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/loadmore.ts @@ -47,11 +47,11 @@ const loadMoreStyleScript = ({ gap: '5px', '& .ss__load-more__progress__indicator': { width: progressIndicatorWidth, - background: backgroundColor || variables?.color?.secondary || '#f8f8f8', + background: backgroundColor || variables?.colors?.secondary || '#f8f8f8', borderRadius: progressIndicatorSize, '& .ss__load-more__progress__indicator__bar': { width: pagination ? `${(pagination.end / pagination.totalResults) * 100}%` : '', - background: color || variables?.color?.primary || '#ccc', + background: color || variables?.colors?.primary || '#ccc', borderRadius: progressIndicatorSize, height: progressIndicatorSize, }, @@ -74,7 +74,7 @@ const loadMoreStyleScript = ({ '& .ss__load-more__progress__indicator': { '& .ss__load-more__progress__indicator__radial': { - background: backgroundColor || variables?.color?.secondary || '#f8f8f8', + background: backgroundColor || variables?.colors?.secondary || '#f8f8f8', height: progressIndicatorWidth, width: progressIndicatorWidth, borderRadius: '50%', @@ -108,7 +108,7 @@ const loadMoreStyleScript = ({ '& .ss__load-more__progress__indicator__radial__mask': { '& .ss__load-more__progress__indicator__radial__mask__fill': { clipPath: `inset(0px calc((${progressIndicatorWidth} / 2)) 0px 0px)`, - backgroundColor: color || variables?.color?.primary || '#ccc', + backgroundColor: color || variables?.colors?.primary || '#ccc', }, '&.ss__load-more__progress__indicator__radial__mask--full': { animation: `${radialAnimation} ease-in-out 1s`, diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/pagination.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/pagination.ts index 69f404fa3..50833ba7b 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/pagination.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/pagination.ts @@ -12,7 +12,7 @@ const paginationStyleScript = ({ theme }: PaginationProps) => { minHeight: '1em', minWidth: '1em', textAlign: 'center', - color: variables?.color?.secondary, + color: variables?.colors?.secondary, '&.ss__pagination__page--active': { textDecoration: 'underline', diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/searchinput.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/searchinput.ts index 767f3a236..2b37dde57 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/searchinput.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/searchinput.ts @@ -9,7 +9,7 @@ const searchInputStyleScript = ({ theme }: SearchInputProps) => { display: 'flex', alignItems: 'center', justifyContent: 'center', - border: `1px solid ${variables?.color?.secondary || '#ccc'}`, + border: `1px solid ${variables?.colors?.secondary || '#ccc'}`, '& .ss__icon': { padding: '5px', diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/select.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/select.ts index ad6f192ef..cabff505d 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/select.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/molecules/select.ts @@ -24,7 +24,7 @@ const selectStyleScript = ({ color, backgroundColor, borderColor, theme }: Selec fontWeight: 'bold', '&:hover': { backgroundColor: 'initial', - color: variables?.color?.secondary || 'unset', + color: variables?.colors?.secondary || 'unset', }, '& .ss__select__dropdown__button__icon': { transition: 'transform 0.25s ease 0s', @@ -54,7 +54,7 @@ const selectStyleScript = ({ color, backgroundColor, borderColor, theme }: Selec listStyle: 'none', cursor: 'pointer', padding: '6px 30px', - color: variables?.color?.secondary, + color: variables?.colors?.secondary, '&.ss__select__select__option--selected': { background: `rgba(109,113,117,.06)`, }, diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/facet.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/facet.ts index 197962cd1..d34407ecc 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/facet.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/facet.ts @@ -22,7 +22,7 @@ const facetStyleScript = ({ color, theme }: FacetProps) => { display: 'flex', justifyContent: 'space-between', alignItems: 'center', - color: color || variables?.color?.secondary, + color: color || variables?.colors?.secondary, fontWeight: 'bold', }, '& .ss__facet__options': { @@ -38,14 +38,14 @@ const facetStyleScript = ({ color, theme }: FacetProps) => { background: '#f2f2f2', }, '&::-webkit-scrollbar-thumb': { - background: variables?.color?.secondary || '#ccc', + background: variables?.colors?.secondary || '#ccc', }, }, '& .ss__facet__show-more-less': { display: 'block', margin: '8px', cursor: 'pointer', - color: color || variables?.color?.secondary, + color: color || variables?.colors?.secondary, '& .ss__icon': { marginRight: '8px', }, diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/filtersummary.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/filtersummary.ts index 3fe034e80..867235fac 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/filtersummary.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/filtersummary.ts @@ -10,19 +10,19 @@ const filterSummaryStyleScript = ({ theme }: FilterSummaryProps) => { '& .ss__filter-summary__filter': { margin: '5px 10px 5px 0', '& .ss__filter__button': { - backgroundColor: variables?.color?.active?.background || '#ccc', - color: variables?.color?.active?.foreground, + backgroundColor: variables?.colors?.active?.background || '#ccc', + color: variables?.colors?.active?.foreground, border: 'none', '& .ss__filter__button__icon': { - fill: variables?.color?.active?.accent, + fill: variables?.colors?.active?.accent, }, }, }, '& .ss__filter-summary__title': { fontSize: '1.2em', fontWeight: 'bold', - color: variables?.color?.secondary, + color: variables?.colors?.secondary, }, }); }; diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/horizontalfacets.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/horizontalfacets.ts index 63599f062..7bcedf872 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/horizontalfacets.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/horizontalfacets.ts @@ -34,11 +34,11 @@ const horizontalFacetsStyleScript = ({ theme }: HorizontalFacetsProps) => { '&.ss__dropdown--open': { '& .ss__dropdown__button__heading': { - backgroundColor: variables?.color?.active?.background, - color: variables?.color?.active?.foreground, + backgroundColor: variables?.colors?.active?.background, + color: variables?.colors?.active?.foreground, '& .ss__icon': { - fill: variables?.color?.active?.accent, + fill: variables?.colors?.active?.accent, transform: 'rotate(180deg)', }, }, @@ -57,7 +57,7 @@ const horizontalFacetsStyleScript = ({ theme }: HorizontalFacetsProps) => { '& .ss__horizontal-facets__header__dropdown': { '&.ss__dropdown--open': { '& .ss__dropdown__content': { - border: `1px solid ${variables?.color?.active?.background || '#ccc'}`, + border: `1px solid ${variables?.colors?.active?.background || '#ccc'}`, }, }, }, diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/sidebar.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/sidebar.ts index 5a0626353..9b32645ae 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/sidebar.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/organisms/sidebar.ts @@ -11,7 +11,7 @@ const sidebarStyleScript = ({ theme }: SidebarProps) => { paddingInlineEnd: '60px', '& .ss__sidebar__title': { - color: variables?.color?.primary, + color: variables?.colors?.primary, }, }); }; diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/templates/autocomplete.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/templates/autocomplete.ts index 4c24c084c..da24e03af 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/templates/autocomplete.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/templates/autocomplete.ts @@ -40,7 +40,7 @@ const autocompleteStyleScript = ({ '.ss__autocomplete__title--trending, .ss__autocomplete__title--history, .ss__autocomplete__title--terms': { fontWeight: 'normal', margin: 0, - color: variables?.color?.secondary || '#c5c5c5', + color: variables?.colors?.secondary || '#c5c5c5', textTransform: 'uppercase', padding: '10px', '& h5': { @@ -61,7 +61,7 @@ const autocompleteStyleScript = ({ minWidth: '150px', order: 1, background: '#fff', - borderBottom: vertical ? `1px solid ${variables?.color?.primary || '#333'}` : undefined, + borderBottom: vertical ? `1px solid ${variables?.colors?.primary || '#333'}` : undefined, '& .ss__autocomplete__terms__options': { display: vertical || horizontalTerms ? 'flex' : undefined, @@ -74,7 +74,7 @@ const autocompleteStyleScript = ({ '& a': { display: 'block', padding: '10px', - color: variables?.color?.primary, + color: variables?.colors?.primary, '& em': { fontStyle: 'normal', @@ -98,7 +98,7 @@ const autocompleteStyleScript = ({ order: 2, padding: vertical ? '10px 20px' : '10px', overflowY: vertical ? undefined : 'auto', - borderBottom: vertical ? `1px solid ${variables?.color?.primary || '#333'}` : undefined, + borderBottom: vertical ? `1px solid ${variables?.colors?.primary || '#333'}` : undefined, '& .ss__facet-palette-options, .ss__facet-grid-options': { gridTemplateColumns: 'repeat(auto-fill, minmax(36px, 1fr))', @@ -109,7 +109,7 @@ const autocompleteStyleScript = ({ }, '& .ss__facet__header': { - color: variables?.color?.primary, + color: variables?.colors?.primary, textTransform: 'uppercase', }, @@ -160,7 +160,7 @@ const autocompleteStyleScript = ({ '& a': { fontWeight: 'bold', textTransform: 'uppercase', - color: variables?.color?.primary, + color: variables?.colors?.primary, '& .ss__icon': { marginLeft: '5px', diff --git a/packages/snap-preact/components/src/themes/bocachica/styles/components/templates/recommendation.ts b/packages/snap-preact/components/src/themes/bocachica/styles/components/templates/recommendation.ts index b8b97325b..198084468 100644 --- a/packages/snap-preact/components/src/themes/bocachica/styles/components/templates/recommendation.ts +++ b/packages/snap-preact/components/src/themes/bocachica/styles/components/templates/recommendation.ts @@ -9,7 +9,7 @@ const recommendationStyleScript = ({ vertical, theme }: RecommendationProps) => return css({ height: vertical ? '100%' : undefined, '& .ss__recommendation__title': { - color: variables?.color?.primary, + color: variables?.colors?.primary, }, '.ss__result__image-wrapper': { height: vertical ? '85%' : undefined, diff --git a/packages/snap-preact/components/src/themes/pike/index.ts b/packages/snap-preact/components/src/themes/pike/index.ts index 9d73749d2..e8cc31d4c 100644 --- a/packages/snap-preact/components/src/themes/pike/index.ts +++ b/packages/snap-preact/components/src/themes/pike/index.ts @@ -3,7 +3,7 @@ import * as style from './styles/styles'; const pikeVariables: ThemeVariables = { breakpoints: [0, 540, 767, 1200], - color: { + colors: { primary: 'blue', secondary: 'blue', accent: 'blue', diff --git a/packages/snap-preact/components/src/themes/pike/styles/components/atoms/button.ts b/packages/snap-preact/components/src/themes/pike/styles/components/atoms/button.ts index 4492c2ba2..63b8fcc0b 100644 --- a/packages/snap-preact/components/src/themes/pike/styles/components/atoms/button.ts +++ b/packages/snap-preact/components/src/themes/pike/styles/components/atoms/button.ts @@ -7,14 +7,14 @@ const buttonStyleScript = ({ color, backgroundColor, borderColor, theme }: Butto display: 'inline-flex', padding: '5px 10px', position: 'relative', - color: color || theme?.variables?.color?.primary, + color: color || theme?.variables?.colors?.primary, outline: 0, backgroundColor: backgroundColor || '#fff', - border: `1px solid ${borderColor || color || theme?.variables?.color?.primary || '#333'}`, + border: `1px solid ${borderColor || color || theme?.variables?.colors?.primary || '#333'}`, borderRadius: '3px', '&:hover': { cursor: 'pointer', - backgroundColor: theme?.variables?.color?.hover?.background || '#f8f8f8', + backgroundColor: theme?.variables?.colors?.hover?.background || '#f8f8f8', }, '&.ss__button--disabled': { opacity: 0.7, diff --git a/packages/snap-preact/components/src/types.ts b/packages/snap-preact/components/src/types.ts index da1126200..251bc24de 100644 --- a/packages/snap-preact/components/src/types.ts +++ b/packages/snap-preact/components/src/types.ts @@ -1,12 +1,11 @@ import type { SerializedStyles } from '@emotion/react'; -import type { Theme } from './providers/theme'; +import type { ThemeMinimal, Theme } from './providers/theme'; import type { AbstractController } from '@searchspring/snap-controller'; import type { UrlManager } from '@searchspring/snap-url-manager'; import type { Product } from '@searchspring/snap-store-mobx'; import { IconProps, IconType } from './components/Atoms/Icon'; import { MutableRef } from 'preact/hooks'; import type { Snap, SnapTemplates } from '../../src'; -import type { DeepPartial } from '../../src/types'; import type { FunctionalComponent, RenderableProps } from 'preact'; import type { CSSInterpolation } from '@emotion/serialize'; @@ -29,7 +28,7 @@ export type ListOption = { disabled?: boolean; default?: boolean; icon?: IconType | Partial; - overrides?: DeepPartial; + overrides?: ThemeMinimal; url?: UrlManager; available?: boolean; }; @@ -43,12 +42,10 @@ export type ResultComponent = object> = React. >; export enum ResultsLayout { - GRID = 'grid', - LIST = 'list', + grid = 'grid', + list = 'list', } -export type ResultsLayoutType = ResultsLayout.GRID | ResultsLayout.LIST; - // TODO: move to store or use store or snapi types export enum FacetType { VALUE = 'value', diff --git a/packages/snap-preact/components/src/utilities/mergeProps.test.ts b/packages/snap-preact/components/src/utilities/mergeProps.test.ts index 93ac8c9f2..397689667 100644 --- a/packages/snap-preact/components/src/utilities/mergeProps.test.ts +++ b/packages/snap-preact/components/src/utilities/mergeProps.test.ts @@ -139,7 +139,7 @@ describe('mergeProps function with theme name', () => { name: 'global', variables: { breakpoints: [0, 540, 767, 1200], - color: { + colors: { primary: '#3A23AD', secondary: '#00cee1', accent: '#4c3ce2', diff --git a/packages/snap-preact/src/Templates/Stores/LibraryStore.test.ts b/packages/snap-preact/src/Templates/Stores/LibraryStore.test.ts index 57f3ea0af..895b4921b 100644 --- a/packages/snap-preact/src/Templates/Stores/LibraryStore.test.ts +++ b/packages/snap-preact/src/Templates/Stores/LibraryStore.test.ts @@ -62,6 +62,7 @@ describe('LibraryStore', () => { for (let index = 0; index < themes.length; index++) { const theme = themes[index]; expect(store.import.theme[theme]).toBeDefined(); + expect(store.themes[theme]).not.toBeDefined(); await store.import.theme[theme](); expect(store.themes[theme]).toBeDefined(); } @@ -70,6 +71,7 @@ describe('LibraryStore', () => { for (let index = 0; index < autocompleteComponents.length; index++) { const componentName = autocompleteComponents[index]; expect(store.import.component.autocomplete[componentName]).toBeDefined(); + expect(store.components.autocomplete[componentName]).not.toBeDefined(); await store.import.component.autocomplete[componentName](); expect(store.components.autocomplete[componentName]).toBeDefined(); } @@ -78,6 +80,7 @@ describe('LibraryStore', () => { for (let index = 0; index < searchComponents.length; index++) { const componentName = searchComponents[index]; expect(store.import.component.search[componentName]).toBeDefined(); + expect(store.components.search[componentName]).not.toBeDefined(); await store.import.component.search[componentName](); expect(store.components.search[componentName]).toBeDefined(); } @@ -95,6 +98,7 @@ describe('LibraryStore', () => { for (let index = 0; index < badgeComponents.length; index++) { const componentName = badgeComponents[index]; expect(store.import.component.badge[componentName]).toBeDefined(); + expect(store.components.badge[componentName]).not.toBeDefined(); await store.import.component.badge[componentName](); expect(store.components.badge[componentName]).toBeDefined(); } @@ -103,6 +107,7 @@ describe('LibraryStore', () => { for (let index = 0; index < resultComponents.length; index++) { const componentName = resultComponents[index]; expect(store.import.component.result[componentName]).toBeDefined(); + expect(store.components.result[componentName]).not.toBeDefined(); await store.import.component.result[componentName](); expect(store.components.result[componentName]).toBeDefined(); } @@ -111,6 +116,7 @@ describe('LibraryStore', () => { for (let index = 0; index < languages.length; index++) { const language = languages[index]; expect(store.import.language[language]).toBeDefined(); + expect(store.locales.languages[language]).not.toBeDefined(); await store.import.language[language](); expect(store.locales.languages[language]).toBeDefined(); } @@ -119,6 +125,7 @@ describe('LibraryStore', () => { for (let index = 0; index < currencies.length; index++) { const currency = currencies[index]; expect(store.import.currency[currency]).toBeDefined(); + expect(store.locales.currencies[currency]).not.toBeDefined(); await store.import.currency[currency](); expect(store.locales.currencies[currency]).toBeDefined(); } diff --git a/packages/snap-preact/src/Templates/Stores/TargetStore.test.ts b/packages/snap-preact/src/Templates/Stores/TargetStore.test.ts index 01ebe75ed..bb880cd2a 100644 --- a/packages/snap-preact/src/Templates/Stores/TargetStore.test.ts +++ b/packages/snap-preact/src/Templates/Stores/TargetStore.test.ts @@ -59,12 +59,18 @@ describe('TargetStore', () => { }; const store = new TargetStore(template, dependencies, settings); + expect(store.component).toStrictEqual('Search'); store.setComponent('NewSearch'); expect(store.component).toStrictEqual('NewSearch'); + expect(store.resultComponent).toStrictEqual('CustomResult'); store.setResultComponent('NewResult'); expect(store.resultComponent).toStrictEqual('NewResult'); + expect(store.theme).toStrictEqual({ + location: 'local', + name: 'customTheme', + }); store.setTheme('newTheme', 'local' as TemplateThemeTypes); expect(store.theme).toStrictEqual({ location: 'local', diff --git a/packages/snap-preact/src/Templates/Stores/TemplateStore.ts b/packages/snap-preact/src/Templates/Stores/TemplateStore.ts index a2d05651c..5d8f1ce08 100644 --- a/packages/snap-preact/src/Templates/Stores/TemplateStore.ts +++ b/packages/snap-preact/src/Templates/Stores/TemplateStore.ts @@ -6,9 +6,9 @@ import { TargetStore } from './TargetStore'; import { LibraryStore } from './LibraryStore'; import { debounce } from '@searchspring/snap-toolbox'; -import type { ResultComponent } from '../../../components/src'; -import type { DeepPartial, GlobalThemeStyleScript } from '../../types'; -import type { Theme, ThemeVariables } from '../../../components/src'; +import type { ResultComponent, ThemeMinimal, ThemeOverrides, ThemePartial, ThemeVariablesPartial } from '../../../components/src'; +import type { GlobalThemeStyleScript } from '../../types'; +import type { Theme } from '../../../components/src'; export type TemplateThemeTypes = 'library' | 'local'; export type TemplateTypes = 'search' | 'autocomplete' | `recommendation/${RecsTemplateTypes}`; export type TemplateCustomComponentTypes = 'result' | 'badge'; @@ -38,8 +38,8 @@ type WindowProperties = { type TemplateStoreThemeConfig = { extends: 'pike' | 'bocachica'; // various themes available style?: GlobalThemeStyleScript; - variables?: DeepPartial; - overrides?: Theme; + variables?: ThemeVariablesPartial; + overrides?: ThemeOverrides; }; export type TemplateStoreComponentConfig = { @@ -212,10 +212,10 @@ export class TemplatesStore { name: string; type: TemplateThemeTypes; base: Theme; - overrides?: Theme; - variables?: DeepPartial; - currency: Partial; - language: Partial; + overrides?: ThemePartial; + variables?: ThemeVariablesPartial; + currency: ThemeMinimal; + language: ThemeMinimal; innerWidth?: number; style?: GlobalThemeStyleScript; }) { diff --git a/packages/snap-preact/src/Templates/Stores/ThemeStore.test.ts b/packages/snap-preact/src/Templates/Stores/ThemeStore.test.ts index 0f704eba8..0c06b0c0a 100644 --- a/packages/snap-preact/src/Templates/Stores/ThemeStore.test.ts +++ b/packages/snap-preact/src/Templates/Stores/ThemeStore.test.ts @@ -1,362 +1,1091 @@ -import { ThemeStore, mergeLayers } from './ThemeStore'; +import { configure as configureMobx } from 'mobx'; +import '@testing-library/jest-dom'; +import { waitFor } from '@testing-library/preact'; + +import { ThemeStore, ThemeStoreConfig, mergeThemeLayers } from './ThemeStore'; import { StorageStore } from '@searchspring/snap-store-mobx'; import type { TemplatesStoreDependencies, TemplateThemeTypes, TemplatesStoreSettings } from './TemplateStore'; -import type { Theme } from '../../../components/src/providers/theme'; +import type { Theme, ThemeVariables, ThemePartial } from '../../../components/src/providers/theme'; import { bocachica } from '../../../components/src/themes/bocachica'; import { pike } from '../../../components/src/themes/pike'; -const themes = [ - { - name: 'bocachica', - base: bocachica, +// configure MobX +configureMobx({ enforceActions: 'never' }); + +const testThemeVariables: ThemeVariables = { + breakpoints: [0, 420, 720, 1440], + colors: { + primary: 'test.color.primary', + secondary: 'test.color.secondary', + accent: 'test.color.accent', + active: { + foreground: 'test.color.active.foreground', + background: 'test.color.active.background', + accent: 'test.color.active.accent', + }, + hover: { + foreground: 'test.color.hover.foreground', + background: 'test.color.hover.background', + accent: 'test.color.hover.accent', + }, }, - { - name: 'pike', - base: pike, +}; + +const testTheme: Theme = { + name: 'test', + variables: testThemeVariables, + components: { + results: { + columns: 5, + }, }, -]; -themes.forEach((theme) => { - describe(`ThemeStore using ${theme.name} theme`, () => { - let dependencies: TemplatesStoreDependencies; - let settings: TemplatesStoreSettings; - let baseTheme: Theme; - - beforeEach(() => { - baseTheme = theme.base; - dependencies = { - storage: new StorageStore(), - }; - settings = { - editMode: false, - }; + layoutOptions: [ + { + label: 'list', + value: 'list', + default: true, + overrides: { + components: { + results: { + layout: 'list', + }, + }, + }, + }, + { + label: 'grid', + value: 'grid', + overrides: { + components: { + results: { + layout: 'grid', + }, + }, + }, + }, + ], + responsive: [ + { + components: { + results: { + columns: 1, + }, + }, + }, + { + components: { + results: { + columns: 2, + }, + }, + }, + { + components: { + results: { + columns: 3, + }, + }, + }, + { + components: { + results: { + columns: 4, + }, + }, + }, + ], +}; + +describe('ThemeStore', () => { + let dependencies: TemplatesStoreDependencies; + let settings: TemplatesStoreSettings; + + beforeEach(() => { + dependencies = { + storage: new StorageStore(), + }; + + settings = { + editMode: false, + }; + + document.body.innerHTML = ``; + }); + + it('has expected defaults and can invoke methods', () => { + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: { + name: 'empty', + variables: testThemeVariables, + responsive: [{}, {}, {}, {}], + components: {}, + }, + overrides: {}, + variables: {}, + currency: {}, + language: {}, + innerWidth: 0, + }; + + const store = new ThemeStore(config, dependencies, settings); + expect(store).toBeDefined(); + expect(store.name).toBe(config.name); + expect(store.type).toBe(config.type); + + expect(store.layout).toBeDefined(); + // @ts-ignore - private property + expect(store.layout.name).toBe(config.name); + // @ts-ignore - private property + expect(store.layout.type).toBe(config.type); + // @ts-ignore - private property + expect(store.layout.storage).toBe(dependencies.storage); + expect(store.layout.selected).toBeUndefined(); + + // @ts-ignore - private property + expect(store.dependencies).toBe(dependencies); + // @ts-ignore - private property + expect(store.base).toStrictEqual(config.base); + // @ts-ignore - private property + expect(store.overrides).toStrictEqual(config.overrides); + expect(store.variables).toStrictEqual(config.variables); + expect(store.currency).toStrictEqual(config.currency); + expect(store.language).toStrictEqual(config.language); + expect(store.stored).toStrictEqual({}); + expect(store.innerWidth).toBe(config.innerWidth); + + expect(store.theme).toStrictEqual({ + ...config.base, + name: config.name, }); - it('has expected defaults and can invoke methods', () => { - const config = { - name: 'global', - type: 'local' as TemplateThemeTypes, - base: {}, - overrides: {}, - variables: {}, - currency: {}, - language: {}, - innerWidth: 0, - style: () => ({}), - }; - - const store = new ThemeStore(config, dependencies, settings); - expect(store).toBeDefined(); - expect(store.name).toBe(config.name); - expect(store.type).toBe(config.type); - - expect(store.layout).toBeDefined(); - // @ts-ignore - private property - expect(store.layout.name).toBe(config.name); - // @ts-ignore - private property - expect(store.layout.type).toBe(config.type); - // @ts-ignore - private property - expect(store.layout.storage).toBe(dependencies.storage); - expect(store.layout.selected).toBeUndefined(); - - // @ts-ignore - private property - expect(store.dependencies).toBe(dependencies); - // @ts-ignore - private property - expect(store.base).toStrictEqual(config.base); - // @ts-ignore - private property - expect(store.overrides).toStrictEqual(config.overrides); - expect(store.variables).toStrictEqual(config.variables); - expect(store.currency).toStrictEqual(config.currency); - expect(store.language).toStrictEqual(config.language); - expect(store.stored).toStrictEqual({}); - expect(store.innerWidth).toBe(config.innerWidth); - - expect(store.theme).toStrictEqual({ - name: config.name, - variables: {}, - }); - - store.setInnerWidth(100); - expect(store.innerWidth).toBe(100); - - const currency = { components: { price: { symbol: '€' } } }; - store.setCurrency(currency); - expect(store.currency).toStrictEqual(currency); - - const language = { components: { filterSummary: { title: 'Filter Summary Title' } } }; - store.setLanguage(language); - expect(store.language).toStrictEqual(language); - - const overrideObj = { path: ['custom'], rootEditingKey: 'variables', value: 'customValue' }; - store.setOverride(overrideObj); - expect(store.stored).toStrictEqual({ variables: { custom: 'customValue' } }); - - const overrideObj2 = { path: ['custom', 'property'], rootEditingKey: 'variables', value: 'customValue' }; - store.setOverride(overrideObj2); - expect(store.stored).toStrictEqual({ variables: { custom: { property: 'customValue' } } }); + store.setInnerWidth(100); + expect(store.innerWidth).toBe(100); + + const currency = { components: { price: { symbol: '€' } } }; + store.setCurrency(currency); + expect(store.currency).toStrictEqual(currency); + + const language = { components: { filterSummary: { title: 'Filter Summary Title' } } }; + store.setLanguage(language); + expect(store.language).toStrictEqual(language); + + const overrideObj = { path: ['custom'], rootEditingKey: 'variables', value: 'customValue' }; + store.setOverride(overrideObj); + expect(store.stored).toStrictEqual({ variables: { custom: 'customValue' } }); + + const overrideObj2 = { path: ['custom', 'property'], rootEditingKey: 'variables', value: 'customValue' }; + store.setOverride(overrideObj2); + expect(store.stored).toStrictEqual({ variables: { custom: { property: 'customValue' } } }); + + // order here matches order merged via theme() getter + const merged = mergeThemeLayers(config.base, currency, language, store.stored); + + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, }); + }); - it('can get base theme with its layoutOptions applied', () => { - const config = { - name: 'global', - type: 'local' as TemplateThemeTypes, - base: baseTheme, - overrides: {}, - variables: {}, - currency: {}, - language: {}, - innerWidth: 0, - style: () => ({}), - }; - - const store = new ThemeStore(config, dependencies, settings); - - const defaultLayoutOptionOverrides = config.base.layoutOptions?.find((option) => option.default)?.overrides || {}; - const base = mergeLayers(config.base, defaultLayoutOptionOverrides as Partial); - - expect(store.theme).toStrictEqual({ - ...base, - name: config.name, - }); + it('can get theme', () => { + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: {}, + variables: {}, + currency: {}, + language: {}, + innerWidth: 0, + }; + + const store = new ThemeStore(config, dependencies, settings); + + const defaultLayoutOptionOverrides = config.base.layoutOptions?.find((option) => option.default)?.overrides || {}; + + // order here matches order merged via theme() getter + const merged = mergeThemeLayers(config.base, config.currency, config.language, config.overrides!, defaultLayoutOptionOverrides); + + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, }); - it('can get theme with overrides applied', () => { - const config = { - name: 'global', - type: 'local' as TemplateThemeTypes, - base: baseTheme, - overrides: { - components: { - results: { - columns: 5, - }, + // assertions on 'test' theme + expect(merged.components?.results?.columns == 5); + }); + + it('can get theme with overrides applied', () => { + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: { + components: { + results: { + columns: 5, }, }, - variables: {}, - currency: {}, - language: {}, - innerWidth: 0, - style: () => ({}), - }; + }, + variables: {}, + currency: {}, + language: {}, + innerWidth: 0, + }; + + const store = new ThemeStore(config, dependencies, settings); - const store = new ThemeStore(config, dependencies, settings); + const defaultLayoutOptionOverrides = config.base.layoutOptions?.find((option) => option.default)?.overrides || {}; - const defaultLayoutOptionOverrides = config.base.layoutOptions?.find((option) => option.default)?.overrides || {}; - const base = mergeLayers(config.base, defaultLayoutOptionOverrides as Partial); - const merged = mergeLayers(base, config.overrides); + // order here matches order merged via theme() getter + const merged = mergeThemeLayers(config.base, config.currency, config.language, config.overrides!, defaultLayoutOptionOverrides); - expect(store.theme).toStrictEqual({ - ...merged, - name: config.name, - }); + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, }); + }); - it('can get theme with variables applied', () => { - const config = { - name: 'global', - type: 'local' as TemplateThemeTypes, - base: baseTheme, - overrides: { - components: { - results: { - columns: 5, - }, + it('can get theme with overrides and variables applied', () => { + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: { + components: { + results: { + columns: 5, }, }, - variables: { - breakpoints: [100, 200, 300, 400] as [number, number, number, number], - color: { - primary: 'primary', - secondary: 'secondary', - accent: 'accent', - active: { - foreground: 'active.foreground', - background: 'active.background', - accent: 'active.accent', - }, - hover: { - foreground: 'hover.foreground', - background: 'hover.background', - accent: 'hover.account', - }, + }, + variables: { + breakpoints: [100, 200, 300, 400], + colors: { + primary: 'primary', + secondary: 'secondary', + accent: 'accent', + active: { + foreground: 'active.foreground', + background: 'active.background', + accent: 'active.accent', + }, + hover: { + foreground: 'hover.foreground', + background: 'hover.background', + accent: 'hover.account', }, }, - currency: {}, - language: {}, - innerWidth: 0, - style: () => ({}), - }; + }, + currency: {}, + language: {}, + innerWidth: 0, + }; + + const store = new ThemeStore(config, dependencies, settings); - const store = new ThemeStore(config, dependencies, settings); + const defaultLayoutOptionOverrides = config.base.layoutOptions?.find((option) => option.default)?.overrides || {}; - const defaultLayoutOptionOverrides = config.base.layoutOptions?.find((option) => option.default)?.overrides || {}; - const base = mergeLayers(config.base, defaultLayoutOptionOverrides as Partial); - const merged = mergeLayers(base, config.overrides, { variables: config.variables }); + // order here matches order merged via theme() getter + const merged = mergeThemeLayers( + config.base, + config.currency, + config.language, + config.overrides!, + { variables: config.variables }, + defaultLayoutOptionOverrides + ); - expect(store.theme).toStrictEqual({ - ...merged, - name: config.name, - }); + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, }); + }); - it('can get theme with currency and language applied', () => { - const config = { - name: 'global', - type: 'local' as TemplateThemeTypes, - base: baseTheme, - overrides: { - components: { - results: { - columns: 5, - }, + it('can get theme with currency and language applied', () => { + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: { + components: { + results: { + columns: 5, }, }, - variables: { - breakpoints: [100, 200, 300, 400] as [number, number, number, number], - color: { - primary: 'primary', - secondary: 'secondary', - accent: 'accent', - active: { - foreground: 'active.foreground', - background: 'active.background', - accent: 'active.accent', - }, - hover: { - foreground: 'hover.foreground', - background: 'hover.background', - accent: 'hover.account', - }, + }, + variables: { + breakpoints: [100, 200, 300, 400], + colors: { + primary: 'primary', + secondary: 'secondary', + accent: 'accent', + active: { + foreground: 'active.foreground', + background: 'active.background', + accent: 'active.accent', + }, + hover: { + foreground: 'hover.foreground', + background: 'hover.background', + accent: 'hover.account', }, }, - currency: { - components: { - price: { - symbol: '€', - }, + }, + currency: { + components: { + price: { + symbol: '€', }, }, - language: { - components: { - filterSummary: { - title: 'Filter Summary Title', + }, + language: { + components: { + filterSummary: { + title: 'Filter Summary Title', + }, + }, + }, + innerWidth: 0, + }; + + const store = new ThemeStore(config, dependencies, settings); + + const defaultLayoutOptionOverrides = config.base.layoutOptions?.find((option) => option.default)?.overrides || {}; + + // order here matches order merged via theme() getter + const merged = mergeThemeLayers( + config.base, + config.currency, + config.language, + config.overrides!, + { variables: config.variables }, + defaultLayoutOptionOverrides + ); + + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, + }); + }); + + it('can get theme at breakpoint', () => { + const bpIndex = 0; // simulate being at first breakpoint + expect(testTheme.variables?.breakpoints[bpIndex]).toBe(0); + + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: {}, + variables: {}, + currency: {}, + language: {}, + innerWidth: 300, + }; + + const store = new ThemeStore(config, dependencies, settings); + expect(store.innerWidth).toBe(config.innerWidth); + + const layoutOptions = config.base.responsive?.[bpIndex].layoutOptions || config.base.layoutOptions; + const defaultLayoutOptionOverrides = layoutOptions?.find((option) => option.default)?.overrides || {}; + + const baseResponsiveOverrides = config.base.responsive?.[bpIndex]!; + expect(baseResponsiveOverrides).toBeDefined(); + + // order here matches order merged via theme() getter + const merged = mergeThemeLayers( + config.base, + baseResponsiveOverrides, + config.currency, + config.language, + config.overrides!, + defaultLayoutOptionOverrides + ); + + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, + }); + + // extra assertions on 'test' theme + expect(merged.components?.results?.columns).toBe(1); + }); + + it('can get theme with last breakpoint applied', () => { + const bpIndex = 3; + expect(testTheme.variables?.breakpoints[bpIndex]).toBeGreaterThan(0); + + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: {}, + variables: {}, + currency: {}, + language: {}, + innerWidth: 1600, + }; + + const store = new ThemeStore(config, dependencies, settings); + expect(store.innerWidth).toBe(config.innerWidth); + + const layoutOptions = config.base.responsive?.[bpIndex].layoutOptions || config.base.layoutOptions; + const defaultLayoutOptionOverrides = layoutOptions?.find((option) => option.default)?.overrides || {}; + + const baseResponsiveOverrides = config.base.responsive?.[bpIndex]!; + + const merged = mergeThemeLayers( + config.base, + config.currency, + config.language, + config.overrides!, + baseResponsiveOverrides, + defaultLayoutOptionOverrides + ); + + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, + }); + + // extra assertions on 'test' theme + expect(merged.components?.results?.columns).toBe(4); + }); + + it('can get theme at breakpoint and use breakpoint overrides', () => { + const bpIndex = 0; // simulate being at first breakpoint + expect(testTheme.variables?.breakpoints[bpIndex]).toBe(0); + + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: { + responsive: [ + { + components: { + results: { columns: 7 }, }, }, + {}, + {}, + {}, + ], + }, + variables: { + breakpoints: [0, 100, 200, 300], + }, + currency: {}, + language: {}, + innerWidth: 50, + }; + + const store = new ThemeStore(config, dependencies, settings); + expect(store.innerWidth).toBe(config.innerWidth); + + const layoutOptions = config.base.responsive?.[bpIndex].layoutOptions || config.base.layoutOptions; + const defaultLayoutOptionOverrides = layoutOptions?.find((option) => option.default)?.overrides || {}; + + const baseResponsiveOverrides = config.base.responsive?.[bpIndex]!; + expect(baseResponsiveOverrides).toBeDefined(); + + const additionalResponsiveOverrides = config.overrides?.responsive?.[bpIndex]!; + expect(baseResponsiveOverrides).toBeDefined(); + + // order here matches order merged via theme() getter + const merged = mergeThemeLayers( + config.base, + baseResponsiveOverrides, + config.currency, + config.language, + config.overrides!, + additionalResponsiveOverrides, + { variables: config.variables }, + defaultLayoutOptionOverrides + ); + + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, + }); + + // extra assertions on 'test' theme + expect(merged.components?.results?.columns).toBe(7); + }); + + it('can get theme with all the things ', () => { + const bpIndex = 0; // simulate being at first breakpoint + expect(testTheme.variables?.breakpoints[bpIndex]).toBe(0); + + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: { + responsive: [ + { + components: { + results: { columns: 7 }, + }, + }, + {}, + {}, + {}, + ], + }, + variables: { + breakpoints: [0, 100, 200, 300], + }, + currency: { + components: { + results: { columns: 9 }, + }, + }, + language: { + components: { + results: { columns: 11 }, }, - innerWidth: 0, - style: () => ({}), - }; + }, + innerWidth: 50, + }; + + const store = new ThemeStore(config, dependencies, settings); + expect(store.innerWidth).toBe(config.innerWidth); + + const layoutOptions = config.base.responsive?.[bpIndex].layoutOptions || config.base.layoutOptions; + const defaultLayoutOptionOverrides = layoutOptions?.find((option) => option.default)?.overrides || {}; + + const baseResponsiveOverrides = config.base.responsive?.[bpIndex]!; + expect(baseResponsiveOverrides).toBeDefined(); + + const additionalResponsiveOverrides = config.overrides?.responsive?.[bpIndex]!; + expect(baseResponsiveOverrides).toBeDefined(); + + const overrideObj = { path: ['results', 'columns'], rootEditingKey: 'components', value: 12 }; + store.setOverride(overrideObj); + + // testing all the things!!! + // mergeThemeLayers(base, baseResponsive, currency, language, overrides, overridesResponsive, variables, layout, editor) + + const merged = mergeThemeLayers( + config.base, + baseResponsiveOverrides, + config.currency, + config.language, + config.overrides!, + additionalResponsiveOverrides, + { variables: config.variables }, + defaultLayoutOptionOverrides, + store.stored + ); + + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, + }); + + // extra assertions on 'test' theme + expect(merged.components?.results?.columns).toBe(12); + }); + + it('can select layoutOption', () => { + const bpIndex = 0; + const config: ThemeStoreConfig = { + name: 'global', + type: 'local', + base: testTheme, + overrides: {}, + variables: {}, + currency: {}, + language: {}, + innerWidth: 0, + }; + + const store = new ThemeStore(config, dependencies, settings); - const store = new ThemeStore(config, dependencies, settings); + const layoutOptions = config.base.responsive?.[bpIndex].layoutOptions || config.base.layoutOptions; + const layoutOptionDefault = layoutOptions?.find((option) => option.default); + const layoutOptionNotDefault = layoutOptions?.find((option) => !option.default); - const defaultLayoutOptionOverrides = config.base.layoutOptions?.find((option) => option.default)?.overrides || {}; - const base = mergeLayers(config.base, defaultLayoutOptionOverrides as Partial); - const merged = mergeLayers(base, config.currency, config.language, config.overrides, { variables: config.variables }); + if (!layoutOptionNotDefault?.overrides || !layoutOptionDefault?.overrides) { + // skip if theme does not have any layoutOptions + return; + } - expect(store.theme).toStrictEqual({ - ...merged, - name: config.name, - }); + expect(store.layout.selected).toStrictEqual(undefined); + + const defaultLayoutOptionOverrides = layoutOptionDefault.overrides; + const selectedLayoutOptionOverrides = layoutOptionNotDefault.overrides; + + const merged = mergeThemeLayers(config.base, config.currency, config.language, config.overrides!, defaultLayoutOptionOverrides); + + expect(store.theme).toStrictEqual({ + ...merged, + name: config.name, }); - it('can get theme with breakpoints applied', () => { - const bpIndex = 1; // simulate being at second breakpoint - expect(baseTheme.variables?.breakpoints[bpIndex]).toBeGreaterThan(0); - - const config = { - name: 'global', - type: 'local' as TemplateThemeTypes, - base: baseTheme, - overrides: {}, - variables: {}, - currency: {}, - language: {}, - innerWidth: baseTheme.variables!.breakpoints[bpIndex] + 10, - style: () => ({}), - }; - - const store = new ThemeStore(config, dependencies, settings); - expect(store.innerWidth).toBe(config.innerWidth); - - const layoutOptions = config.base.responsive?.[bpIndex].layoutOptions || config.base.layoutOptions; - const defaultLayoutOptionOverrides = layoutOptions?.find((option) => option.default)?.overrides || {}; - const base = mergeLayers(config.base, defaultLayoutOptionOverrides as Partial); - - const bpOverrides = config.base.responsive?.[bpIndex]!; - expect(bpOverrides).toBeDefined(); - - const merged = mergeLayers(base, config.currency, config.language, config.overrides, bpOverrides); - - expect(store.theme).toStrictEqual({ - ...merged, - name: config.name, - }); + // extra assertions on 'test' theme + expect(merged.components?.results?.layout).toBe('list'); + + store.layout.select(layoutOptionNotDefault); + expect(store.layout.selected).toStrictEqual(layoutOptionNotDefault); + + // check merged theme after new layout selection + const mergedSelectedLayout = mergeThemeLayers(config.base, config.currency, config.language, config.overrides!, selectedLayoutOptionOverrides); + + expect(store.theme).toStrictEqual({ + ...mergedSelectedLayout, + name: config.name, }); - it('can get theme with last breakpoint applied & responsive layoutOptions', () => { - const bpIndex = baseTheme.variables!.breakpoints.length - 1; - expect(baseTheme.variables?.breakpoints[bpIndex]).toBeGreaterThan(0); + expect(merged).not.toEqual(mergedSelectedLayout); + + // extra assertions on 'test' theme + expect(mergedSelectedLayout.components?.results?.layout).toBe('grid'); + }); + + it('adds a style sheet to the page when a style is provided', async () => { + const config: ThemeStoreConfig = { + name: 'globally', + type: 'local', + base: testTheme, + overrides: {}, + variables: {}, + currency: {}, + language: {}, + innerWidth: 0, + style: (theme) => { + return { + '.ss__result': { + fontSize: '200%', + }, + }; + }, + }; + + new ThemeStore(config, dependencies, settings); + + // wait for rendering of BranchOverride component + await waitFor(() => { + const styleElements = document.querySelectorAll('head style')!; + expect(styleElements).toHaveLength(2); + + expect(styleElements[0].innerHTML).toBe(``); + + expect(styleElements[1]).toHaveAttribute('data-emotion', 'ss-global'); + }); + }); +}); - const config = { - name: 'global', - type: 'local' as TemplateThemeTypes, - base: baseTheme, - overrides: {}, - variables: {}, - currency: {}, - language: {}, - innerWidth: baseTheme.variables!.breakpoints[bpIndex] + 10, - style: () => ({}), - }; +describe('mergeThemeLayers function', () => { + it(`deep merges 'variables' in ThemePartials`, () => { + const themePartial1: ThemePartial = { + variables: { + breakpoints: [1, 2, 3, 4], + colors: { + primary: 'blue', + active: { + accent: 'blue', + }, + hover: { + accent: 'blue', + }, + }, + }, + }; - const store = new ThemeStore(config, dependencies, settings); - expect(store.innerWidth).toBe(config.innerWidth); + const themePartial2: ThemePartial = { + variables: { + breakpoints: [5, 6, 7, 8], + colors: { + primary: 'red', + active: { + foreground: 'red', + accent: 'red', + }, + }, + }, + }; - const layoutOptions = config.base.responsive?.[bpIndex].layoutOptions || config.base.layoutOptions; - const defaultLayoutOptionOverrides = layoutOptions?.find((option) => option.default)?.overrides || {}; + const merged = mergeThemeLayers(themePartial1, themePartial2); + expect(merged).toStrictEqual({ + variables: { + breakpoints: [5, 6, 7, 8], + colors: { + primary: 'red', + active: { + foreground: 'red', + accent: 'red', + }, + hover: { + accent: 'blue', + }, + }, + }, + }); + }); - const base = mergeLayers(config.base, defaultLayoutOptionOverrides as Partial); + it(`deep merges 'responsive' in ThemePartials`, () => { + const themePartial1: ThemePartial = { + responsive: [ + { + components: { + results: { columns: 1 }, + }, + }, + {}, + {}, + {}, + ], + }; + + const themePartial2: ThemePartial = { + responsive: [ + { + components: { + result: { layout: 'grid' }, + }, + }, + {}, + {}, + { + components: { + results: { columns: 1 }, + }, + }, + ], + }; + + const merged = mergeThemeLayers(themePartial1, themePartial2); + expect(merged).toStrictEqual({ + responsive: [ + { + components: { + result: { layout: 'grid' }, + results: { columns: 1 }, + }, + }, + {}, + {}, + { + components: { + results: { columns: 1 }, + }, + }, + ], + }); + }); + + it(`deep merges 'components' in ThemePartials`, () => { + const themePartial1: ThemePartial = { + components: { + results: { columns: 1, gapSize: '20px' }, + noResults: { + suggestionsList: ['do this', 'do that'], + contactsList: [ + { + title: 'call us', + content: '1-800-its-snap', + }, + { + title: 'email us', + content: 'snap@searchspring.com', + }, + ], + }, + }, + }; + + const themePartial2: ThemePartial = { + components: { + results: { columns: 5, rows: 2 }, + noResults: { + suggestionsList: ['dont do this', 'do instead', 'this'], + contactsList: [ + { + title: 'dont call us', + content: '1-800-its-snap', + }, + ], + }, + }, + }; + + const merged = mergeThemeLayers(themePartial1, themePartial2); + expect(merged).toStrictEqual({ + components: { + results: { columns: 5, rows: 2, gapSize: '20px' }, + noResults: { + suggestionsList: ['dont do this', 'do instead', 'this'], + contactsList: [ + { + title: 'dont call us', + content: '1-800-its-snap', + }, + ], + }, + }, + }); + }); - const bpOverrides = config.base.responsive?.[bpIndex]!; - expect(bpOverrides).toBeDefined(); + it(`deep merges 'layoutOptions' in ThemePartials`, () => { + const themePartial1: ThemePartial = { + layoutOptions: [ + { + label: 'dos', + value: '2', + default: true, + overrides: { + components: { + results: { + layout: 'grid', + columns: 2, + }, + }, + }, + }, + { + label: 'uno', + value: '1', + overrides: { + components: { + results: { + layout: 'grid', + columns: 1, + }, + }, + }, + }, + ], + }; - const merged = mergeLayers(base, config.currency, config.language, config.overrides, bpOverrides); + const themePartial2: ThemePartial = { + layoutOptions: [ + { + label: 'list', + value: 'list', + default: true, + overrides: { + components: { + results: { + layout: 'list', + columns: 1, + }, + }, + }, + }, + { + label: 'grid', + value: 'grid', + overrides: { + components: { + results: { + layout: 'grid', + columns: 3, + }, + }, + }, + }, + ], + }; - expect(store.theme).toStrictEqual({ - ...merged, - name: config.name, - }); + const merged = mergeThemeLayers(themePartial1, themePartial2); + expect(merged).toStrictEqual({ + layoutOptions: [ + { + label: 'list', + value: 'list', + default: true, + overrides: { + components: { + results: { + layout: 'list', + columns: 1, + }, + }, + }, + }, + { + label: 'grid', + value: 'grid', + overrides: { + components: { + results: { + layout: 'grid', + columns: 3, + }, + }, + }, + }, + ], }); + }); - it('can select layoutOptions', () => { - const bpIndex = 0; - const config = { - name: 'global', - type: 'local' as TemplateThemeTypes, - base: baseTheme, - overrides: {}, - variables: {}, - currency: {}, - language: {}, - innerWidth: 0, - style: () => ({}), - }; - - const store = new ThemeStore(config, dependencies, settings); - - const layoutOptions = config.base.responsive?.[bpIndex].layoutOptions || config.base.layoutOptions; - const layoutOptionDefault = layoutOptions?.find((option) => option.default); - const layoutOptionNotDefault = layoutOptions?.find((option) => !option.default); - - if (!layoutOptionNotDefault || !layoutOptionDefault) { - // skip if theme does not have any layoutOptions - return; - } - - expect(store.layout.selected).toStrictEqual(undefined); - - store.layout.select(layoutOptionNotDefault!); - expect(store.layout.selected).toStrictEqual(layoutOptionNotDefault); + it(`deep merges all the things in ThemePartials`, () => { + const themePartial1: ThemePartial = { + variables: { + breakpoints: [1, 2, 3, 4], + colors: { + primary: 'blue', + active: { + accent: 'blue', + }, + hover: { + accent: 'blue', + }, + }, + }, + components: { + results: { columns: 1, gapSize: '20px' }, + noResults: { + suggestionsList: ['do this', 'do that'], + contactsList: [ + { + title: 'call us', + content: '1-800-its-snap', + }, + { + title: 'email us', + content: 'snap@searchspring.com', + }, + ], + }, + }, + responsive: [ + { + components: { + results: { columns: 1 }, + }, + layoutOptions: [ + { + label: 'dos', + value: '2', + default: true, + overrides: { + components: { + results: { + layout: 'grid', + columns: 2, + }, + }, + }, + }, + ], + }, + {}, + {}, + {}, + ], + layoutOptions: [ + { + label: 'dos', + value: '2', + default: true, + overrides: { + components: { + results: { + layout: 'grid', + columns: 2, + }, + }, + }, + }, + { + label: 'uno', + value: '1', + overrides: { + components: { + results: { + layout: 'grid', + columns: 1, + }, + }, + }, + }, + ], + }; + + const themePartial2: ThemePartial = { + variables: { + breakpoints: [0, 420, 720, 1440], + colors: { + primary: 'red', + active: { + accent: 'red', + }, + hover: { + foreground: 'red', + background: 'red', + }, + }, + }, + components: { + results: { columns: 5, rows: 2 }, + noResults: { + suggestionsList: ['dont do this', 'do instead', 'this'], + contactsList: [ + { + title: 'dont call us', + content: '1-800-its-snap', + }, + ], + }, + }, + responsive: [ + { + layoutOptions: [], + }, + { + components: {}, + }, + {}, + {}, + ], + layoutOptions: [], + }; + + const merged = mergeThemeLayers(themePartial1, themePartial2); + expect(merged).toStrictEqual({ + variables: { + breakpoints: [0, 420, 720, 1440], + colors: { + primary: 'red', + active: { + accent: 'red', + }, + hover: { + accent: 'blue', + foreground: 'red', + background: 'red', + }, + }, + }, + components: { + results: { columns: 5, rows: 2, gapSize: '20px' }, + noResults: { + suggestionsList: ['dont do this', 'do instead', 'this'], + contactsList: [ + { + title: 'dont call us', + content: '1-800-its-snap', + }, + ], + }, + }, + responsive: [ + { + components: { + results: { columns: 1 }, + }, + layoutOptions: [], + }, + { components: {} }, + {}, + {}, + ], + layoutOptions: [], }); }); }); diff --git a/packages/snap-preact/src/Templates/Stores/ThemeStore.tsx b/packages/snap-preact/src/Templates/Stores/ThemeStore.tsx index 4c058131e..62f39ded0 100644 --- a/packages/snap-preact/src/Templates/Stores/ThemeStore.tsx +++ b/packages/snap-preact/src/Templates/Stores/ThemeStore.tsx @@ -6,12 +6,24 @@ import { StorageStore } from '@searchspring/snap-store-mobx'; import { TemplateThemeTypes, type TemplatesStoreSettings, type TemplatesStoreDependencies } from './TemplateStore'; import { Global, css } from '@emotion/react'; -import { type Theme, type ThemeVariables } from '../../../components/src'; +import { ThemeMinimal, ThemeVariablesPartial, type Theme, ThemePartial, ThemeOverrides } from '../../../components/src'; import { CacheProvider } from '../../../components/src/providers/cache'; -import type { DeepPartial, GlobalThemeStyleScript } from '../../types'; +import type { GlobalThemeStyleScript } from '../../types'; import type { ListOption } from '../../../components/src/types'; import { observer } from 'mobx-react'; +export type ThemeStoreConfig = { + name: string; + type: TemplateThemeTypes; + base: Theme; + overrides?: ThemeOverrides; + variables?: ThemeVariablesPartial; + currency: ThemeMinimal; + language: ThemeMinimal; + innerWidth?: number; + style?: GlobalThemeStyleScript; +}; + class SelectedLayout { public selected?: ListOption; private storage: StorageStore; @@ -42,28 +54,14 @@ export class ThemeStore { private dependencies: TemplatesStoreDependencies; private base: Theme; - private overrides: Theme; - variables: DeepPartial; - currency: Partial; - language: Partial; - stored: Partial; + private overrides: ThemeOverrides; + variables: ThemeVariablesPartial; + currency: ThemeMinimal; + language: ThemeMinimal; + stored: ThemePartial; innerWidth?: number; - constructor( - config: { - name: string; - type: TemplateThemeTypes; - base: Theme; - overrides?: Theme; - variables?: DeepPartial; - currency: Partial; - language: Partial; - innerWidth?: number; - style?: GlobalThemeStyleScript; - }, - dependencies: TemplatesStoreDependencies, - settings: TemplatesStoreSettings - ) { + constructor(config: ThemeStoreConfig, dependencies: TemplatesStoreDependencies, settings: TemplatesStoreSettings) { this.dependencies = dependencies; const { name, style, type, base, overrides, variables, currency, language, innerWidth } = config; @@ -87,6 +85,7 @@ export class ThemeStore { innerWidth: observable, }); + // handle adding the style to the document (should only happen once per theme) if (style) { const GlobalStyle = observer((props: any) => { const { self } = props; @@ -108,39 +107,40 @@ export class ThemeStore { } public get theme(): Theme { - let baseBreakpoint = {}; - let overrideBreakpoint = {}; - if (this.innerWidth && Number.isInteger(this.innerWidth)) { - const breakpoints = (this.variables.breakpoints || this.base.variables?.breakpoints) as number[]; - const breakpoint = breakpoints.find((breakpoint) => this.innerWidth! < breakpoint); - - if (breakpoint) { - const breakpointIndex = breakpoints.indexOf(breakpoint); - const responsiveIndex = Math.max(breakpointIndex - 1, 0); // index 0 also applies to under first breakpoint - baseBreakpoint = (this.base?.responsive && (this.base?.responsive as any)[responsiveIndex]) || {}; - overrideBreakpoint = (this.overrides?.responsive && (this.overrides?.responsive as any)[responsiveIndex]) || {}; - } else if (this.innerWidth >= breakpoints[breakpoints.length - 1]) { - // if innerWidth is greater than the last breakpoint, use the last breakpoint - const responsiveIndex = breakpoints.length - 1; - baseBreakpoint = (this.base?.responsive && (this.base?.responsive as any)[responsiveIndex]) || {}; - overrideBreakpoint = (this.overrides?.responsive && (this.overrides?.responsive as any)[responsiveIndex]) || {}; - } - } - - let theme = mergeLayers(this.base, baseBreakpoint, this.currency, this.language, this.overrides as Partial, overrideBreakpoint, { + /* + Themes consist of layers which are deep merged together in order (last merge has highest priority) + 1. base theme + 2. base theme responsive breakpoints + 3. currency overrides + 4. language overrides + 5. theme overrides + 6. theme overrides at responsive breakpoints + 7. altered theme variables + 8. layout option overrides + 9. stored theme editor overrides + */ + + const breakpoints = (this.variables.breakpoints || this.base.variables?.breakpoints) as number[]; + + const baseBreakpoint = getOverridesAtWidth(this.innerWidth, breakpoints, this.base); + const overrideBreakpoint = getOverridesAtWidth(this.innerWidth, breakpoints, this.overrides); + + let theme: Theme = mergeThemeLayers(this.base, baseBreakpoint, this.currency, this.language, this.overrides, overrideBreakpoint, { variables: this.variables, - } as Partial); + } as ThemePartial) as Theme; + // find layout option overrides const layoutOptions = theme.layoutOptions; const selectedOption: ListOption | undefined = layoutOptions?.find((option) => option?.value === this.layout.selected?.value) || layoutOptions?.find((option) => option?.default) || (Array.isArray(layoutOptions) ? layoutOptions[0] : undefined); + // apply selected overrides if they exist if (selectedOption?.overrides) { - theme = mergeLayers(theme, selectedOption.overrides as Partial); + theme = mergeThemeLayers(theme, selectedOption.overrides) as Theme; - // if the the selectedOption differs from this.layout.selected, then select the layout + // if the the selectedOption differs from this.layout.selected, then select the layout (can happen at breakpoint recalculations) if ( !this?.layout?.selected || (this?.layout?.selected && selectedOption.value !== this.layout.selected.value && selectedOption.label !== this.layout.selected.label) @@ -149,9 +149,9 @@ export class ThemeStore { } } - // Template Editor overrides + // TemplateEditor overrides if (this.stored) { - theme = mergeLayers(theme, this.stored); + theme = mergeThemeLayers(theme, this.stored) as Theme; } // change the theme name to match the ThemeStore theme name @@ -163,11 +163,11 @@ export class ThemeStore { this.innerWidth = innerWidth; } - public setCurrency(currency: Partial) { + public setCurrency(currency: ThemeMinimal) { this.currency = currency; } - public setLanguage(language: Partial) { + public setLanguage(language: ThemeMinimal) { this.language = language; } @@ -176,10 +176,18 @@ export class ThemeStore { // // TODO: remove key from stored // } + // alternative to setOverride below... + // public setOverrides(overrides: ThemeOverrides) { + // this.stored = mergeThemeLayers(this.stored, overrides); + // this.dependencies.storage.set(`themes.${this.type}.${this.name}.variables`, this.stored); + // } + // setting a key custom theme - public setOverride(obj: { path: string[]; rootEditingKey: string; value: string }) { + // TODO: any reason the rootEditingKey cannot be in the path? + // maybe interface could be: setOverride(path, value); + public setOverride(obj: { path: string[]; rootEditingKey: string; value: unknown }) { const { path, rootEditingKey, value } = obj; - const overrides = { + const overrides: ThemeOverrides = { [rootEditingKey]: path.reverse().reduce((res, key) => { if (path.indexOf(key) === 0) { return { @@ -191,25 +199,47 @@ export class ThemeStore { }; }, {}), }; - this.stored = mergeLayers(this.stored, overrides); + this.stored = mergeThemeLayers(this.stored, overrides); this.dependencies.storage.set(`themes.${this.type}.${this.name}.variables`, this.stored); } } -export function mergeLayers(...layers: Partial[]): Partial { - return deepmerge.all(layers, { isMergeableObject: isPlainObject, arrayMerge: combineMerge }); +export function mergeThemeLayers(...layers: ThemePartial[]): ThemePartial { + return deepmerge.all(layers, { arrayMerge: arrayMerge }); } -const combineMerge = (target: any, source: any, options: any) => { - const destination = target.slice(); +export function getOverridesAtWidth(width: number | undefined, breakpoints: number[], theme: ThemePartial): ThemePartial { + let overrides: ThemePartial = {}; + if (width && Number.isInteger(width) && theme.responsive) { + const breakpoint = breakpoints.find((breakpoint) => width! < breakpoint); + + if (breakpoint) { + const breakpointIndex = breakpoints.indexOf(breakpoint); + const responsiveIndex = Math.max(breakpointIndex - 1, 0); // index 0 also applies to under first breakpoint + overrides = (theme.responsive && (theme.responsive as any)[responsiveIndex]) || {}; + } else if (width >= breakpoints[breakpoints.length - 1]) { + // if innerWidth is greater than the last breakpoint, use the last breakpoint + const responsiveIndex = breakpoints.length - 1; + overrides = (theme.responsive && (theme.responsive as any)[responsiveIndex]) || {}; + } + } + + return overrides; +} + +const arrayMerge = (target: any, source: any, options: any) => { + // trim off any excess array entries + const destination = target.slice(0, source.length); + source.forEach((item: any, index: any) => { if (typeof destination[index] === 'undefined') { destination[index] = options.cloneUnlessOtherwiseSpecified(item, options); - } else if (options.isMergeableObject(item)) { + } else if (isPlainObject(item)) { destination[index] = deepmerge(target[index], item, options); - } else if (target.indexOf(item) === -1) { - destination.push(item); + } else { + destination[index] = item; } }); + return destination; };