@patternfly/react-core 4.271.0 → 4.271.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/components/DualListSelector/DualListSelectorPane.d.ts +3 -1
- package/components/DualListSelector/DualListSelectorPane.d.ts.map +1 -1
- package/components/DualListSelector/DualListSelectorPane.js +7 -3
- package/components/DualListSelector/DualListSelectorPane.js.map +1 -1
- package/dist/esm/components/DualListSelector/DualListSelectorPane.d.ts +3 -1
- package/dist/esm/components/DualListSelector/DualListSelectorPane.d.ts.map +1 -1
- package/dist/esm/components/DualListSelector/DualListSelectorPane.js +7 -3
- package/dist/esm/components/DualListSelector/DualListSelectorPane.js.map +1 -1
- package/dist/js/components/DualListSelector/DualListSelectorPane.d.ts +3 -1
- package/dist/js/components/DualListSelector/DualListSelectorPane.d.ts.map +1 -1
- package/dist/js/components/DualListSelector/DualListSelectorPane.js +7 -3
- package/dist/js/components/DualListSelector/DualListSelectorPane.js.map +1 -1
- package/dist/umd/react-core.min.js +1 -1
- package/package.json +2 -2
- package/src/components/DualListSelector/DualListSelectorPane.tsx +14 -2
- package/src/components/DualListSelector/examples/DualListSelector.md +1 -0
- package/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +76 -46
- package/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx +33 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "4.271.
|
|
3
|
+
"version": "4.271.1",
|
|
4
4
|
"description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
|
|
5
5
|
"main": "dist/js/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"react": "^16.8 || ^17 || ^18",
|
|
72
72
|
"react-dom": "^16.8 || ^17 || ^18"
|
|
73
73
|
},
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "541f76a7ca04fc3206d75f13648dda8cc92c9e15"
|
|
75
75
|
}
|
|
@@ -13,7 +13,7 @@ import { SearchInput } from '../SearchInput';
|
|
|
13
13
|
* such as sorting, can also be passed into this sub-component.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
export interface DualListSelectorPaneProps {
|
|
16
|
+
export interface DualListSelectorPaneProps extends Omit<React.HTMLProps<HTMLDivElement>, 'title'> {
|
|
17
17
|
/** Additional classes applied to the dual list selector pane. */
|
|
18
18
|
className?: string;
|
|
19
19
|
/** A dual list selector list or dual list selector tree to be rendered in the pane. */
|
|
@@ -65,6 +65,8 @@ export interface DualListSelectorPaneProps {
|
|
|
65
65
|
searchInputAriaLabel?: string;
|
|
66
66
|
/** @hide Callback for updating the filtered options in DualListSelector. To be used when isSearchable is true. */
|
|
67
67
|
onFilterUpdate?: (newFilteredOptions: React.ReactNode[], paneType: string, isSearchReset: boolean) => void;
|
|
68
|
+
/** Minimum height of the list of options rendered in the pane. **/
|
|
69
|
+
listMinHeight?: string;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
export const DualListSelectorPane: React.FunctionComponent<DualListSelectorPaneProps> = ({
|
|
@@ -87,6 +89,7 @@ export const DualListSelectorPane: React.FunctionComponent<DualListSelectorPaneP
|
|
|
87
89
|
filterOption,
|
|
88
90
|
id = getUniqueId('dual-list-selector-pane'),
|
|
89
91
|
isDisabled = false,
|
|
92
|
+
listMinHeight,
|
|
90
93
|
...props
|
|
91
94
|
}: DualListSelectorPaneProps) => {
|
|
92
95
|
const [input, setInput] = React.useState('');
|
|
@@ -198,12 +201,21 @@ export const DualListSelectorPane: React.FunctionComponent<DualListSelectorPaneP
|
|
|
198
201
|
displayOption={displayOption}
|
|
199
202
|
id={`${id}-list`}
|
|
200
203
|
isDisabled={isDisabled}
|
|
204
|
+
{...(listMinHeight && {
|
|
205
|
+
style: { '--pf-c-dual-list-selector__menu--MinHeight': listMinHeight } as React.CSSProperties
|
|
206
|
+
})}
|
|
201
207
|
>
|
|
202
208
|
{children}
|
|
203
209
|
</DualListSelectorListWrapper>
|
|
204
210
|
)}
|
|
205
211
|
{isTree && (
|
|
206
|
-
<DualListSelectorListWrapper
|
|
212
|
+
<DualListSelectorListWrapper
|
|
213
|
+
aria-labelledby={`${id}-status`}
|
|
214
|
+
id={`${id}-list`}
|
|
215
|
+
{...(listMinHeight && {
|
|
216
|
+
style: { '--pf-c-dual-list-selector__menu--MinHeight': listMinHeight } as React.CSSProperties
|
|
217
|
+
})}
|
|
218
|
+
>
|
|
207
219
|
{options.length > 0 ? (
|
|
208
220
|
<DualListSelectorList>
|
|
209
221
|
<DualListSelectorTree
|
|
@@ -20,6 +20,7 @@ import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-ico
|
|
|
20
20
|
import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';
|
|
21
21
|
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
|
22
22
|
import PficonSortCommonAscIcon from '@patternfly/react-icons/dist/esm/icons/pficon-sort-common-asc-icon';
|
|
23
|
+
import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
|
|
23
24
|
|
|
24
25
|
## Examples
|
|
25
26
|
|
|
@@ -8,13 +8,20 @@ import {
|
|
|
8
8
|
DualListSelectorListItem,
|
|
9
9
|
DualListSelectorControlsWrapper,
|
|
10
10
|
DualListSelectorControl,
|
|
11
|
-
SearchInput
|
|
11
|
+
SearchInput,
|
|
12
|
+
Title,
|
|
13
|
+
EmptyState,
|
|
14
|
+
EmptyStateVariant,
|
|
15
|
+
EmptyStateIcon,
|
|
16
|
+
EmptyStateBody,
|
|
17
|
+
EmptyStatePrimary
|
|
12
18
|
} from '@patternfly/react-core';
|
|
13
19
|
import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon';
|
|
14
20
|
import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
|
|
15
21
|
import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';
|
|
16
22
|
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
|
17
23
|
import PficonSortCommonAscIcon from '@patternfly/react-icons/dist/esm/icons/pficon-sort-common-asc-icon';
|
|
24
|
+
import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
|
|
18
25
|
|
|
19
26
|
interface Option {
|
|
20
27
|
text: string;
|
|
@@ -86,25 +93,23 @@ export const DualListSelectorComposable: React.FunctionComponent = () => {
|
|
|
86
93
|
}
|
|
87
94
|
};
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
option.isVisible = value === '' || option.text.toLowerCase().includes(value.toLowerCase());
|
|
96
|
-
});
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
return (
|
|
100
|
-
<SearchInput
|
|
101
|
-
value={isAvailable ? availableFilter : chosenFilter}
|
|
102
|
-
onChange={onChange}
|
|
103
|
-
onClear={() => onChange('')}
|
|
104
|
-
/>
|
|
105
|
-
);
|
|
96
|
+
const onFilterChange = (value: string, isAvailable: boolean) => {
|
|
97
|
+
isAvailable ? setAvailableFilter(value) : setChosenFilter(value);
|
|
98
|
+
const toFilter = isAvailable ? [...availableOptions] : [...chosenOptions];
|
|
99
|
+
toFilter.forEach(option => {
|
|
100
|
+
option.isVisible = value === '' || option.text.toLowerCase().includes(value.toLowerCase());
|
|
101
|
+
});
|
|
106
102
|
};
|
|
107
103
|
|
|
104
|
+
// builds a search input - used in each dual list selector pane
|
|
105
|
+
const buildSearchInput = (isAvailable: boolean) => (
|
|
106
|
+
<SearchInput
|
|
107
|
+
value={isAvailable ? availableFilter : chosenFilter}
|
|
108
|
+
onChange={value => onFilterChange(value, isAvailable)}
|
|
109
|
+
onClear={() => onFilterChange('', isAvailable)}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
|
|
108
113
|
// builds a sort control - passed to both dual list selector panes
|
|
109
114
|
const buildSort = (isAvailable: boolean) => {
|
|
110
115
|
const onSort = () => {
|
|
@@ -132,6 +137,21 @@ export const DualListSelectorComposable: React.FunctionComponent = () => {
|
|
|
132
137
|
);
|
|
133
138
|
};
|
|
134
139
|
|
|
140
|
+
const buildEmptyState = (isAvailable: boolean) => (
|
|
141
|
+
<EmptyState variant={EmptyStateVariant.small}>
|
|
142
|
+
<EmptyStateIcon icon={SearchIcon} />
|
|
143
|
+
<Title headingLevel="h4" size="md">
|
|
144
|
+
No results found
|
|
145
|
+
</Title>
|
|
146
|
+
<EmptyStateBody>No results match the filter criteria. Clear all filters and try again.</EmptyStateBody>
|
|
147
|
+
<EmptyStatePrimary>
|
|
148
|
+
<Button variant="link" onClick={() => onFilterChange('', isAvailable)}>
|
|
149
|
+
Clear all filters
|
|
150
|
+
</Button>
|
|
151
|
+
</EmptyStatePrimary>
|
|
152
|
+
</EmptyState>
|
|
153
|
+
);
|
|
154
|
+
|
|
135
155
|
return (
|
|
136
156
|
<DualListSelector>
|
|
137
157
|
<DualListSelectorPane
|
|
@@ -141,21 +161,27 @@ export const DualListSelectorComposable: React.FunctionComponent = () => {
|
|
|
141
161
|
} options selected`}
|
|
142
162
|
searchInput={buildSearchInput(true)}
|
|
143
163
|
actions={[buildSort(true)]}
|
|
164
|
+
listMinHeight="300px"
|
|
144
165
|
>
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
{availableFilter !== '' &&
|
|
167
|
+
availableOptions.filter(option => option.isVisible).length === 0 &&
|
|
168
|
+
buildEmptyState(true)}
|
|
169
|
+
{availableOptions.filter(option => option.isVisible).length > 0 && (
|
|
170
|
+
<DualListSelectorList>
|
|
171
|
+
{availableOptions.map((option, index) =>
|
|
172
|
+
option.isVisible ? (
|
|
173
|
+
<DualListSelectorListItem
|
|
174
|
+
key={index}
|
|
175
|
+
isSelected={option.selected}
|
|
176
|
+
id={`composable-available-option-${index}`}
|
|
177
|
+
onOptionSelect={e => onOptionSelect(e, index, false)}
|
|
178
|
+
>
|
|
179
|
+
{option.text}
|
|
180
|
+
</DualListSelectorListItem>
|
|
181
|
+
) : null
|
|
182
|
+
)}
|
|
183
|
+
</DualListSelectorList>
|
|
184
|
+
)}
|
|
159
185
|
</DualListSelectorPane>
|
|
160
186
|
<DualListSelectorControlsWrapper>
|
|
161
187
|
<DualListSelectorControl
|
|
@@ -199,21 +225,25 @@ export const DualListSelectorComposable: React.FunctionComponent = () => {
|
|
|
199
225
|
searchInput={buildSearchInput(false)}
|
|
200
226
|
actions={[buildSort(false)]}
|
|
201
227
|
isChosen
|
|
228
|
+
listMinHeight="300px"
|
|
202
229
|
>
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
230
|
+
{chosenFilter !== '' && chosenOptions.filter(option => option.isVisible).length === 0 && buildEmptyState(false)}
|
|
231
|
+
{chosenOptions.filter(option => option.isVisible).length > 0 && (
|
|
232
|
+
<DualListSelectorList>
|
|
233
|
+
{chosenOptions.map((option, index) =>
|
|
234
|
+
option.isVisible ? (
|
|
235
|
+
<DualListSelectorListItem
|
|
236
|
+
key={index}
|
|
237
|
+
isSelected={option.selected}
|
|
238
|
+
id={`composable-chosen-option-${index}`}
|
|
239
|
+
onOptionSelect={e => onOptionSelect(e, index, true)}
|
|
240
|
+
>
|
|
241
|
+
{option.text}
|
|
242
|
+
</DualListSelectorListItem>
|
|
243
|
+
) : null
|
|
244
|
+
)}
|
|
245
|
+
</DualListSelectorList>
|
|
246
|
+
)}
|
|
217
247
|
</DualListSelectorPane>
|
|
218
248
|
</DualListSelector>
|
|
219
249
|
);
|
|
@@ -7,12 +7,20 @@ import {
|
|
|
7
7
|
DualListSelectorControl,
|
|
8
8
|
DualListSelectorTree,
|
|
9
9
|
DualListSelectorTreeItemData,
|
|
10
|
-
SearchInput
|
|
10
|
+
SearchInput,
|
|
11
|
+
Title,
|
|
12
|
+
Button,
|
|
13
|
+
EmptyState,
|
|
14
|
+
EmptyStateVariant,
|
|
15
|
+
EmptyStateIcon,
|
|
16
|
+
EmptyStateBody,
|
|
17
|
+
EmptyStatePrimary
|
|
11
18
|
} from '@patternfly/react-core';
|
|
12
19
|
import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-left-icon';
|
|
13
20
|
import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-icon';
|
|
14
21
|
import AngleDoubleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-double-right-icon';
|
|
15
22
|
import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-icon';
|
|
23
|
+
import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
|
|
16
24
|
|
|
17
25
|
interface FoodNode {
|
|
18
26
|
id: string;
|
|
@@ -235,19 +243,37 @@ export const DualListSelectorComposableTree: React.FunctionComponent<ExampleProp
|
|
|
235
243
|
isChosen ? chosenLeafIds.includes(id) : !chosenLeafIds.includes(id)
|
|
236
244
|
).length;
|
|
237
245
|
const status = `${numSelected} of ${numOptions} options selected`;
|
|
246
|
+
const filterApplied = isChosen ? chosenFilter !== '' : availableFilter !== '';
|
|
238
247
|
return (
|
|
239
248
|
<DualListSelectorPane
|
|
240
249
|
title={isChosen ? 'Chosen' : 'Available'}
|
|
241
250
|
status={status}
|
|
242
251
|
searchInput={buildSearchInput(isChosen)}
|
|
243
252
|
isChosen={isChosen}
|
|
253
|
+
listMinHeight="300px"
|
|
244
254
|
>
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
255
|
+
{filterApplied && options.length === 0 && (
|
|
256
|
+
<EmptyState variant={EmptyStateVariant.small}>
|
|
257
|
+
<EmptyStateIcon icon={SearchIcon} />
|
|
258
|
+
<Title headingLevel="h4" size="md">
|
|
259
|
+
No results found
|
|
260
|
+
</Title>
|
|
261
|
+
<EmptyStateBody>No results match the filter criteria. Clear all filters and try again.</EmptyStateBody>
|
|
262
|
+
<EmptyStatePrimary>
|
|
263
|
+
<Button variant="link" onClick={() => (isChosen ? setChosenFilter('') : setAvailableFilter(''))}>
|
|
264
|
+
Clear all filters
|
|
265
|
+
</Button>
|
|
266
|
+
</EmptyStatePrimary>
|
|
267
|
+
</EmptyState>
|
|
268
|
+
)}
|
|
269
|
+
{options.length > 0 && (
|
|
270
|
+
<DualListSelectorList>
|
|
271
|
+
<DualListSelectorTree
|
|
272
|
+
data={options}
|
|
273
|
+
onOptionCheck={(e, isChecked, itemData) => onOptionCheck(e, isChecked, itemData, isChosen)}
|
|
274
|
+
/>
|
|
275
|
+
</DualListSelectorList>
|
|
276
|
+
)}
|
|
251
277
|
</DualListSelectorPane>
|
|
252
278
|
);
|
|
253
279
|
};
|