From eace95124f411bd18f167690287d870749206588 Mon Sep 17 00:00:00 2001 From: Byron Wall Date: Mon, 21 Oct 2024 14:46:08 -0400 Subject: [PATCH] feat(filters): [publish_preview] insure that options and option groups are handled by the 'add filters' select input --- .../filters/src/filter-menu/filter-menu.tsx | 1 + .../trigger-button/trigger-button.styles.ts | 5 +- .../filters/src/filters.stories.tsx | 25 +++--- packages/components/filters/src/filters.tsx | 85 +++++++++++++------ 4 files changed, 75 insertions(+), 41 deletions(-) diff --git a/packages/components/filters/src/filter-menu/filter-menu.tsx b/packages/components/filters/src/filter-menu/filter-menu.tsx index a383e5ab57..40d70fa4a4 100644 --- a/packages/components/filters/src/filter-menu/filter-menu.tsx +++ b/packages/components/filters/src/filter-menu/filter-menu.tsx @@ -111,6 +111,7 @@ export const menuStyles = css` const menuBodyStyle = css` width: 100%; + overflow: hidden; `; function FilterMenu(props: TFilterMenuProps) { diff --git a/packages/components/filters/src/filter-menu/trigger-button/trigger-button.styles.ts b/packages/components/filters/src/filter-menu/trigger-button/trigger-button.styles.ts index a1c7a77254..afcadd0691 100644 --- a/packages/components/filters/src/filter-menu/trigger-button/trigger-button.styles.ts +++ b/packages/components/filters/src/filter-menu/trigger-button/trigger-button.styles.ts @@ -64,7 +64,7 @@ export const mainActionButton = css` z-index: 1; border-radius: ${designTokens.borderRadius20}; &:focus { - box-shadow: 0px 0px 0px 2px ${designTokens.colorPrimary40}; + box-shadow: 0 0 0 2px ${designTokens.colorPrimary40}; } `; @@ -76,6 +76,9 @@ export const removeButton = css` display: flex; svg { fill: ${designTokens.colorNeutral40} !important; + &:hover { + fill: ${designTokens.colorPrimary40} !important; + } } `; diff --git a/packages/components/filters/src/filters.stories.tsx b/packages/components/filters/src/filters.stories.tsx index 12cd45fbcb..15ba95d85f 100644 --- a/packages/components/filters/src/filters.stories.tsx +++ b/packages/components/filters/src/filters.stories.tsx @@ -7,6 +7,9 @@ import TextInput from '@commercetools-uikit/text-input'; import { MenuProps, MenuListProps } from 'react-select'; import Filters, { type TFiltersProps } from './filters'; +//TODO: move example data and components to separate files +//TODO: better docs on different states and how to accomplish them + const primaryColorOptions = [ { label: 'Blue', value: 'blue', key: 'blue', id: '2' }, { label: 'Red', value: 'red', key: 'red', id: '4' }, @@ -22,10 +25,10 @@ const secondaryColorOptions = [ { label: 'Silver', value: 'silver', key: 'silver', id: '10' }, ]; -// const filterGroups = [ -// { key: 'secondaryColors', label:
Secondary Colors
}, -// { key: 'primaryColors', label:
Primary Colors
}, -// ]; +const filterGroups = [ + { key: 'secondaryColors', label:
Secondary Colors
}, + { key: 'primaryColors', label:
Primary Colors
}, +]; const CustomSelectMenu = ({ children, @@ -57,14 +60,6 @@ const SearchComponent = () => { ); }; -// const RadioComponent = () => { -// const [v, setV] = useState('1'); - -// return ( - -// ); -// }; - const meta: Meta = { title: 'components/Filters', component: Filters, @@ -144,7 +139,7 @@ export const BasicExample: Story = (_props: TFiltersProps) => { { key: 'primaryColors', label: 'Primary Colors', - groupKey: 'primaryColors', + groupKey: 'notarealkey', filterMenuConfiguration: { renderMenuBody: () => ( { { key: 'fruits', label: 'Fruits', + groupKey: 'secondaryColors', filterMenuConfiguration: { renderMenuBody: () => ( { name="fruits" value={fruitsValue} onChange={(e) => { - console.log(e); setFruitsValue(e.target.value); }} > @@ -237,7 +232,7 @@ export const BasicExample: Story = (_props: TFiltersProps) => { diff --git a/packages/components/filters/src/filters.tsx b/packages/components/filters/src/filters.tsx index 77565aea44..150dd7fd49 100644 --- a/packages/components/filters/src/filters.tsx +++ b/packages/components/filters/src/filters.tsx @@ -14,7 +14,10 @@ import { FilterIcon, PlusBoldIcon, } from '@commercetools-uikit/icons'; -import SelectInput from '@commercetools-uikit/select-input'; +import SelectInput, { + TOption, + TOptionObject, +} from '@commercetools-uikit/select-input'; import Spacings from '@commercetools-uikit/spacings'; import { useIntl } from 'react-intl'; import { MenuProps, MenuListProps } from 'react-select'; @@ -25,6 +28,13 @@ import FilterMenu, { import messages from './messages'; import { Badge } from './badge'; +type TAddFilterSelectOption = { + value: string; + label: ReactNode; + isDisabled: boolean; +}; + +//TODO: JSDoc annotations of types that are arrays/objects for storybook type TAppliedFilter = { /** * unique identifier for the filter @@ -153,11 +163,19 @@ const menuListStyles = css` gap: ${designTokens.spacing20}; `; +//TODO: better styling and set up scrolling when there are lots of options const CustomSelectMenu = ({ children, innerProps: { ref, ...restInnerProps }, }: MenuProps) => ( -
+ ); @@ -211,24 +229,42 @@ function Filters(props: TFiltersProps) { // TODO: handle options that don't have a group if `filterGroups` is passed const getFilterOptions = () => { + let filterOptions: (TOption | (TOptionObject & { key: string }))[] = []; + // set option groups if (props.filterGroups) { - return props.filterGroups.map((filterGroup) => ({ + filterOptions = props.filterGroups.map((filterGroup) => ({ label: filterGroup.label, - options: props.filters - .filter((filter) => filter.groupKey === filterGroup.key) - .map((filter) => ({ + key: filterGroup.key, + options: [], + })); + } + return props.filters.reduce((filterOptions, filter) => { + //if theres a groupkey and an filterGroups, add option to its group + if (filter.groupKey && props.filterGroups) { + const optionGroup = filterOptions.find( + //@ts-ignore + (option) => option?.key === filter.groupKey + ); + if (optionGroup) { + //@ts-ignore + optionGroup?.options?.push({ value: filter.key, label: filter.label, isDisabled: filter.isDisabled, - })), - })); - } else { - return props.filters.map((filter) => ({ - value: filter.key, - label: filter.label, - isDisabled: filter.isDisabled, - })); - } + }); + return filterOptions; + } + } + // otherwise add option directly + return [ + ...filterOptions, + { + value: filter.key, + label: filter.label, + isDisabled: filter.isDisabled, + }, + ]; + }, filterOptions); }; const removeFilter = (filterKey: string) => @@ -238,9 +274,6 @@ function Filters(props: TFiltersProps) { ) ); - const locallyVisibleRemovableFilterCount = - localVisibleFilters.length - persistedFilterKeys.length; - return ( <> @@ -254,16 +287,15 @@ function Filters(props: TFiltersProps) { icon={} onClick={handleFiltersClick} /> - {locallyVisibleRemovableFilterCount > 1 && !showFilterControls && ( + {props.appliedFilters.length > 1 && !showFilterControls && ( )} -
- +
setShowFilterControls(!showFilterControls)} @@ -333,8 +365,9 @@ function Filters(props: TFiltersProps) { { setLocalVisibleFilters( Array.prototype.concat( @@ -351,14 +384,16 @@ function Filters(props: TFiltersProps) { controlShouldRenderValue={false} isMulti={true} // @ts-ignore - isOptionDisabled={(option: unknown) => option.isDisabled} + isOptionDisabled={(option: TAddFilterSelectOption) => + option.isDisabled + } backspaceRemovesValue={false} isClearable={false} /> - {locallyVisibleRemovableFilterCount > 1 && ( + {props.appliedFilters.length > 1 && ( <>