@patternfly/react-templates 1.1.0-prerelease.36 → 1.1.0-prerelease.38
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 +10 -0
- package/components/package.json +1 -1
- package/dist/dynamic/components/Dropdown/package.json +1 -1
- package/dist/dynamic/components/Select/package.json +1 -1
- package/dist/esm/components/Select/TypeaheadSelect.d.ts +8 -2
- package/dist/esm/components/Select/TypeaheadSelect.d.ts.map +1 -1
- package/dist/esm/components/Select/TypeaheadSelect.js +39 -18
- package/dist/esm/components/Select/TypeaheadSelect.js.map +1 -1
- package/dist/js/components/Select/TypeaheadSelect.d.ts +8 -2
- package/dist/js/components/Select/TypeaheadSelect.d.ts.map +1 -1
- package/dist/js/components/Select/TypeaheadSelect.js +39 -18
- package/dist/js/components/Select/TypeaheadSelect.js.map +1 -1
- package/dist/umd/assets/{output-C5HiCNDQ.css → output-BuTRBEea.css} +8843 -8843
- package/dist/umd/react-templates.min.js +1 -1
- package/package.json +2 -2
- package/src/components/Select/TypeaheadSelect.tsx +54 -20
- package/src/components/Select/examples/TypeaheadSelectDemo.tsx +39 -9
|
@@ -22,14 +22,14 @@ export interface TypeaheadSelectOption extends Omit<SelectOptionProps, 'content'
|
|
|
22
22
|
value: string | number;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export interface TypeaheadSelectProps extends Omit<SelectProps, 'toggle'> {
|
|
25
|
+
export interface TypeaheadSelectProps extends Omit<SelectProps, 'toggle' | 'onSelect'> {
|
|
26
26
|
/** @hide Forwarded ref */
|
|
27
27
|
innerRef?: React.Ref<any>;
|
|
28
28
|
/** Initial options of the select. */
|
|
29
29
|
initialOptions: TypeaheadSelectOption[];
|
|
30
30
|
/** Callback triggered on selection. */
|
|
31
31
|
onSelect?: (
|
|
32
|
-
_event: React.MouseEvent<Element, MouseEvent> | React.KeyboardEvent<HTMLInputElement
|
|
32
|
+
_event: React.MouseEvent<Element, MouseEvent> | React.KeyboardEvent<HTMLInputElement> | undefined,
|
|
33
33
|
selection: string | number
|
|
34
34
|
) => void;
|
|
35
35
|
/** Callback triggered when the select opens or closes. */
|
|
@@ -40,6 +40,12 @@ export interface TypeaheadSelectProps extends Omit<SelectProps, 'toggle'> {
|
|
|
40
40
|
onClearSelection?: () => void;
|
|
41
41
|
/** Placeholder text for the select input. */
|
|
42
42
|
placeholder?: string;
|
|
43
|
+
/** Flag to indicate if the typeahead select allows new items */
|
|
44
|
+
isCreatable?: boolean;
|
|
45
|
+
/** Flag to indicate if create option should be at top of typeahead */
|
|
46
|
+
isCreateOptionOnTop?: boolean;
|
|
47
|
+
/** Message to display to create a new option */
|
|
48
|
+
createOptionMessage?: string | ((newValue: string) => string);
|
|
43
49
|
/** Message to display when no options are available. */
|
|
44
50
|
noOptionsAvailableMessage?: string;
|
|
45
51
|
/** Message to display when no options match the filter. */
|
|
@@ -52,6 +58,9 @@ export interface TypeaheadSelectProps extends Omit<SelectProps, 'toggle'> {
|
|
|
52
58
|
toggleProps?: MenuToggleProps;
|
|
53
59
|
}
|
|
54
60
|
|
|
61
|
+
const defaultNoOptionsFoundMessage = (filter: string) => `No results found for "${filter}"`;
|
|
62
|
+
const defaultCreateOptionMessage = (newValue: string) => `Create "${newValue}"`;
|
|
63
|
+
|
|
55
64
|
export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps> = ({
|
|
56
65
|
innerRef,
|
|
57
66
|
initialOptions,
|
|
@@ -61,7 +70,10 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
61
70
|
onClearSelection,
|
|
62
71
|
placeholder = 'Select an option',
|
|
63
72
|
noOptionsAvailableMessage = 'No options are available',
|
|
64
|
-
noOptionsFoundMessage =
|
|
73
|
+
noOptionsFoundMessage = defaultNoOptionsFoundMessage,
|
|
74
|
+
isCreatable = false,
|
|
75
|
+
isCreateOptionOnTop = false,
|
|
76
|
+
createOptionMessage = defaultCreateOptionMessage,
|
|
65
77
|
isDisabled,
|
|
66
78
|
toggleWidth,
|
|
67
79
|
toggleProps,
|
|
@@ -89,6 +101,20 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
89
101
|
String(option.content).toLowerCase().includes(filterValue.toLowerCase())
|
|
90
102
|
);
|
|
91
103
|
|
|
104
|
+
if (
|
|
105
|
+
isCreatable &&
|
|
106
|
+
filterValue &&
|
|
107
|
+
!initialOptions.find((o) => String(o.content).toLowerCase() === filterValue.toLowerCase())
|
|
108
|
+
) {
|
|
109
|
+
const createOption = {
|
|
110
|
+
content: typeof createOptionMessage === 'string' ? createOptionMessage : createOptionMessage(filterValue),
|
|
111
|
+
value: filterValue
|
|
112
|
+
};
|
|
113
|
+
newSelectOptions = isCreateOptionOnTop
|
|
114
|
+
? [createOption, ...newSelectOptions]
|
|
115
|
+
: [...newSelectOptions, createOption];
|
|
116
|
+
}
|
|
117
|
+
|
|
92
118
|
// When no options are found after filtering, display 'No results found'
|
|
93
119
|
if (!newSelectOptions.length) {
|
|
94
120
|
newSelectOptions = [
|
|
@@ -102,9 +128,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
102
128
|
}
|
|
103
129
|
|
|
104
130
|
// Open the menu when the input value changes and the new value is not empty
|
|
105
|
-
|
|
106
|
-
openMenu();
|
|
107
|
-
}
|
|
131
|
+
openMenu();
|
|
108
132
|
}
|
|
109
133
|
|
|
110
134
|
// When no options are available, display 'No options available'
|
|
@@ -119,17 +143,30 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
119
143
|
}
|
|
120
144
|
|
|
121
145
|
setSelectOptions(newSelectOptions);
|
|
122
|
-
}, [
|
|
146
|
+
}, [
|
|
147
|
+
filterValue,
|
|
148
|
+
initialOptions,
|
|
149
|
+
noOptionsFoundMessage,
|
|
150
|
+
isCreatable,
|
|
151
|
+
isCreateOptionOnTop,
|
|
152
|
+
createOptionMessage,
|
|
153
|
+
noOptionsAvailableMessage
|
|
154
|
+
]);
|
|
123
155
|
|
|
124
156
|
React.useEffect(() => {
|
|
157
|
+
// If the selected option changed and the current input value is the previously selected item, update the displayed value.
|
|
125
158
|
const selectedOption = initialOptions.find((o) => o.selected);
|
|
126
|
-
|
|
159
|
+
if (inputValue === selected && selectedOption?.value !== selected) {
|
|
160
|
+
setInputValue(String(selectedOption?.content ?? ''));
|
|
161
|
+
}
|
|
162
|
+
// Only update when options change
|
|
163
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
127
164
|
}, [initialOptions]);
|
|
128
165
|
|
|
129
166
|
const setActiveAndFocusedItem = (itemIndex: number) => {
|
|
130
167
|
setFocusedItemIndex(itemIndex);
|
|
131
168
|
const focusedItem = selectOptions[itemIndex];
|
|
132
|
-
setActiveItemId(focusedItem.value
|
|
169
|
+
setActiveItemId(String(focusedItem.value));
|
|
133
170
|
};
|
|
134
171
|
|
|
135
172
|
const resetActiveAndFocusedItem = () => {
|
|
@@ -138,8 +175,10 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
138
175
|
};
|
|
139
176
|
|
|
140
177
|
const openMenu = () => {
|
|
141
|
-
|
|
142
|
-
|
|
178
|
+
if (!isOpen) {
|
|
179
|
+
onToggle && onToggle(true);
|
|
180
|
+
setIsOpen(true);
|
|
181
|
+
}
|
|
143
182
|
};
|
|
144
183
|
|
|
145
184
|
const closeMenu = () => {
|
|
@@ -191,9 +230,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
191
230
|
const handleMenuArrowKeys = (key: string) => {
|
|
192
231
|
let indexToFocus = 0;
|
|
193
232
|
|
|
194
|
-
|
|
195
|
-
openMenu();
|
|
196
|
-
}
|
|
233
|
+
openMenu();
|
|
197
234
|
|
|
198
235
|
if (selectOptions.every((option) => option.isDisabled)) {
|
|
199
236
|
return;
|
|
@@ -245,10 +282,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
245
282
|
selectOption(event, focusedItem);
|
|
246
283
|
}
|
|
247
284
|
|
|
248
|
-
|
|
249
|
-
onToggle && onToggle(true);
|
|
250
|
-
setIsOpen(true);
|
|
251
|
-
}
|
|
285
|
+
openMenu();
|
|
252
286
|
|
|
253
287
|
break;
|
|
254
288
|
case 'ArrowUp':
|
|
@@ -262,7 +296,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
262
296
|
const onToggleClick = () => {
|
|
263
297
|
onToggle && onToggle(!isOpen);
|
|
264
298
|
setIsOpen(!isOpen);
|
|
265
|
-
textInputRef
|
|
299
|
+
textInputRef.current?.focus();
|
|
266
300
|
};
|
|
267
301
|
|
|
268
302
|
const onClearButtonClick = () => {
|
|
@@ -271,7 +305,7 @@ export const TypeaheadSelectBase: React.FunctionComponent<TypeaheadSelectProps>
|
|
|
271
305
|
onInputChange && onInputChange('');
|
|
272
306
|
setFilterValue('');
|
|
273
307
|
resetActiveAndFocusedItem();
|
|
274
|
-
textInputRef
|
|
308
|
+
textInputRef.current?.focus();
|
|
275
309
|
onClearSelection && onClearSelection();
|
|
276
310
|
};
|
|
277
311
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { TypeaheadSelect, TypeaheadSelectOption } from '@patternfly/react-templates';
|
|
3
|
+
import { Checkbox } from '@patternfly/react-core';
|
|
3
4
|
|
|
4
5
|
const Options = [
|
|
5
6
|
{ content: 'Alabama', value: 'option1' },
|
|
@@ -13,10 +14,13 @@ const Options = [
|
|
|
13
14
|
/* eslint-disable no-console */
|
|
14
15
|
export const SelectTypeaheadDemo: React.FunctionComponent = () => {
|
|
15
16
|
const [selected, setSelected] = React.useState<string | undefined>();
|
|
17
|
+
const [options, setOptions] = React.useState(Options);
|
|
18
|
+
const [isCreatable, setIsCreatable] = React.useState<boolean>(false);
|
|
19
|
+
const [isCreateOptionOnTop, setIsCreateOptionOnTop] = React.useState<boolean>(false);
|
|
16
20
|
|
|
17
21
|
const initialOptions = React.useMemo<TypeaheadSelectOption[]>(
|
|
18
|
-
() =>
|
|
19
|
-
[selected]
|
|
22
|
+
() => options.map((o) => ({ ...o, selected: o.value === selected })),
|
|
23
|
+
[options, selected]
|
|
20
24
|
);
|
|
21
25
|
|
|
22
26
|
React.useEffect(() => {
|
|
@@ -24,12 +28,38 @@ export const SelectTypeaheadDemo: React.FunctionComponent = () => {
|
|
|
24
28
|
}, [selected]);
|
|
25
29
|
|
|
26
30
|
return (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
<>
|
|
32
|
+
<TypeaheadSelect
|
|
33
|
+
initialOptions={initialOptions}
|
|
34
|
+
placeholder="Select a state"
|
|
35
|
+
noOptionsFoundMessage={(filter) => `No state was found for "${filter}"`}
|
|
36
|
+
onClearSelection={() => setSelected(undefined)}
|
|
37
|
+
onSelect={(_ev, selection) => {
|
|
38
|
+
if (!options.find((o) => o.value === selection)) {
|
|
39
|
+
setOptions([...options, { content: String(selection), value: String(selection) }]);
|
|
40
|
+
}
|
|
41
|
+
setSelected(String(selection));
|
|
42
|
+
}}
|
|
43
|
+
isCreatable={isCreatable}
|
|
44
|
+
isCreateOptionOnTop={isCreateOptionOnTop}
|
|
45
|
+
/>
|
|
46
|
+
<Checkbox
|
|
47
|
+
className="pf-u-mt-sm"
|
|
48
|
+
label="isCreatable"
|
|
49
|
+
isChecked={isCreatable}
|
|
50
|
+
onChange={(_event, checked) => setIsCreatable(checked)}
|
|
51
|
+
aria-label="toggle creatable checkbox"
|
|
52
|
+
id="toggle-creatable-typeahead"
|
|
53
|
+
name="toggle-creatable-typeahead"
|
|
54
|
+
/>
|
|
55
|
+
<Checkbox
|
|
56
|
+
label="isCreateOptionOnTop"
|
|
57
|
+
isChecked={isCreateOptionOnTop}
|
|
58
|
+
onChange={(_event, checked) => setIsCreateOptionOnTop(checked)}
|
|
59
|
+
aria-label="toggle createOptionOnTop checkbox"
|
|
60
|
+
id="toggle-create-option-on-top-typeahead"
|
|
61
|
+
name="toggle-create-option-on-top-typeahead"
|
|
62
|
+
/>
|
|
63
|
+
</>
|
|
34
64
|
);
|
|
35
65
|
};
|