From 3bc9a838e01ca9b6a000c166b09e463b50f9fb84 Mon Sep 17 00:00:00 2001 From: Anna Llorens Date: Tue, 2 Feb 2021 17:26:54 +0100 Subject: [PATCH 1/3] APP-3784 Add filtering --- package.json | 1 + src/components/dropdown/CustomRender.tsx | 164 +++++++++++++++-------- src/components/dropdown/Dropdown.tsx | 127 +++++++++++++----- stories/Dropdown.stories.tsx | 37 ++--- yarn.lock | 49 ++++++- 5 files changed, 269 insertions(+), 109 deletions(-) diff --git a/package.json b/package.json index 1440f31b..5cea2389 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "@types/lodash": "^4.14.154", "@types/react": "^16.9.35", "@types/react-dom": "^16.9.8", + "@types/react-select": "^4.0.11", "@types/react-virtualized": "^9.21.10", "@types/storybook__react": "^5.2.1", "@types/styled-components": "^5.1.0", diff --git a/src/components/dropdown/CustomRender.tsx b/src/components/dropdown/CustomRender.tsx index ff4e07a7..4d39efa3 100644 --- a/src/components/dropdown/CustomRender.tsx +++ b/src/components/dropdown/CustomRender.tsx @@ -10,70 +10,106 @@ import Icon from '../icon'; const stopPropagation = (e) => { e.preventDefault(); e.stopPropagation(); -} +}; -/** The following components are defined to override +/** The following components are defined to override * the appereace of the react-select library components **/ -export const DefaultOptionRenderer = (props:any) => { +export const DefaultOptionRenderer = (props: any) => { const OptionRenderer = props?.selectProps?.optionRenderer; - const rendererProps = {data: props.data} - return (
{OptionRenderer ? - - - : }
+ const rendererProps = { data: props.data }; + return ( +
+ {OptionRenderer ? ( + + + + ) : ( + + )} +
); }; -export const SingleValue = (props:any) => { +export const SingleValue = (props: any) => { const OptionRenderer = props.selectProps.optionRenderer; - const rendererProps = {data: props.data} - return (
{OptionRenderer ? - - - : }
+ const rendererProps = { data: props.data }; + return ( +
+ {OptionRenderer ? ( + + + + ) : ( + + )} +
); }; -export const DefaultTagRenderer = (props:any) => { +export const DefaultTagRenderer = (props: any) => { const TagRender = props.selectProps?.tagRenderer; - const rendererProps = {remove: props.removeProps.onClick, data:props.data}; - return (
{TagRender ? - -
- -
-
- : -
- {props.data?.label} - props.removeProps.onClick()}/> -
-
}
+ const rendererProps = { remove: props.removeProps.onClick, data: props.data }; + return ( +
+ {TagRender ? ( + +
+ +
+
+ ) : ( + +
+ {props.data?.label} + props.removeProps.onClick()} + /> +
+
+ )} +
); -} +}; -export const MultiValueContainerOverride = ({ children, ...props }:any) => { +export const MultiValueContainerOverride = ({ children, ...props }: any) => { return (
{children}
); -} +}; -export const MultiValueRemove = ()=>{return null}; +export const MultiValueRemove = () => { + return null; +}; -export const DropdownIndicator = (props:any) => { - return (
{props?.selectProps?.isMulti ? - (
{props?.selectProps?.isMulti && props?.selectProps?.displayArrowIndicator ? - : }
) : - - - }
+export const DropdownIndicator = (props: any) => { + return ( +
+ {props?.selectProps?.isMulti ? ( +
+ {props?.selectProps?.isMulti && + props?.selectProps?.displayArrowIndicator ? ( + + ) : ( + + )} +
+ ) : ( + + + + )} +
); }; -export const ClearIndicator = (props:any) => { +export const ClearIndicator = (props: any) => { return ( @@ -81,22 +117,36 @@ export const ClearIndicator = (props:any) => { ); }; -export const Control = ({ children, ...props }:any) => { +export const Control = ({ children, ...props }: any) => { const iconName = props.selectProps.iconName; - return (
- {} - {iconName ? - -
- -
- {children} -
: - {children} - } -
+ return ( +
+ {} + {iconName ? ( + +
+ +
+ {children} +
+ ) : ( + {children} + )} +
); -} +}; + +export const NoOptionsMessage = (props: any) => { + const noOptionMessage = props.selectProps?.noOptionMessage; + return ( +
+ {noOptionMessage ? ( + +
{noOptionMessage}
+
+ ) : ( + + )} +
+ ); +}; diff --git a/src/components/dropdown/Dropdown.tsx b/src/components/dropdown/Dropdown.tsx index 8ea58fa5..71f3eac7 100644 --- a/src/components/dropdown/Dropdown.tsx +++ b/src/components/dropdown/Dropdown.tsx @@ -1,7 +1,23 @@ import * as React from 'react'; import Select from 'react-select'; -import { ClearIndicator, Control, DefaultOptionRenderer, DefaultTagRenderer, DropdownIndicator, MultiValueContainerOverride, MultiValueRemove, SingleValue } from './CustomRender'; -import { DropdownOption, LabelValue, OptionRendererProps, TagRendererProps } from './interfaces'; +import { Option } from 'react-select/src/filters'; +import { + ClearIndicator, + Control, + DefaultOptionRenderer, + DefaultTagRenderer, + DropdownIndicator, + MultiValueContainerOverride, + MultiValueRemove, + SingleValue, + NoOptionsMessage, +} from './CustomRender'; +import { + DropdownOption, + LabelValue, + OptionRendererProps, + TagRendererProps, +} from './interfaces'; // css baseclass prefix const prefix = 'tk-select'; @@ -14,59 +30,95 @@ export type DropdownProps = { isDisabled?: boolean; id?: string; placeHolder?: string; - label?: string - onBlur?: (e)=>any; + label?: string; + onBlur?: (e) => any; className?: string; /** Used to override the default appearance of the list items. */ - optionRenderer?: React.Component, any> | React.FunctionComponent>; + optionRenderer?: + | React.Component, any> + | React.FunctionComponent>; /** Used to override the default appearance of the dropdown select input item/s */ - tagRenderer?: React.Component, any> | React.FunctionComponent>; + tagRenderer?: + | React.Component, any> + | React.FunctionComponent>; /* It renders an icon on the left side of the dropdown input*/ iconName?: string; /** Close the expanded menu when the user selects an option */ closeMenuOnSelect?: boolean; /** Hide the selected option from the list */ hideSelectedOptions?: boolean; - /** Enables the indicator to fully clear the selected content */ + /** Is the select value clearable */ isInputClearable?: boolean; /** Allows the usage of the component in controlled value mode */ - value?: T + value?: T; + /** Decides if an item with data and current input value should be displayed in dropdown menu or not **/ + filterFunction?: (data: Option, inputValue: string) => boolean; + /** Mesage to display if there isn't any match in the search input */ + noOptionMessage?: string; } & (OnChangeMultiProps | OnChangeSingleProps); type OnChangeMultiProps = { - isMultiSelect:true; - onChange?: (value:T[])=>any; -} + /** Support multiple selected options */ + isMultiSelect: true; + onChange?: (value: T[]) => any; +}; type OnChangeSingleProps = { - isMultiSelect?:false; - onChange?: (value:T)=>any; -} + isMultiSelect?: false; + onChange?: (value: T) => any; +}; type DropdownState = { selectedOption: T; closeMenuOnSelect?: boolean; hideSelectedOptions?: boolean; displayArrowIndicator?: boolean; -} - -class Dropdown extends React.Component, DropdownState> { +}; +class Dropdown extends React.Component< + DropdownProps, + DropdownState +> { state = { selectedOption: null, - hideSelectedOptions: (this.props?.isMultiSelect || !!this.props?.hideSelectedOptions), - closeMenuOnSelect: !!(!this.props?.isMultiSelect || this.props?.closeMenuOnSelect), - displayArrowIndicator: !!(!this.props?.isMultiSelect || this.props?.displayArrowIndicator) + hideSelectedOptions: + this.props?.isMultiSelect || !!this.props?.hideSelectedOptions, + closeMenuOnSelect: !!( + !this.props?.isMultiSelect || this.props?.closeMenuOnSelect + ), + displayArrowIndicator: !!( + !this.props?.isMultiSelect || this.props?.displayArrowIndicator + ), }; handleChange = (selectedOption) => { - if(this.props.onChange){ + if (this.props.onChange) { this.props.onChange(selectedOption); } }; render() { - const { hideSelectedOptions, closeMenuOnSelect, displayArrowIndicator } = this.state; - const { isMultiSelect, isDisabled, placeHolder, options, id, defaultValue, onBlur, isInputClearable, label, optionRenderer, iconName,tagRenderer, value} = this.props; + const { + hideSelectedOptions, + closeMenuOnSelect, + displayArrowIndicator, + } = this.state; + const { + isMultiSelect, + isDisabled, + placeHolder, + options, + id, + defaultValue, + onBlur, + isInputClearable, + label, + optionRenderer, + iconName, + tagRenderer, + value, + filterFunction, + noOptionMessage, + } = this.props; return (
@@ -76,15 +128,16 @@ class Dropdown extends React.Component, DropdownS tagRenderer={tagRenderer} isClearable={isInputClearable} label={label} - components={{ - DropdownIndicator, - Control, - SingleValue, - Option: DefaultOptionRenderer, + components={{ + DropdownIndicator, + Control, + SingleValue, + Option: DefaultOptionRenderer, MultiValueContainer: MultiValueContainerOverride, - MultiValue: DefaultTagRenderer, - ClearIndicator, - MultiValueRemove + MultiValue: DefaultTagRenderer, + ClearIndicator, + MultiValueRemove, + NoOptionsMessage }} defaultValue={defaultValue} id={id} @@ -100,16 +153,20 @@ class Dropdown extends React.Component, DropdownS isMulti={isMultiSelect} isDisabled={isDisabled} iconName={iconName} + filterOption={filterFunction} + noOptionMessage={noOptionMessage} + isSearchable + menuIsOpen />
); } - + static defaultProps = { isDisabled: false, isMultiSelect: false, - isInputClearable: false - } + isInputClearable: false, + }; } -export default Dropdown; \ No newline at end of file +export default Dropdown; diff --git a/stories/Dropdown.stories.tsx b/stories/Dropdown.stories.tsx index 41a09fa1..16b62d7e 100644 --- a/stories/Dropdown.stories.tsx +++ b/stories/Dropdown.stories.tsx @@ -48,29 +48,29 @@ const timeZoneOptions: DropdownOption[] = [ /** Icon custom renderers */ interface Icon { - displayName:string; + label:string; value:string; } const iconData: Icon[] = [ - { value: '1', displayName: 'app' }, - { value: '2', displayName: 'bot' }, - { value: '9', displayName: 'hide' }, - { value: '10', displayName: 'link' }, - { value: '3', displayName: 'adjust' }, - { value: '4', displayName: 'archive' }, - { value: '5', displayName: 'cashtag' }, - { value: '6', displayName: 'emoticon' }, - { value: '7', displayName: 'following' }, - { value: '8', displayName: 'flags' } + { value: '1', label: 'app' }, + { value: '2', label: 'bot' }, + { value: '9', label: 'hide' }, + { value: '10', label: 'link' }, + { value: '3', label: 'adjust' }, + { value: '4', label: 'archive' }, + { value: '5', label: 'cashtag' }, + { value: '6', label: 'emoticon' }, + { value: '7', label: 'following' }, + { value: '8', label: 'flags' } ]; const IconPickerTagRenderer = (props: TagRendererProps) => { const {data, remove} = props; return (
- {data.displayName} - + {data.label} +
); @@ -80,8 +80,8 @@ const IconPickerOptionRenderer = (props: OptionRendererProps) => { const {data} = props; return (
- {data.displayName} - + {data.label} +
); }; @@ -116,12 +116,19 @@ export const Select: React.FC = () => ( Clear selection with isClearable

+ +

+ With noOptionMessage +

+ +

Disabled dropdown

With label

+

Custom render

You can replace the default components with your own, using the optionRenderer and{' '} diff --git a/yarn.lock b/yarn.lock index 51076070..21aba489 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1148,7 +1148,7 @@ "@emotion/utils" "0.11.3" babel-plugin-emotion "^10.0.27" -"@emotion/hash@0.8.0": +"@emotion/hash@0.8.0", "@emotion/hash@^0.8.0": version "0.8.0" resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha1-u7/2iXj+/b5ozLUzvIy+HRr7VBM= @@ -1165,6 +1165,11 @@ resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" integrity sha1-Gb8PWvGRSREcQNmLsM+CEZ9dnus= +"@emotion/memoize@^0.7.4": + version "0.7.5" + resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50" + integrity sha1-LED4FEmk5VTp/GOWkQ7UhD7CvlA= + "@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16": version "0.11.16" resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad" @@ -1176,6 +1181,17 @@ "@emotion/utils" "0.11.3" csstype "^2.5.7" +"@emotion/serialize@^1.0.0": + version "1.0.0" + resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/serialize/-/serialize-1.0.0.tgz#1a61f4f037cf39995c97fc80ebe99abc7b191ca9" + integrity sha1-GmH08DfPOZlcl/yA6+mavHsZHKk= + dependencies: + "@emotion/hash" "^0.8.0" + "@emotion/memoize" "^0.7.4" + "@emotion/unitless" "^0.7.5" + "@emotion/utils" "^1.0.0" + csstype "^3.0.2" + "@emotion/sheet@0.9.4": version "0.9.4" resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" @@ -1204,7 +1220,7 @@ resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" integrity sha1-3qyzib1u530ef8rMzp4WxcfnjgQ= -"@emotion/unitless@0.7.5", "@emotion/unitless@^0.7.4": +"@emotion/unitless@0.7.5", "@emotion/unitless@^0.7.4", "@emotion/unitless@^0.7.5": version "0.7.5" resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha1-dyESkcGQCnALinjPr9oxYNdpSe0= @@ -1214,6 +1230,11 @@ resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924" integrity sha1-p1mGOGe++n5YNADTImUqP0SCCSQ= +"@emotion/utils@^1.0.0": + version "1.0.0" + resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/utils/-/utils-1.0.0.tgz#abe06a83160b10570816c913990245813a2fd6af" + integrity sha1-q+BqgxYLEFcIFskTmQJFgTov1q8= + "@emotion/weak-memoize@0.2.5": version "0.2.5" resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" @@ -2719,6 +2740,13 @@ "@types/react" "*" "@types/reactcss" "*" +"@types/react-dom@*": + version "17.0.0" + resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@types/react-dom/-/react-dom-17.0.0.tgz#b3b691eb956c4b3401777ee67b900cb28415d95a" + integrity sha1-s7aR65VsSzQBd37me5AMsoQV2Vo= + dependencies: + "@types/react" "*" + "@types/react-dom@^16.9.8": version "16.9.9" resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@types/react-dom/-/react-dom-16.9.9.tgz#d2d0a6f720a0206369ccbefff752ba37b9583136" @@ -2733,6 +2761,16 @@ dependencies: "@types/react" "*" +"@types/react-select@^4.0.11": + version "4.0.11" + resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@types/react-select/-/react-select-4.0.11.tgz#9a9079f52775c7cb63e8db5f095360400ce47933" + integrity sha1-mpB59Sd1x8tj6NtfCVNgQAzkeTM= + dependencies: + "@emotion/serialize" "^1.0.0" + "@types/react" "*" + "@types/react-dom" "*" + "@types/react-transition-group" "*" + "@types/react-syntax-highlighter@11.0.4": version "11.0.4" resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd" @@ -2740,6 +2778,13 @@ dependencies: "@types/react" "*" +"@types/react-transition-group@*": + version "4.4.0" + resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d" + integrity sha1-iCg520Zd8TIOR1Pm6fcMp+m01G0= + dependencies: + "@types/react" "*" + "@types/react-virtualized@^9.21.10": version "9.21.10" resolved "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/@types/react-virtualized/-/react-virtualized-9.21.10.tgz#cd072dc9c889291ace2c4c9de8e8c050da8738b7" From 23d7b84ae3ed33fb3e1a437deab83d2b6d981ba5 Mon Sep 17 00:00:00 2001 From: Anna Llorens Date: Wed, 3 Feb 2021 17:23:33 +0100 Subject: [PATCH 2/3] APP-3784 Improve storybook dropdown --- spec/components/dropdown/Dropdown.spec.tsx | 5 +- src/components/dropdown/CustomRender.tsx | 2 +- src/components/dropdown/Dropdown.tsx | 66 +++++++---- stories/Card.stories.tsx | 2 +- stories/Dropdown.stories.tsx | 131 +++++++++------------ 5 files changed, 100 insertions(+), 106 deletions(-) diff --git a/spec/components/dropdown/Dropdown.spec.tsx b/spec/components/dropdown/Dropdown.spec.tsx index 2aca6908..3992e5a2 100644 --- a/spec/components/dropdown/Dropdown.spec.tsx +++ b/spec/components/dropdown/Dropdown.spec.tsx @@ -9,6 +9,7 @@ const CustomComponent = (props) => { return (

{props?.data?.label}
); } } + describe('Dropdown component test suite =>', () => { const dropdownProps = { options: [], @@ -35,12 +36,14 @@ describe('Dropdown component test suite =>', () => { }); it('should select first option', async () => { - const { getByText } = render(); + const { getByText } = render(); const input = screen.getByRole('textbox'); userEvent.click(input); const option = screen.getByText('banana'); userEvent.click(option); expect(getByText('banana')).toBeTruthy(); + userEvent.type(input, 'zz'); + expect(screen.getByText('no options message')).toBeTruthy(); }); it('should render costum render dropdown', async () => { diff --git a/src/components/dropdown/CustomRender.tsx b/src/components/dropdown/CustomRender.tsx index 4d39efa3..9fa6a7cd 100644 --- a/src/components/dropdown/CustomRender.tsx +++ b/src/components/dropdown/CustomRender.tsx @@ -17,7 +17,7 @@ const stopPropagation = (e) => { export const DefaultOptionRenderer = (props: any) => { const OptionRenderer = props?.selectProps?.optionRenderer; - const rendererProps = { data: props.data }; + const rendererProps = { data: props.data, inputValue: props.selectProps?.inputValue }; return (
{OptionRenderer ? ( diff --git a/src/components/dropdown/Dropdown.tsx b/src/components/dropdown/Dropdown.tsx index 71f3eac7..e6ccdc5f 100644 --- a/src/components/dropdown/Dropdown.tsx +++ b/src/components/dropdown/Dropdown.tsx @@ -23,43 +23,54 @@ import { const prefix = 'tk-select'; export type DropdownProps = { + /** Array of options that populate the dropdown menu */ options: DropdownOption[]; - defaultValue?: T; - /** Enables the indicator to expand the Dropdown */ - displayArrowIndicator?: boolean; - isDisabled?: boolean; - id?: string; - placeHolder?: string; - label?: string; - onBlur?: (e) => any; - className?: string; - /** Used to override the default appearance of the list items. */ + /** Custom component used to override the default appearance of the list items. */ optionRenderer?: - | React.Component, any> - | React.FunctionComponent>; - /** Used to override the default appearance of the dropdown select input item/s */ + | React.Component, any> + | React.FunctionComponent>; + /** Custom component used to override the default appearance of the dropdown select input item/s */ tagRenderer?: | React.Component, any> | React.FunctionComponent>; - /* It renders an icon on the left side of the dropdown input*/ + /** Handle blur events on the control */ + onBlur?: (e) => any; + /** Decides if an item with data and current input value should be displayed in dropdown menu or not */ + filterFunction?: (data: T, inputValue: string) => boolean; + /** If provided, it renders an icon on the left side of the dropdown input*/ iconName?: string; + /** Allows the usage of the component in controlled value mode */ + value?: T; + /** Mesage to display if there isn't any match in the search input */ + noOptionMessage?: string; + /** Is the dropdown disabled */ + isDisabled?: boolean; + /** Placeholder text for the dropdown */ + placeHolder?: string; + /** Label text for the dropdown */ + label?: string; + /** If false, user can not type on the control Input */ + isTypeAheadEnabled?: boolean; + /** Default value selected on the Dropdown */ + defaultValue?: T; + /** Enables the indicator to expand the Dropdown */ + displayArrowIndicator?: boolean; + /** Default value selected on the Dropdown */ + id?: string; + /** Optional CSS class name */ + className?: string; /** Close the expanded menu when the user selects an option */ closeMenuOnSelect?: boolean; /** Hide the selected option from the list */ hideSelectedOptions?: boolean; - /** Is the select value clearable */ + /** Is the select value clearable */ isInputClearable?: boolean; - /** Allows the usage of the component in controlled value mode */ - value?: T; - /** Decides if an item with data and current input value should be displayed in dropdown menu or not **/ - filterFunction?: (data: Option, inputValue: string) => boolean; - /** Mesage to display if there isn't any match in the search input */ - noOptionMessage?: string; } & (OnChangeMultiProps | OnChangeSingleProps); type OnChangeMultiProps = { /** Support multiple selected options */ isMultiSelect: true; + /** Handle change events on the Dropdown */ onChange?: (value: T[]) => any; }; type OnChangeSingleProps = { @@ -96,6 +107,11 @@ class Dropdown extends React.Component< } }; + handleFiltering = (this.props.filterFunction) ? + (option: Option, input:string) => { + return this.props.filterFunction(option.data, input); + } : undefined; + render() { const { hideSelectedOptions, @@ -116,8 +132,8 @@ class Dropdown extends React.Component< iconName, tagRenderer, value, - filterFunction, noOptionMessage, + isTypeAheadEnabled: isSearchable } = this.props; return ( @@ -153,10 +169,9 @@ class Dropdown extends React.Component< isMulti={isMultiSelect} isDisabled={isDisabled} iconName={iconName} - filterOption={filterFunction} noOptionMessage={noOptionMessage} - isSearchable - menuIsOpen + filterOption={this.handleFiltering} + isSearchable={isSearchable} />
); @@ -166,6 +181,7 @@ class Dropdown extends React.Component< isDisabled: false, isMultiSelect: false, isInputClearable: false, + isTypeAheadEnabled: true, }; } diff --git a/stories/Card.stories.tsx b/stories/Card.stories.tsx index 9d81a0a4..0f707c32 100644 --- a/stories/Card.stories.tsx +++ b/stories/Card.stories.tsx @@ -28,5 +28,5 @@ Content.args = { }; export default { - title: 'Components/Card', + title: 'Components/Containers/Card', }; \ No newline at end of file diff --git a/stories/Dropdown.stories.tsx b/stories/Dropdown.stories.tsx index 16b62d7e..145039c8 100644 --- a/stories/Dropdown.stories.tsx +++ b/stories/Dropdown.stories.tsx @@ -4,11 +4,6 @@ import { Dropdown, DropdownOption, Icon, LabelValue, OptionRendererProps, TagRen const defaultOptions: LabelValue[] = [ { label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }, - { label: 'Option 3', value: '3' }, - { label: 'Option 4', value: '4' }, - { label: 'Option 5', value: '5' }, - { label: 'Option 6', value: '6' }, - { label: 'Option 7', value: '7' } ]; interface Person { @@ -48,29 +43,29 @@ const timeZoneOptions: DropdownOption[] = [ /** Icon custom renderers */ interface Icon { - label:string; + displayName:string; value:string; } -const iconData: Icon[] = [ - { value: '1', label: 'app' }, - { value: '2', label: 'bot' }, - { value: '9', label: 'hide' }, - { value: '10', label: 'link' }, - { value: '3', label: 'adjust' }, - { value: '4', label: 'archive' }, - { value: '5', label: 'cashtag' }, - { value: '6', label: 'emoticon' }, - { value: '7', label: 'following' }, - { value: '8', label: 'flags' } +const iconData: DropdownOption[] = [ + { value: '1', displayName: 'app' }, + { value: '2', displayName: 'bot' }, + { value: '9', displayName: 'hide' }, + { value: '10', displayName: 'link' }, + { value: '3', displayName: 'adjust' }, + { value: '4', displayName: 'archive' }, + { value: '5', displayName: 'cashtag' }, + { value: '6', displayName: 'emoticon' }, + { value: '7', displayName: 'following' }, + { value: '8', displayName: 'flags' } ]; const IconPickerTagRenderer = (props: TagRendererProps) => { const {data, remove} = props; return (
- {data.label} - + {data.displayName} +
); @@ -80,15 +75,21 @@ const IconPickerOptionRenderer = (props: OptionRendererProps) => { const {data} = props; return (
- {data.label} - + {data.displayName} +
); }; + +const filterFunction = (icon: Icon, input: string) => { + return !input || icon.displayName.indexOf(input)>-1 ; +}; + + const Template = (args) => { return ( -
+
); @@ -101,91 +102,65 @@ Default.args = { export const Select: React.FC = () => (
-

Dropdown

-

Default

- {console.info(value)}}/> +

Let`s have a look on the different props than can be used to render the dropdown:

- With placeholder + With placeholder:

- +

- With label + With label:

- Clear selection with isClearable + Clear selection with isClearable:

- With noOptionMessage + With noOptionMessage customize the message that the dropdown will display when does not found any item on the list:

- - -

Disabled dropdown

- +

- With label + With isDisabled:

+

+ With iconName displays the specified icon on the left side of the dropdown: +

+ +

Grouped option list

+ +

MultiSelect

+

The Dropdown component can handle multiple selections. It is enabled with the isMultiSelect prop:

+ -

Custom render

+

Customized selects

- You can replace the default components with your own, using the optionRenderer and{' '} - tagRenderer props. + You can easily customize the appearance of the UIToolkit Dropdown and render your own components.

+

With optionRenderer prop you can customize the rendering of the option list:

-
-); - -export const Multiselect: React.FC = () => ( -
-

Multiselect

-

Default

- -

- With label -

- -

- Clear selection with isClearable -

- -

- with displayArrowIndicator -

- -

Disabled dropdown

- +

With tagRenderer prop you can customize the rendering of the selected item/s:

- -

Custom render

-

- You can replace the default components with your own, using the - optionRenderer and - tagRenderer props. -

+

Custom Filter logic

+

If you would like to rewrite the filtration logic from the ground up, simply declare a new filterFunction to be passed in as a prop:

console.info('SELECTED VALUE',value)} + placeHolder="Select an icon.." + filterFunction={filterFunction} />
); From bd8110bb76ae6c88d477bffc464ef7132a9024a1 Mon Sep 17 00:00:00 2001 From: Anna Llorens Date: Thu, 4 Feb 2021 19:30:28 +0100 Subject: [PATCH 3/3] APP-3784 Fix formatter --- src/components/dropdown/Dropdown.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/dropdown/Dropdown.tsx b/src/components/dropdown/Dropdown.tsx index e6ccdc5f..6622a4ca 100644 --- a/src/components/dropdown/Dropdown.tsx +++ b/src/components/dropdown/Dropdown.tsx @@ -27,8 +27,8 @@ export type DropdownProps = { options: DropdownOption[]; /** Custom component used to override the default appearance of the list items. */ optionRenderer?: - | React.Component, any> - | React.FunctionComponent>; + | React.Component, any> + | React.FunctionComponent>; /** Custom component used to override the default appearance of the dropdown select input item/s */ tagRenderer?: | React.Component, any> @@ -55,7 +55,7 @@ export type DropdownProps = { defaultValue?: T; /** Enables the indicator to expand the Dropdown */ displayArrowIndicator?: boolean; - /** Default value selected on the Dropdown */ + /** Default value selected on the Dropdown */ id?: string; /** Optional CSS class name */ className?: string; @@ -70,7 +70,7 @@ export type DropdownProps = { type OnChangeMultiProps = { /** Support multiple selected options */ isMultiSelect: true; - /** Handle change events on the Dropdown */ + /** Handle change events on the Dropdown */ onChange?: (value: T[]) => any; }; type OnChangeSingleProps = { @@ -107,10 +107,11 @@ class Dropdown extends React.Component< } }; - handleFiltering = (this.props.filterFunction) ? - (option: Option, input:string) => { + handleFiltering = this.props.filterFunction + ? (option: Option, input: string) => { return this.props.filterFunction(option.data, input); - } : undefined; + } + : undefined; render() { const { @@ -133,7 +134,7 @@ class Dropdown extends React.Component< tagRenderer, value, noOptionMessage, - isTypeAheadEnabled: isSearchable + isTypeAheadEnabled, } = this.props; return ( @@ -153,7 +154,7 @@ class Dropdown extends React.Component< MultiValue: DefaultTagRenderer, ClearIndicator, MultiValueRemove, - NoOptionsMessage + NoOptionsMessage, }} defaultValue={defaultValue} id={id} @@ -171,7 +172,7 @@ class Dropdown extends React.Component< iconName={iconName} noOptionMessage={noOptionMessage} filterOption={this.handleFiltering} - isSearchable={isSearchable} + isSearchable={isTypeAheadEnabled} />
);