From a8a87eafed362ba70a3e5a11413127927976cc51 Mon Sep 17 00:00:00 2001 From: John Leidy <71462506+j-leidy@users.noreply.github.com> Date: Mon, 17 Jul 2023 16:06:17 -0500 Subject: [PATCH] 451 dropdown value count (#456) * feat(dropdown.tsx): variant Support, show count, show close, show both, style-able, prop-able feat #451 * feat(dropdown.tsx): updated snapshots previous changes include: show value count, show close icon, show both, value count, style-able, prop-able feat #451 * feat(dropdown.tsx): modified logic for displaying count & close icon, made requested changes feat #451 * feat(dropdown.tsx): updated snapshots feat #451 * feat(dropdown.tsx dropdown.stories.tsx): removed showCloseIcon prop, replaced with clearable feat #451 * feat: snapshots --------- Co-authored-by: Oliver Baker --- src/components/Dropdown/Dropdown.stories.tsx | 7 +- src/components/Dropdown/Dropdown.tsx | 78 +++++++++++++++---- .../__snapshots__/Dropdown.test.tsx.snap | 63 +++++++++++++-- 3 files changed, 122 insertions(+), 26 deletions(-) diff --git a/src/components/Dropdown/Dropdown.stories.tsx b/src/components/Dropdown/Dropdown.stories.tsx index 8051d6a38..4ab7bfbed 100644 --- a/src/components/Dropdown/Dropdown.stories.tsx +++ b/src/components/Dropdown/Dropdown.stories.tsx @@ -29,7 +29,7 @@ const generateCityList = (amount: number): OptionProps[] => { return cityOptions; }; -type BasicProps = DropdownProps & { clearable: boolean; numCities: number }; +type BasicProps = DropdownProps & { numCities: number }; export const Basic: Story = ({ clearable, @@ -62,6 +62,7 @@ export const Basic: Story = ({ options={cities} values={values} searchable={searchable} + clearable={clearable} searchFiltersOptions={searchFiltersOptions} onSearchChange={onSearchChange} onDebouncedSearchChange={onDebouncedSearchChange} @@ -74,7 +75,6 @@ Basic.args = { elevation: 0, multi: true, placeholder: 'Choose a city...', - clearable: true, rememberScrollPosition: true, variant: variants.fill, optionsVariant: variants.outline, @@ -129,7 +129,7 @@ const teaOptions = [ }, ]; -type IconsProps = DropdownProps & { clearable: boolean }; +type IconsProps = DropdownProps; export const Icons: Story = ({ onSelect, ...args }: IconsProps) => { const [values, setValues] = useState<(string | number)[] | undefined>(); @@ -152,7 +152,6 @@ Icons.args = { ...Basic.args, multi: false, placeholder: 'Choose a rating...', - clearable: false, color: '#0A7700', valueVariant: variants.text, elevation: 1, diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index 07e0f2051..0b2f8e1fc 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -85,8 +85,9 @@ export const ValueContainer = styled(Button.Container)` // TODO: Don't use explicit height here - this div is ending up larger than the icon otherwise export const CloseIconContainer = styled(StyledBaseDiv)` - height: 1.125em; z-index: 1; + display: flex; + align-items: center; `; export const ArrowIconContainer = styled(StyledBaseDiv)` @@ -231,6 +232,21 @@ const SearchInput = styled(StyledBaseInput)` text-align: left; `; +const ValuesCountContainer = styled(StyledBaseDiv)` + ${({ + variant, + color, + dropdownVariant, + transparentColor, + }: UsefulDropdownState & { dropdownVariant: variants; transparentColor: string }) => { + return ` + ${getDropdownTagStyle(dropdownVariant, variant, color, transparentColor)} + padding: 0.125rem; + border-radius: 1.5rem; + `; + }} +`; + export interface DropdownProps { StyledContainer?: StyledSubcomponentType; StyledValueContainer?: StyledSubcomponentType; @@ -245,6 +261,7 @@ export interface DropdownProps { StyledArrowIconContainer?: StyledSubcomponentType; StyledSearchInput?: StyledSubcomponentType; StyledSearchContainer?: StyledSubcomponentType; + StyledValueCountContainer?: StyledSubcomponentType; containerProps?: SubcomponentPropsType; valueContainerProps?: SubcomponentPropsType; @@ -258,6 +275,7 @@ export interface DropdownProps { arrowIconProps?: SubcomponentPropsType; searchInputProps?: SubcomponentPropsType; searchContainerProps?: SubcomponentPropsType; + valueCountProps?: SubcomponentPropsType; containerRef?: React.RefObject; optionsContainerRef?: React.RefObject; @@ -272,6 +290,7 @@ export interface DropdownProps { arrowIconRef?: React.RefObject; searchContainerRef?: React.RefObject; searchInputRef?: React.RefObject; + valueCountRef?: React.RefObject; color?: string; elevation?: number; @@ -294,6 +313,7 @@ export interface DropdownProps { variant?: variants; optionsVariant?: variants; valueVariant?: variants; + valueCountVariant?: variants; shouldStayInView?: boolean; intersectionThreshold?: number; @@ -301,7 +321,9 @@ export interface DropdownProps { intersectionObserverPrecision?: number; virtualizeOptions?: boolean; + showValueCount?: boolean; showSelectedValues?: boolean; + clearable?: boolean; searchable?: boolean; searchFiltersOptions?: boolean; onSearchChange?: TextInputProps['onChange']; @@ -324,6 +346,7 @@ const Dropdown = ({ StyledArrowIconContainer = ArrowIconContainer, StyledSearchContainer = SearchContainer, StyledSearchInput = SearchInput, + StyledValueCountContainer = ValuesCountContainer, containerProps, valueContainerProps, @@ -337,6 +360,7 @@ const Dropdown = ({ valueItemTagProps = {}, searchInputProps, searchContainerProps, + valueCountProps, containerRef, optionsContainerRef, @@ -351,6 +375,7 @@ const Dropdown = ({ arrowIconRef, searchContainerRef, searchInputRef, + valueCountRef, color, elevation = 0, @@ -368,6 +393,7 @@ const Dropdown = ({ optionsVariant = variants.outline, rememberScrollPosition = true, valueVariant = variants.text, + valueCountVariant = variants.outline, values = [], shouldStayInView = true, intersectionThreshold = 1.0, @@ -375,7 +401,9 @@ const Dropdown = ({ intersectionObserverPrecision = 100, virtualizeOptions = true, + showValueCount = false, showSelectedValues = true, + clearable = true, searchable = false, searchFiltersOptions = true, onSearchChange = defaultCallback, @@ -786,20 +814,40 @@ const Dropdown = ({ [rememberScrollPosition], ); - const closeIcons = ( + const valueCountCloseIconHandler = () => { + return ( + <> + {showValueCount && ( + + {values.length} + + )} + {clearable && ( + e.stopPropagation()} + onClick={handleClear} + onFocus={(e: React.FocusEvent) => e.stopPropagation()} + tabIndex={tabIndex} + ref={closeIconRef} + {...closeIconProps} + > + + + )} + + ); + }; + + const infoIcons = ( <> - {handleClear && values.length > 0 && ( - e.stopPropagation()} - onClick={handleClear} - onFocus={(e: React.FocusEvent) => e.stopPropagation()} - tabIndex={tabIndex} - ref={closeIconRef} - {...closeIconProps} - > - - - )} + {values.length > 0 && valueCountCloseIconHandler()} @@ -959,7 +1007,7 @@ const Dropdown = ({ : placeholder} )} - {closeIcons} + {infoIcons} {isOpen && ( <> diff --git a/src/components/Dropdown/__tests__/__snapshots__/Dropdown.test.tsx.snap b/src/components/Dropdown/__tests__/__snapshots__/Dropdown.test.tsx.snap index ef35f1fca..8e676d88e 100644 --- a/src/components/Dropdown/__tests__/__snapshots__/Dropdown.test.tsx.snap +++ b/src/components/Dropdown/__tests__/__snapshots__/Dropdown.test.tsx.snap @@ -3079,8 +3079,15 @@ exports[`Dropdown renders a value when given a matching option id through props } .c7 { - height: 1.125em; z-index: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c8 { @@ -3317,8 +3324,15 @@ exports[`Dropdown renders no value when given a value that doesn't match any opt } .c5 { - height: 1.125em; z-index: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c6 { @@ -3557,8 +3571,15 @@ exports[`Dropdown renders one value when given two values but only one matches a } .c7 { - height: 1.125em; z-index: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c8 { @@ -3816,8 +3837,15 @@ exports[`Dropdown renders two values when given matching option ids through prop } .c7 { - height: 1.125em; z-index: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c8 { @@ -4083,8 +4111,15 @@ exports[`Dropdown renders two values when given matching option ids through prop } .c7 { - height: 1.125em; z-index: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c8 { @@ -4350,8 +4385,15 @@ exports[`Dropdown selects options from values prop 1`] = ` } .c7 { - height: 1.125em; z-index: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c8 { @@ -4617,8 +4659,15 @@ exports[`Dropdown selects options from values prop 2`] = ` } .c7 { - height: 1.125em; z-index: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c8 {