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 (
- {
{props?.selectProps?.label} }
- {iconName ?
-
-
-
-
- {children}
- :
- {children}
- }
-
+ return (
+
+ {
{props?.selectProps?.label} }
+ {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}
/>
);