From 3c2db98183db1498fde26ce815ce6572a61f2467 Mon Sep 17 00:00:00 2001 From: nicolethoen Date: Tue, 3 Jan 2023 11:37:21 -0500 Subject: [PATCH 1/5] fix(DualListSelector): added empty state to composable examples --- .../DualListSelector/DualListSelectorPane.tsx | 16 ++- .../examples/DualListSelector.md | 1 + .../examples/DualListSelectorComposable.tsx | 128 +++++++++++------- .../DualListSelectorComposableTree.tsx | 39 +++++- 4 files changed, 129 insertions(+), 55 deletions(-) diff --git a/packages/react-core/src/components/DualListSelector/DualListSelectorPane.tsx b/packages/react-core/src/components/DualListSelector/DualListSelectorPane.tsx index a633181e03f..e0c3398010a 100644 --- a/packages/react-core/src/components/DualListSelector/DualListSelectorPane.tsx +++ b/packages/react-core/src/components/DualListSelector/DualListSelectorPane.tsx @@ -13,7 +13,7 @@ import { SearchInput } from '../SearchInput'; * such as sorting, can also be passed into this sub-component. */ -export interface DualListSelectorPaneProps { +export interface DualListSelectorPaneProps extends Omit, 'title'> { /** Additional classes applied to the dual list selector pane. */ className?: string; /** A dual list selector list or dual list selector tree to be rendered in the pane. */ @@ -65,6 +65,8 @@ export interface DualListSelectorPaneProps { searchInputAriaLabel?: string; /** @hide Callback for updating the filtered options in DualListSelector. To be used when isSearchable is true. */ onFilterUpdate?: (newFilteredOptions: React.ReactNode[], paneType: string, isSearchReset: boolean) => void; + /** Height of the list of options rendered in the pane. **/ + listHeight?: string; } export const DualListSelectorPane: React.FunctionComponent = ({ @@ -87,6 +89,7 @@ export const DualListSelectorPane: React.FunctionComponent { const [input, setInput] = React.useState(''); @@ -198,12 +201,21 @@ export const DualListSelectorPane: React.FunctionComponent {children} )} {isTree && ( - + {options.length > 0 ? ( { } }; - // builds a search input - used in each dual list selector pane - const buildSearchInput = (isAvailable: boolean) => { - const onChange = (value: string) => { - isAvailable ? setAvailableFilter(value) : setChosenFilter(value); - const toFilter = isAvailable ? [...availableOptions] : [...chosenOptions]; - toFilter.forEach(option => { - option.isVisible = value === '' || option.text.toLowerCase().includes(value.toLowerCase()); - }); - }; - - return ( - onChange('')} - /> - ); + const onFilterChange = (value: string, isAvailable: boolean) => { + isAvailable ? setAvailableFilter(value) : setChosenFilter(value); + const toFilter = isAvailable ? [...availableOptions] : [...chosenOptions]; + toFilter.forEach(option => { + option.isVisible = value === '' || option.text.toLowerCase().includes(value.toLowerCase()); + }); }; + // builds a search input - used in each dual list selector pane + const buildSearchInput = (isAvailable: boolean) => ( + onFilterChange(value, isAvailable)} + onClear={() => onFilterChange('', isAvailable)} + /> + ); + // builds a sort control - passed to both dual list selector panes const buildSort = (isAvailable: boolean) => { const onSort = () => { @@ -141,21 +147,36 @@ export const DualListSelectorComposable: React.FunctionComponent = () => { } options selected`} searchInput={buildSearchInput(true)} actions={[buildSort(true)]} + listHeight="270px" > - - {availableOptions.map((option, index) => - option.isVisible ? ( - onOptionSelect(e, index, false)} - > - {option.text} - - ) : null - )} - + {availableFilter !== '' && availableOptions.filter(option => option.isVisible).length === 0 && ( + + + + No results found + + No results match the filter criteria. Clear all filters and try again. + + + )} + {availableOptions.filter(option => option.isVisible).length > 0 && ( + + {availableOptions.map((option, index) => + option.isVisible ? ( + onOptionSelect(e, index, false)} + > + {option.text} + + ) : null + )} + + )} { searchInput={buildSearchInput(false)} actions={[buildSort(false)]} isChosen + listHeight="270px" > - - {chosenOptions.map((option, index) => - option.isVisible ? ( - onOptionSelect(e, index, true)} - > - {option.text} - - ) : null - )} - + {chosenFilter !== '' && chosenOptions.filter(option => option.isVisible).length === 0 && ( + + + + No results found + + No results match the filter criteria. Clear all filters and try again. + + + )} + {chosenOptions.filter(option => option.isVisible).length > 0 && ( + + {chosenOptions.map((option, index) => + option.isVisible ? ( + onOptionSelect(e, index, true)} + > + {option.text} + + ) : null + )} + + )} ); diff --git a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx index c88ae3a46ee..c3bef7e8c43 100644 --- a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx +++ b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx @@ -7,12 +7,21 @@ import { DualListSelectorControl, DualListSelectorTree, DualListSelectorTreeItemData, - SearchInput + SearchInput, + Title, + Button, + EmptyState, + EmptyStateVariant, + EmptyStateIcon, + EmptyStateBody, + EmptyStatePrimary, + EmptyStateSecondaryActions } from '@patternfly/react-core'; import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon'; import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon'; import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon'; import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon'; +import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; interface FoodNode { id: string; @@ -235,19 +244,35 @@ export const DualListSelectorComposableTree: React.FunctionComponent - - onOptionCheck(e, isChecked, itemData, isChosen)} - /> - + {filterApplied && options.length === 0 && ( + + + + No results found + + No results match the filter criteria. Clear all filters and try again. + + + )} + {options.length > 0 && ( + + onOptionCheck(e, isChecked, itemData, isChosen)} + /> + + )} ); }; From 366b71ddeeb166c9f61cea200ce9593f4a1aebc4 Mon Sep 17 00:00:00 2001 From: nicolethoen Date: Tue, 3 Jan 2023 11:44:50 -0500 Subject: [PATCH 2/5] remove unused imports --- .../DualListSelector/examples/DualListSelectorComposable.tsx | 4 +--- .../examples/DualListSelectorComposableTree.tsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx index 14319ee30b6..23e282e7719 100644 --- a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +++ b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx @@ -13,9 +13,7 @@ import { EmptyState, EmptyStateVariant, EmptyStateIcon, - EmptyStateBody, - EmptyStatePrimary, - EmptyStateSecondaryActions + EmptyStateBody } from '@patternfly/react-core'; import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon'; import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon'; diff --git a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx index c3bef7e8c43..6268eaa7746 100644 --- a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx +++ b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx @@ -13,9 +13,7 @@ import { EmptyState, EmptyStateVariant, EmptyStateIcon, - EmptyStateBody, - EmptyStatePrimary, - EmptyStateSecondaryActions + EmptyStateBody } from '@patternfly/react-core'; import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon'; import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon'; From de4cc0aa90347de8c67cc902f70fb2e32d3028f6 Mon Sep 17 00:00:00 2001 From: nicolethoen Date: Wed, 4 Jan 2023 08:35:02 -0500 Subject: [PATCH 3/5] update per PR comments --- .../DualListSelector/DualListSelectorPane.tsx | 14 +++---- .../examples/DualListSelectorComposable.tsx | 39 ++++++++----------- .../DualListSelectorComposableTree.tsx | 2 +- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/packages/react-core/src/components/DualListSelector/DualListSelectorPane.tsx b/packages/react-core/src/components/DualListSelector/DualListSelectorPane.tsx index e0c3398010a..178de4d296b 100644 --- a/packages/react-core/src/components/DualListSelector/DualListSelectorPane.tsx +++ b/packages/react-core/src/components/DualListSelector/DualListSelectorPane.tsx @@ -65,8 +65,8 @@ export interface DualListSelectorPaneProps extends Omit void; - /** Height of the list of options rendered in the pane. **/ - listHeight?: string; + /** Minimum height of the list of options rendered in the pane. **/ + listMinHeight?: string; } export const DualListSelectorPane: React.FunctionComponent = ({ @@ -89,7 +89,7 @@ export const DualListSelectorPane: React.FunctionComponent { const [input, setInput] = React.useState(''); @@ -201,8 +201,8 @@ export const DualListSelectorPane: React.FunctionComponent {children} @@ -212,8 +212,8 @@ export const DualListSelectorPane: React.FunctionComponent {options.length > 0 ? ( diff --git a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx index 23e282e7719..55feaf74947 100644 --- a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +++ b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx @@ -136,6 +136,19 @@ export const DualListSelectorComposable: React.FunctionComponent = () => { ); }; + const buildEmptyState = (isAvailable: boolean) => ( + + + + No results found + + No results match the filter criteria. Clear all filters and try again. + + + ); + return ( { } options selected`} searchInput={buildSearchInput(true)} actions={[buildSort(true)]} - listHeight="270px" + listMinHeight="270px" > {availableFilter !== '' && availableOptions.filter(option => option.isVisible).length === 0 && ( - - - - No results found - - No results match the filter criteria. Clear all filters and try again. - - + buildEmptyState(true) )} {availableOptions.filter(option => option.isVisible).length > 0 && ( @@ -218,19 +222,10 @@ export const DualListSelectorComposable: React.FunctionComponent = () => { searchInput={buildSearchInput(false)} actions={[buildSort(false)]} isChosen - listHeight="270px" + listMinHeight="270px" > {chosenFilter !== '' && chosenOptions.filter(option => option.isVisible).length === 0 && ( - - - - No results found - - No results match the filter criteria. Clear all filters and try again. - - + buildEmptyState(false) )} {chosenOptions.filter(option => option.isVisible).length > 0 && ( diff --git a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx index 6268eaa7746..96ece9db799 100644 --- a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx +++ b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx @@ -249,7 +249,7 @@ export const DualListSelectorComposableTree: React.FunctionComponent {filterApplied && options.length === 0 && ( From 8dc969c8d2432fb215db0244921aac1ed2010fe8 Mon Sep 17 00:00:00 2001 From: nicolethoen Date: Wed, 4 Jan 2023 22:07:27 -0500 Subject: [PATCH 4/5] attempt to fix linting errors --- .../examples/DualListSelectorComposable.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx index 55feaf74947..97ac6288807 100644 --- a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +++ b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx @@ -160,9 +160,9 @@ export const DualListSelectorComposable: React.FunctionComponent = () => { actions={[buildSort(true)]} listMinHeight="270px" > - {availableFilter !== '' && availableOptions.filter(option => option.isVisible).length === 0 && ( - buildEmptyState(true) - )} + {availableFilter !== '' && + availableOptions.filter(option => option.isVisible).length === 0 && + buildEmptyState(true)} {availableOptions.filter(option => option.isVisible).length > 0 && ( {availableOptions.map((option, index) => @@ -224,9 +224,7 @@ export const DualListSelectorComposable: React.FunctionComponent = () => { isChosen listMinHeight="270px" > - {chosenFilter !== '' && chosenOptions.filter(option => option.isVisible).length === 0 && ( - buildEmptyState(false) - )} + {chosenFilter !== '' && chosenOptions.filter(option => option.isVisible).length === 0 && buildEmptyState(false)} {chosenOptions.filter(option => option.isVisible).length > 0 && ( {chosenOptions.map((option, index) => From 3a49a3ae539c622e20929509907af961da372113 Mon Sep 17 00:00:00 2001 From: nicolethoen Date: Mon, 9 Jan 2023 13:54:20 -0500 Subject: [PATCH 5/5] wrap clear button in EmptyStatePrimary --- .../examples/DualListSelectorComposable.tsx | 15 +++++++++------ .../examples/DualListSelectorComposableTree.tsx | 13 ++++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx index 97ac6288807..08bf7c82752 100644 --- a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +++ b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposable.tsx @@ -13,7 +13,8 @@ import { EmptyState, EmptyStateVariant, EmptyStateIcon, - EmptyStateBody + EmptyStateBody, + EmptyStatePrimary } from '@patternfly/react-core'; import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon'; import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon'; @@ -143,9 +144,11 @@ export const DualListSelectorComposable: React.FunctionComponent = () => { No results found No results match the filter criteria. Clear all filters and try again. - + + + ); @@ -158,7 +161,7 @@ export const DualListSelectorComposable: React.FunctionComponent = () => { } options selected`} searchInput={buildSearchInput(true)} actions={[buildSort(true)]} - listMinHeight="270px" + listMinHeight="300px" > {availableFilter !== '' && availableOptions.filter(option => option.isVisible).length === 0 && @@ -222,7 +225,7 @@ export const DualListSelectorComposable: React.FunctionComponent = () => { searchInput={buildSearchInput(false)} actions={[buildSort(false)]} isChosen - listMinHeight="270px" + listMinHeight="300px" > {chosenFilter !== '' && chosenOptions.filter(option => option.isVisible).length === 0 && buildEmptyState(false)} {chosenOptions.filter(option => option.isVisible).length > 0 && ( diff --git a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx index 96ece9db799..e711d7baa99 100644 --- a/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx +++ b/packages/react-core/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx @@ -13,7 +13,8 @@ import { EmptyState, EmptyStateVariant, EmptyStateIcon, - EmptyStateBody + EmptyStateBody, + EmptyStatePrimary } from '@patternfly/react-core'; import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon'; import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon'; @@ -249,7 +250,7 @@ export const DualListSelectorComposableTree: React.FunctionComponent {filterApplied && options.length === 0 && ( @@ -258,9 +259,11 @@ export const DualListSelectorComposableTree: React.FunctionComponent No results match the filter criteria. Clear all filters and try again. - + + + )} {options.length > 0 && (