@patternfly/react-core 4.270.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/components/DualListSelector/DualListSelectorPane.d.ts +3 -1
  3. package/components/DualListSelector/DualListSelectorPane.d.ts.map +1 -1
  4. package/components/DualListSelector/DualListSelectorPane.js +7 -3
  5. package/components/DualListSelector/DualListSelectorPane.js.map +1 -1
  6. package/components/SearchInput/SearchInput.js +1 -1
  7. package/components/SearchInput/SearchInput.js.map +1 -1
  8. package/components/TextInputGroup/TextInputGroupMain.d.ts +1 -1
  9. package/components/TextInputGroup/TextInputGroupMain.d.ts.map +1 -1
  10. package/components/TextInputGroup/TextInputGroupMain.js +1 -1
  11. package/components/TextInputGroup/TextInputGroupMain.js.map +1 -1
  12. package/dist/esm/components/DualListSelector/DualListSelectorPane.d.ts +3 -1
  13. package/dist/esm/components/DualListSelector/DualListSelectorPane.d.ts.map +1 -1
  14. package/dist/esm/components/DualListSelector/DualListSelectorPane.js +7 -3
  15. package/dist/esm/components/DualListSelector/DualListSelectorPane.js.map +1 -1
  16. package/dist/esm/components/SearchInput/SearchInput.js +1 -1
  17. package/dist/esm/components/SearchInput/SearchInput.js.map +1 -1
  18. package/dist/esm/components/TextInputGroup/TextInputGroupMain.d.ts +1 -1
  19. package/dist/esm/components/TextInputGroup/TextInputGroupMain.d.ts.map +1 -1
  20. package/dist/esm/components/TextInputGroup/TextInputGroupMain.js +1 -1
  21. package/dist/esm/components/TextInputGroup/TextInputGroupMain.js.map +1 -1
  22. package/dist/js/components/DualListSelector/DualListSelectorPane.d.ts +3 -1
  23. package/dist/js/components/DualListSelector/DualListSelectorPane.d.ts.map +1 -1
  24. package/dist/js/components/DualListSelector/DualListSelectorPane.js +7 -3
  25. package/dist/js/components/DualListSelector/DualListSelectorPane.js.map +1 -1
  26. package/dist/js/components/SearchInput/SearchInput.js +1 -1
  27. package/dist/js/components/SearchInput/SearchInput.js.map +1 -1
  28. package/dist/js/components/TextInputGroup/TextInputGroupMain.d.ts +1 -1
  29. package/dist/js/components/TextInputGroup/TextInputGroupMain.d.ts.map +1 -1
  30. package/dist/js/components/TextInputGroup/TextInputGroupMain.js +1 -1
  31. package/dist/js/components/TextInputGroup/TextInputGroupMain.js.map +1 -1
  32. package/dist/umd/react-core.min.js +1 -1
  33. package/package.json +2 -2
  34. package/src/components/DualListSelector/DualListSelectorPane.tsx +14 -2
  35. package/src/components/DualListSelector/examples/DualListSelector.md +1 -0
  36. package/src/components/DualListSelector/examples/DualListSelectorComposable.tsx +76 -46
  37. package/src/components/DualListSelector/examples/DualListSelectorComposableTree.tsx +33 -7
  38. package/src/components/SearchInput/SearchInput.tsx +1 -1
  39. package/src/components/TextInputGroup/TextInputGroupMain.tsx +2 -2
  40. package/src/components/TextInputGroup/examples/TextInputGroupFilters.tsx +1 -1
  41. package/src/components/TextInputGroup/examples/TextInputGroupUtilitiesAndIcon.tsx +1 -1
  42. package/src/demos/ComposableMenu/examples/ComposableMultipleTypeaheadSelect.tsx +1 -1
  43. package/src/demos/ComposableMenu/examples/ComposableTypeaheadSelect.tsx +1 -1
  44. package/src/demos/examples/TextInputGroup/AttributeValueFiltering.tsx +1 -1
  45. package/src/demos/examples/TextInputGroup/AutoCompleteSearch.tsx +1 -1
  46. package/src/next/components/Select/examples/SelectMultiTypeahead.tsx +1 -1
  47. package/src/next/components/Select/examples/SelectTypeahead.tsx +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.270.0",
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": "78cd4be8d80842ed0236d9ac106f6e8ed56bb075"
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 aria-labelledby={`${id}-status`} id={`${id}-list`}>
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
- // builds a search input - used in each dual list selector pane
90
- const buildSearchInput = (isAvailable: boolean) => {
91
- const onChange = (value: string) => {
92
- isAvailable ? setAvailableFilter(value) : setChosenFilter(value);
93
- const toFilter = isAvailable ? [...availableOptions] : [...chosenOptions];
94
- toFilter.forEach(option => {
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
- <DualListSelectorList>
146
- {availableOptions.map((option, index) =>
147
- option.isVisible ? (
148
- <DualListSelectorListItem
149
- key={index}
150
- isSelected={option.selected}
151
- id={`composable-available-option-${index}`}
152
- onOptionSelect={e => onOptionSelect(e, index, false)}
153
- >
154
- {option.text}
155
- </DualListSelectorListItem>
156
- ) : null
157
- )}
158
- </DualListSelectorList>
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
- <DualListSelectorList>
204
- {chosenOptions.map((option, index) =>
205
- option.isVisible ? (
206
- <DualListSelectorListItem
207
- key={index}
208
- isSelected={option.selected}
209
- id={`composable-chosen-option-${index}`}
210
- onOptionSelect={e => onOptionSelect(e, index, true)}
211
- >
212
- {option.text}
213
- </DualListSelectorListItem>
214
- ) : null
215
- )}
216
- </DualListSelectorList>
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
- <DualListSelectorList>
246
- <DualListSelectorTree
247
- data={options}
248
- onOptionCheck={(e, isChecked, itemData) => onOptionCheck(e, isChecked, itemData, isChosen)}
249
- />
250
- </DualListSelectorList>
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
  };
@@ -206,7 +206,7 @@ const SearchInputBase: React.FunctionComponent<SearchInputProps> = ({
206
206
  setIsSearchMenuOpen(isAdvancedSearchOpen);
207
207
  }, [isAdvancedSearchOpen]);
208
208
 
209
- const onChangeHandler = (value: string, event: React.FormEvent<HTMLInputElement>) => {
209
+ const onChangeHandler = (event: React.FormEvent<HTMLInputElement>, value: string) => {
210
210
  if (onChange) {
211
211
  onChange(value, event);
212
212
  }
@@ -26,7 +26,7 @@ export interface TextInputGroupMainProps extends Omit<React.HTMLProps<HTMLDivEle
26
26
  /** Suggestion that will show up like a placeholder even with text in the input */
27
27
  hint?: string;
28
28
  /** Callback for when there is a change in the input field*/
29
- onChange?: (value: string, event: React.FormEvent<HTMLInputElement>) => void;
29
+ onChange?: (event: React.FormEvent<HTMLInputElement>, value: string) => void;
30
30
  /** Callback for when the input field is focused*/
31
31
  onFocus?: (event?: any) => void;
32
32
  /** Callback for when focus is lost on the input field*/
@@ -61,7 +61,7 @@ export const TextInputGroupMain: React.FunctionComponent<TextInputGroupMainProps
61
61
  const textInputGroupInputInputRef = innerRef || ref;
62
62
 
63
63
  const handleChange = (event: React.FormEvent<HTMLInputElement>) => {
64
- onChange(event.currentTarget.value, event);
64
+ onChange(event, event.currentTarget.value);
65
65
  };
66
66
 
67
67
  return (
@@ -27,7 +27,7 @@ export const TextInputGroupFilters: React.FunctionComponent = () => {
27
27
  const showSearchIcon = !currentChips.length;
28
28
 
29
29
  /** callback for updating the inputValue state in this component so that the input can be controlled */
30
- const handleInputChange = (value: string, _event: React.FormEvent<HTMLInputElement>) => {
30
+ const handleInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
31
31
  setInputValue(value);
32
32
  };
33
33
 
@@ -7,7 +7,7 @@ export const TextInputGroupUtilitiesAndIcon: React.FunctionComponent = () => {
7
7
  const [inputValue, setInputValue] = React.useState('');
8
8
 
9
9
  /** callback for updating the inputValue state in this component so that the input can be controlled */
10
- const handleInputChange = (value: string, _event: React.FormEvent<HTMLInputElement>) => {
10
+ const handleInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
11
11
  setInputValue(value);
12
12
  };
13
13
 
@@ -123,7 +123,7 @@ export const ComposableMultipleTypeaheadSelect: React.FunctionComponent = () =>
123
123
  textInputRef.current?.focus();
124
124
  };
125
125
 
126
- const onTextInputChange = (value: string) => {
126
+ const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
127
127
  setInputValue(value);
128
128
  };
129
129
 
@@ -135,7 +135,7 @@ export const ComposableTypeaheadSelect: React.FunctionComponent = () => {
135
135
  textInputRef.current?.focus();
136
136
  };
137
137
 
138
- const onTextInputChange = (value: string) => {
138
+ const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
139
139
  setInputValue(value);
140
140
  setIsSelected(false);
141
141
  };
@@ -44,7 +44,7 @@ export const AttributeValueFiltering: React.FunctionComponent = () => {
44
44
  const textInputGroupRef = React.useRef<HTMLDivElement>();
45
45
 
46
46
  /** callback for updating the inputValue state in this component so that the input can be controlled */
47
- const handleInputChange = (value: string, _event: React.FormEvent<HTMLInputElement>) => {
47
+ const handleInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
48
48
  setInputValue(value);
49
49
  };
50
50
 
@@ -31,7 +31,7 @@ export const AutoCompleteSearch: React.FunctionComponent = () => {
31
31
  const textInputGroupRef = React.useRef<HTMLDivElement>();
32
32
 
33
33
  /** callback for updating the inputValue state in this component so that the input can be controlled */
34
- const handleInputChange = (value: string, _event: React.FormEvent<HTMLInputElement>) => {
34
+ const handleInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
35
35
  setInputValue(value);
36
36
  };
37
37
 
@@ -109,7 +109,7 @@ export const SelectMultiTypeahead: React.FunctionComponent = () => {
109
109
  setIsOpen(!isOpen);
110
110
  };
111
111
 
112
- const onTextInputChange = (value: string) => {
112
+ const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
113
113
  setInputValue(value);
114
114
  };
115
115
 
@@ -67,7 +67,7 @@ export const SelectBasic: React.FunctionComponent = () => {
67
67
  setFocusedItemIndex(null);
68
68
  };
69
69
 
70
- const onTextInputChange = (value: string) => {
70
+ const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
71
71
  setInputValue(value);
72
72
  setFilterValue(value);
73
73
  };