@patternfly/react-core 5.0.0-alpha.107 → 5.0.0-alpha.108
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 +6 -0
- package/dist/esm/components/AboutModal/package.json +1 -1
- package/dist/esm/components/Accordion/package.json +1 -1
- package/dist/esm/components/ActionList/package.json +1 -1
- package/dist/esm/components/Alert/package.json +1 -1
- package/dist/esm/components/Avatar/package.json +1 -1
- package/dist/esm/components/BackToTop/package.json +1 -1
- package/dist/esm/components/Backdrop/package.json +1 -1
- package/dist/esm/components/BackgroundImage/package.json +1 -1
- package/dist/esm/components/Badge/package.json +1 -1
- package/dist/esm/components/Banner/package.json +1 -1
- package/dist/esm/components/Brand/package.json +1 -1
- package/dist/esm/components/Breadcrumb/package.json +1 -1
- package/dist/esm/components/Button/package.json +1 -1
- package/dist/esm/components/CalendarMonth/package.json +1 -1
- package/dist/esm/components/Card/package.json +1 -1
- package/dist/esm/components/Checkbox/package.json +1 -1
- package/dist/esm/components/Chip/package.json +1 -1
- package/dist/esm/components/ClipboardCopy/package.json +1 -1
- package/dist/esm/components/CodeBlock/package.json +1 -1
- package/dist/esm/components/DataList/package.json +1 -1
- package/dist/esm/components/DatePicker/package.json +1 -1
- package/dist/esm/components/DescriptionList/package.json +1 -1
- package/dist/esm/components/Divider/package.json +1 -1
- package/dist/esm/components/DragDrop/package.json +1 -1
- package/dist/esm/components/Drawer/package.json +1 -1
- package/dist/esm/components/Dropdown/Dropdown.d.ts +11 -3
- package/dist/esm/components/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/esm/components/Dropdown/Dropdown.js +11 -5
- package/dist/esm/components/Dropdown/Dropdown.js.map +1 -1
- package/dist/esm/components/Dropdown/package.json +1 -1
- package/dist/esm/components/DualListSelector/package.json +1 -1
- package/dist/esm/components/EmptyState/package.json +1 -1
- package/dist/esm/components/ExpandableSection/package.json +1 -1
- package/dist/esm/components/FileUpload/package.json +1 -1
- package/dist/esm/components/Form/package.json +1 -1
- package/dist/esm/components/FormSelect/package.json +1 -1
- package/dist/esm/components/HelperText/package.json +1 -1
- package/dist/esm/components/Hint/package.json +1 -1
- package/dist/esm/components/Icon/package.json +1 -1
- package/dist/esm/components/InputGroup/package.json +1 -1
- package/dist/esm/components/JumpLinks/package.json +1 -1
- package/dist/esm/components/Label/package.json +1 -1
- package/dist/esm/components/List/package.json +1 -1
- package/dist/esm/components/LoginPage/package.json +1 -1
- package/dist/esm/components/Masthead/package.json +1 -1
- package/dist/esm/components/Menu/package.json +1 -1
- package/dist/esm/components/MenuToggle/package.json +1 -1
- package/dist/esm/components/Modal/package.json +1 -1
- package/dist/esm/components/MultipleFileUpload/package.json +1 -1
- package/dist/esm/components/Nav/package.json +1 -1
- package/dist/esm/components/NotificationBadge/package.json +1 -1
- package/dist/esm/components/NotificationDrawer/package.json +1 -1
- package/dist/esm/components/NumberInput/package.json +1 -1
- package/dist/esm/components/OverflowMenu/package.json +1 -1
- package/dist/esm/components/Page/package.json +1 -1
- package/dist/esm/components/Pagination/package.json +1 -1
- package/dist/esm/components/Panel/package.json +1 -1
- package/dist/esm/components/Popover/package.json +1 -1
- package/dist/esm/components/Progress/package.json +1 -1
- package/dist/esm/components/ProgressStepper/package.json +1 -1
- package/dist/esm/components/Radio/package.json +1 -1
- package/dist/esm/components/SearchInput/package.json +1 -1
- package/dist/esm/components/Select/Select.d.ts +31 -5
- package/dist/esm/components/Select/Select.d.ts.map +1 -1
- package/dist/esm/components/Select/Select.js +16 -10
- package/dist/esm/components/Select/Select.js.map +1 -1
- package/dist/esm/components/Select/SelectGroup.d.ts +3 -0
- package/dist/esm/components/Select/SelectGroup.d.ts.map +1 -1
- package/dist/esm/components/Select/SelectGroup.js.map +1 -1
- package/dist/esm/components/Select/SelectOption.d.ts +14 -1
- package/dist/esm/components/Select/SelectOption.d.ts.map +1 -1
- package/dist/esm/components/Select/SelectOption.js +4 -3
- package/dist/esm/components/Select/SelectOption.js.map +1 -1
- package/dist/esm/components/Select/package.json +1 -1
- package/dist/esm/components/Sidebar/package.json +1 -1
- package/dist/esm/components/SimpleList/package.json +1 -1
- package/dist/esm/components/Skeleton/package.json +1 -1
- package/dist/esm/components/SkipToContent/package.json +1 -1
- package/dist/esm/components/Slider/package.json +1 -1
- package/dist/esm/components/Spinner/package.json +1 -1
- package/dist/esm/components/Switch/package.json +1 -1
- package/dist/esm/components/Tabs/package.json +1 -1
- package/dist/esm/components/Text/package.json +1 -1
- package/dist/esm/components/TextArea/package.json +1 -1
- package/dist/esm/components/TextInput/package.json +1 -1
- package/dist/esm/components/TextInputGroup/package.json +1 -1
- package/dist/esm/components/Tile/package.json +1 -1
- package/dist/esm/components/TimePicker/package.json +1 -1
- package/dist/esm/components/Timestamp/package.json +1 -1
- package/dist/esm/components/Title/package.json +1 -1
- package/dist/esm/components/ToggleGroup/package.json +1 -1
- package/dist/esm/components/Toolbar/package.json +1 -1
- package/dist/esm/components/Tooltip/package.json +1 -1
- package/dist/esm/components/TreeView/package.json +1 -1
- package/dist/esm/components/Truncate/package.json +1 -1
- package/dist/esm/components/Wizard/hooks/package.json +1 -1
- package/dist/esm/components/Wizard/package.json +1 -1
- package/dist/esm/components/package.json +1 -1
- package/dist/esm/deprecated/components/ApplicationLauncher/package.json +1 -1
- package/dist/esm/deprecated/components/ContextSelector/package.json +1 -1
- package/dist/esm/deprecated/components/Dropdown/package.json +1 -1
- package/dist/esm/deprecated/components/OptionsMenu/package.json +1 -1
- package/dist/esm/deprecated/components/PageHeader/package.json +1 -1
- package/dist/esm/deprecated/components/Select/package.json +1 -1
- package/dist/esm/deprecated/components/Wizard/package.json +1 -1
- package/dist/esm/deprecated/components/package.json +1 -1
- package/dist/esm/deprecated/package.json +1 -1
- package/dist/esm/helpers/Popper/Popper.d.ts +1 -1
- package/dist/esm/helpers/Popper/Popper.d.ts.map +1 -1
- package/dist/esm/helpers/Popper/thirdparty/popper-core/package.json +1 -1
- package/dist/esm/helpers/package.json +1 -1
- package/dist/esm/layouts/Bullseye/package.json +1 -1
- package/dist/esm/layouts/Flex/package.json +1 -1
- package/dist/esm/layouts/Gallery/package.json +1 -1
- package/dist/esm/layouts/Grid/package.json +1 -1
- package/dist/esm/layouts/Level/package.json +1 -1
- package/dist/esm/layouts/Split/package.json +1 -1
- package/dist/esm/layouts/Stack/package.json +1 -1
- package/dist/esm/layouts/package.json +1 -1
- package/dist/esm/next/components/package.json +1 -1
- package/dist/esm/next/package.json +1 -1
- package/dist/esm/package.json +1 -1
- package/dist/js/components/AboutModal/package.json +1 -1
- package/dist/js/components/Accordion/package.json +1 -1
- package/dist/js/components/ActionList/package.json +1 -1
- package/dist/js/components/Alert/package.json +1 -1
- package/dist/js/components/Avatar/package.json +1 -1
- package/dist/js/components/BackToTop/package.json +1 -1
- package/dist/js/components/Backdrop/package.json +1 -1
- package/dist/js/components/BackgroundImage/package.json +1 -1
- package/dist/js/components/Badge/package.json +1 -1
- package/dist/js/components/Banner/package.json +1 -1
- package/dist/js/components/Brand/package.json +1 -1
- package/dist/js/components/Breadcrumb/package.json +1 -1
- package/dist/js/components/Button/package.json +1 -1
- package/dist/js/components/CalendarMonth/package.json +1 -1
- package/dist/js/components/Card/package.json +1 -1
- package/dist/js/components/Checkbox/package.json +1 -1
- package/dist/js/components/Chip/package.json +1 -1
- package/dist/js/components/ClipboardCopy/package.json +1 -1
- package/dist/js/components/CodeBlock/package.json +1 -1
- package/dist/js/components/DataList/package.json +1 -1
- package/dist/js/components/DatePicker/package.json +1 -1
- package/dist/js/components/DescriptionList/package.json +1 -1
- package/dist/js/components/Divider/package.json +1 -1
- package/dist/js/components/DragDrop/package.json +1 -1
- package/dist/js/components/Drawer/package.json +1 -1
- package/dist/js/components/Dropdown/Dropdown.d.ts +11 -3
- package/dist/js/components/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/js/components/Dropdown/Dropdown.js +11 -5
- package/dist/js/components/Dropdown/Dropdown.js.map +1 -1
- package/dist/js/components/Dropdown/package.json +1 -1
- package/dist/js/components/DualListSelector/package.json +1 -1
- package/dist/js/components/EmptyState/package.json +1 -1
- package/dist/js/components/ExpandableSection/package.json +1 -1
- package/dist/js/components/FileUpload/package.json +1 -1
- package/dist/js/components/Form/package.json +1 -1
- package/dist/js/components/FormSelect/package.json +1 -1
- package/dist/js/components/HelperText/package.json +1 -1
- package/dist/js/components/Hint/package.json +1 -1
- package/dist/js/components/Icon/package.json +1 -1
- package/dist/js/components/InputGroup/package.json +1 -1
- package/dist/js/components/JumpLinks/package.json +1 -1
- package/dist/js/components/Label/package.json +1 -1
- package/dist/js/components/List/package.json +1 -1
- package/dist/js/components/LoginPage/package.json +1 -1
- package/dist/js/components/Masthead/package.json +1 -1
- package/dist/js/components/Menu/package.json +1 -1
- package/dist/js/components/MenuToggle/package.json +1 -1
- package/dist/js/components/Modal/package.json +1 -1
- package/dist/js/components/MultipleFileUpload/package.json +1 -1
- package/dist/js/components/Nav/package.json +1 -1
- package/dist/js/components/NotificationBadge/package.json +1 -1
- package/dist/js/components/NotificationDrawer/package.json +1 -1
- package/dist/js/components/NumberInput/package.json +1 -1
- package/dist/js/components/OverflowMenu/package.json +1 -1
- package/dist/js/components/Page/package.json +1 -1
- package/dist/js/components/Pagination/package.json +1 -1
- package/dist/js/components/Panel/package.json +1 -1
- package/dist/js/components/Popover/package.json +1 -1
- package/dist/js/components/Progress/package.json +1 -1
- package/dist/js/components/ProgressStepper/package.json +1 -1
- package/dist/js/components/Radio/package.json +1 -1
- package/dist/js/components/SearchInput/package.json +1 -1
- package/dist/js/components/Select/Select.d.ts +31 -5
- package/dist/js/components/Select/Select.d.ts.map +1 -1
- package/dist/js/components/Select/Select.js +16 -10
- package/dist/js/components/Select/Select.js.map +1 -1
- package/dist/js/components/Select/SelectGroup.d.ts +3 -0
- package/dist/js/components/Select/SelectGroup.d.ts.map +1 -1
- package/dist/js/components/Select/SelectGroup.js.map +1 -1
- package/dist/js/components/Select/SelectOption.d.ts +14 -1
- package/dist/js/components/Select/SelectOption.d.ts.map +1 -1
- package/dist/js/components/Select/SelectOption.js +4 -4
- package/dist/js/components/Select/SelectOption.js.map +1 -1
- package/dist/js/components/Select/package.json +1 -1
- package/dist/js/components/Sidebar/package.json +1 -1
- package/dist/js/components/SimpleList/package.json +1 -1
- package/dist/js/components/Skeleton/package.json +1 -1
- package/dist/js/components/SkipToContent/package.json +1 -1
- package/dist/js/components/Slider/package.json +1 -1
- package/dist/js/components/Spinner/package.json +1 -1
- package/dist/js/components/Switch/package.json +1 -1
- package/dist/js/components/Tabs/package.json +1 -1
- package/dist/js/components/Text/package.json +1 -1
- package/dist/js/components/TextArea/package.json +1 -1
- package/dist/js/components/TextInput/package.json +1 -1
- package/dist/js/components/TextInputGroup/package.json +1 -1
- package/dist/js/components/Tile/package.json +1 -1
- package/dist/js/components/TimePicker/package.json +1 -1
- package/dist/js/components/Timestamp/package.json +1 -1
- package/dist/js/components/Title/package.json +1 -1
- package/dist/js/components/ToggleGroup/package.json +1 -1
- package/dist/js/components/Toolbar/package.json +1 -1
- package/dist/js/components/Tooltip/package.json +1 -1
- package/dist/js/components/TreeView/package.json +1 -1
- package/dist/js/components/Truncate/package.json +1 -1
- package/dist/js/components/Wizard/hooks/package.json +1 -1
- package/dist/js/components/Wizard/package.json +1 -1
- package/dist/js/components/package.json +1 -1
- package/dist/js/deprecated/components/ApplicationLauncher/package.json +1 -1
- package/dist/js/deprecated/components/ContextSelector/package.json +1 -1
- package/dist/js/deprecated/components/Dropdown/package.json +1 -1
- package/dist/js/deprecated/components/OptionsMenu/package.json +1 -1
- package/dist/js/deprecated/components/PageHeader/package.json +1 -1
- package/dist/js/deprecated/components/Select/package.json +1 -1
- package/dist/js/deprecated/components/Wizard/package.json +1 -1
- package/dist/js/deprecated/components/package.json +1 -1
- package/dist/js/deprecated/package.json +1 -1
- package/dist/js/helpers/Popper/Popper.d.ts +1 -1
- package/dist/js/helpers/Popper/Popper.d.ts.map +1 -1
- package/dist/js/helpers/Popper/thirdparty/popper-core/package.json +1 -1
- package/dist/js/helpers/package.json +1 -1
- package/dist/js/layouts/Bullseye/package.json +1 -1
- package/dist/js/layouts/Flex/package.json +1 -1
- package/dist/js/layouts/Gallery/package.json +1 -1
- package/dist/js/layouts/Grid/package.json +1 -1
- package/dist/js/layouts/Level/package.json +1 -1
- package/dist/js/layouts/Split/package.json +1 -1
- package/dist/js/layouts/Stack/package.json +1 -1
- package/dist/js/layouts/package.json +1 -1
- package/dist/js/next/components/package.json +1 -1
- package/dist/js/next/package.json +1 -1
- package/dist/js/package.json +1 -1
- package/dist/umd/react-core.min.js +2 -2
- package/package.json +2 -2
- package/src/components/DatePicker/__tests__/__snapshots__/DatePicker.test.tsx.snap +27 -29
- package/src/components/Dropdown/Dropdown.tsx +25 -11
- package/src/components/Dropdown/examples/Dropdown.md +10 -1
- package/src/components/Dropdown/examples/DropdownBasic.tsx +2 -6
- package/src/components/Dropdown/examples/DropdownWithDescriptions.tsx +2 -6
- package/src/components/Dropdown/examples/DropdownWithGroups.tsx +2 -6
- package/src/components/Dropdown/examples/DropdownWithKebabToggle.tsx +2 -6
- package/src/components/Select/Select.tsx +62 -24
- package/src/components/Select/SelectGroup.tsx +3 -0
- package/src/components/Select/SelectOption.tsx +22 -2
- package/src/components/Select/examples/Select.md +49 -2
- package/src/components/Select/examples/SelectBasic.tsx +27 -17
- package/src/components/Select/examples/SelectCheckbox.tsx +1 -3
- package/src/components/Select/examples/SelectFooter.tsx +57 -0
- package/src/components/Select/examples/SelectGrouped.tsx +14 -6
- package/src/components/Select/examples/SelectMultiTypeahead.tsx +7 -12
- package/src/components/Select/examples/SelectMultiTypeaheadCheckbox.tsx +204 -0
- package/src/components/Select/examples/SelectMultiTypeaheadCreatable.tsx +222 -0
- package/src/components/Select/examples/SelectOptionVariations.tsx +70 -0
- package/src/components/Select/examples/SelectTypeahead.tsx +13 -13
- package/src/components/Select/examples/SelectTypeaheadCreatable.tsx +220 -0
- package/src/components/Select/examples/SelectViewMore.tsx +146 -0
- package/src/demos/ComposableMenu/ComposableMenu.md +14 -0
- package/src/helpers/Popper/Popper.tsx +1 -1
|
@@ -26,28 +26,25 @@ export const SelectBasic: React.FunctionComponent = () => {
|
|
|
26
26
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
27
27
|
const [selected, setSelected] = React.useState<string>('');
|
|
28
28
|
const [inputValue, setInputValue] = React.useState<string>('');
|
|
29
|
+
const [filterValue, setFilterValue] = React.useState<string>('');
|
|
29
30
|
const [selectOptions, setSelectOptions] = React.useState<SelectOptionProps[]>(initialSelectOptions);
|
|
30
31
|
const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
|
|
31
32
|
const [activeItem, setActiveItem] = React.useState<string | null>(null);
|
|
32
|
-
|
|
33
|
-
const menuRef = React.useRef<HTMLDivElement>(null);
|
|
34
33
|
const textInputRef = React.useRef<HTMLInputElement>();
|
|
35
34
|
|
|
36
35
|
React.useEffect(() => {
|
|
37
36
|
let newSelectOptions: SelectOptionProps[] = initialSelectOptions;
|
|
38
37
|
|
|
39
38
|
// Filter menu items based on the text input value when one exists
|
|
40
|
-
if (
|
|
41
|
-
newSelectOptions = initialSelectOptions.filter(menuItem =>
|
|
42
|
-
String(menuItem.children)
|
|
43
|
-
.toLowerCase()
|
|
44
|
-
.includes(inputValue.toLowerCase())
|
|
39
|
+
if (filterValue) {
|
|
40
|
+
newSelectOptions = initialSelectOptions.filter((menuItem) =>
|
|
41
|
+
String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase())
|
|
45
42
|
);
|
|
46
43
|
|
|
47
44
|
// When no options are found after filtering, display 'No results found'
|
|
48
45
|
if (!newSelectOptions.length) {
|
|
49
46
|
newSelectOptions = [
|
|
50
|
-
{ isDisabled: false, children: `No results found for "${
|
|
47
|
+
{ isDisabled: false, children: `No results found for "${filterValue}"`, itemId: 'no results' }
|
|
51
48
|
];
|
|
52
49
|
}
|
|
53
50
|
|
|
@@ -60,7 +57,7 @@ export const SelectBasic: React.FunctionComponent = () => {
|
|
|
60
57
|
setSelectOptions(newSelectOptions);
|
|
61
58
|
setActiveItem(null);
|
|
62
59
|
setFocusedItemIndex(null);
|
|
63
|
-
}, [
|
|
60
|
+
}, [filterValue]);
|
|
64
61
|
|
|
65
62
|
const onToggleClick = () => {
|
|
66
63
|
setIsOpen(!isOpen);
|
|
@@ -72,6 +69,7 @@ export const SelectBasic: React.FunctionComponent = () => {
|
|
|
72
69
|
|
|
73
70
|
if (itemId && itemId !== 'no results') {
|
|
74
71
|
setInputValue(itemId as string);
|
|
72
|
+
setFilterValue('');
|
|
75
73
|
setSelected(itemId as string);
|
|
76
74
|
}
|
|
77
75
|
setIsOpen(false);
|
|
@@ -81,6 +79,7 @@ export const SelectBasic: React.FunctionComponent = () => {
|
|
|
81
79
|
|
|
82
80
|
const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
|
|
83
81
|
setInputValue(value);
|
|
82
|
+
setFilterValue(value);
|
|
84
83
|
};
|
|
85
84
|
|
|
86
85
|
const handleMenuArrowKeys = (key: string) => {
|
|
@@ -106,13 +105,13 @@ export const SelectBasic: React.FunctionComponent = () => {
|
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
setFocusedItemIndex(indexToFocus);
|
|
109
|
-
const focusedItem = selectOptions.filter(option => !option.isDisabled)[indexToFocus];
|
|
108
|
+
const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus];
|
|
110
109
|
setActiveItem(`select-typeahead-${focusedItem.itemId.replace(' ', '-')}`);
|
|
111
110
|
}
|
|
112
111
|
};
|
|
113
112
|
|
|
114
113
|
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
115
|
-
const enabledMenuItems = selectOptions.filter(option => !option.isDisabled);
|
|
114
|
+
const enabledMenuItems = selectOptions.filter((option) => !option.isDisabled);
|
|
116
115
|
const [firstMenuItem] = enabledMenuItems;
|
|
117
116
|
const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
|
|
118
117
|
|
|
@@ -121,10 +120,11 @@ export const SelectBasic: React.FunctionComponent = () => {
|
|
|
121
120
|
case 'Enter':
|
|
122
121
|
if (isOpen && focusedItem.itemId !== 'no results') {
|
|
123
122
|
setInputValue(String(focusedItem.children));
|
|
123
|
+
setFilterValue('');
|
|
124
124
|
setSelected(String(focusedItem.children));
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
setIsOpen(prevIsOpen => !prevIsOpen);
|
|
127
|
+
setIsOpen((prevIsOpen) => !prevIsOpen);
|
|
128
128
|
setFocusedItemIndex(null);
|
|
129
129
|
setActiveItem(null);
|
|
130
130
|
|
|
@@ -167,6 +167,7 @@ export const SelectBasic: React.FunctionComponent = () => {
|
|
|
167
167
|
onClick={() => {
|
|
168
168
|
setSelected('');
|
|
169
169
|
setInputValue('');
|
|
170
|
+
setFilterValue('');
|
|
170
171
|
textInputRef?.current?.focus();
|
|
171
172
|
}}
|
|
172
173
|
aria-label="Clear input value"
|
|
@@ -182,7 +183,6 @@ export const SelectBasic: React.FunctionComponent = () => {
|
|
|
182
183
|
return (
|
|
183
184
|
<Select
|
|
184
185
|
id="typeahead-select"
|
|
185
|
-
ref={menuRef}
|
|
186
186
|
isOpen={isOpen}
|
|
187
187
|
selected={selected}
|
|
188
188
|
onSelect={onSelect}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Select,
|
|
4
|
+
SelectOption,
|
|
5
|
+
SelectList,
|
|
6
|
+
SelectOptionProps,
|
|
7
|
+
MenuToggle,
|
|
8
|
+
MenuToggleElement,
|
|
9
|
+
TextInputGroup,
|
|
10
|
+
TextInputGroupMain,
|
|
11
|
+
TextInputGroupUtilities,
|
|
12
|
+
Button
|
|
13
|
+
} from '@patternfly/react-core';
|
|
14
|
+
import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
|
|
15
|
+
|
|
16
|
+
let initialSelectOptions: SelectOptionProps[] = [
|
|
17
|
+
{ itemId: 'Alabama', children: 'Alabama' },
|
|
18
|
+
{ itemId: 'Florida', children: 'Florida' },
|
|
19
|
+
{ itemId: 'New Jersey', children: 'New Jersey' },
|
|
20
|
+
{ itemId: 'New Mexico', children: 'New Mexico' },
|
|
21
|
+
{ itemId: 'New York', children: 'New York' },
|
|
22
|
+
{ itemId: 'North Carolina', children: 'North Carolina' }
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
|
|
26
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
27
|
+
const [selected, setSelected] = React.useState<string>('');
|
|
28
|
+
const [inputValue, setInputValue] = React.useState<string>('');
|
|
29
|
+
const [filterValue, setFilterValue] = React.useState<string>('');
|
|
30
|
+
const [selectOptions, setSelectOptions] = React.useState<SelectOptionProps[]>(initialSelectOptions);
|
|
31
|
+
const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
|
|
32
|
+
const [activeItem, setActiveItem] = React.useState<string | null>(null);
|
|
33
|
+
const [onCreation, setOnCreation] = React.useState<boolean>(false); // Boolean to refresh filter state after new option is created
|
|
34
|
+
const textInputRef = React.useRef<HTMLInputElement>();
|
|
35
|
+
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
let newSelectOptions: SelectOptionProps[] = initialSelectOptions;
|
|
38
|
+
|
|
39
|
+
// Filter menu items based on the text input value when one exists
|
|
40
|
+
if (filterValue) {
|
|
41
|
+
newSelectOptions = initialSelectOptions.filter((menuItem) =>
|
|
42
|
+
String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase())
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// When no options are found after filtering, display creation option
|
|
46
|
+
if (!newSelectOptions.length) {
|
|
47
|
+
newSelectOptions = [{ isDisabled: false, children: `Create new option "${filterValue}"`, itemId: 'create' }];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Open the menu when the input value changes and the new value is not empty
|
|
51
|
+
if (!isOpen) {
|
|
52
|
+
setIsOpen(true);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setSelectOptions(newSelectOptions);
|
|
57
|
+
setActiveItem(null);
|
|
58
|
+
setFocusedItemIndex(null);
|
|
59
|
+
}, [filterValue, onCreation]);
|
|
60
|
+
|
|
61
|
+
const onToggleClick = () => {
|
|
62
|
+
setIsOpen(!isOpen);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
|
|
66
|
+
// eslint-disable-next-line no-console
|
|
67
|
+
|
|
68
|
+
if (itemId) {
|
|
69
|
+
if (itemId === 'create') {
|
|
70
|
+
if (!initialSelectOptions.some((item) => item.itemId === filterValue)) {
|
|
71
|
+
initialSelectOptions = [...initialSelectOptions, { itemId: filterValue, children: filterValue }];
|
|
72
|
+
}
|
|
73
|
+
setSelected(filterValue);
|
|
74
|
+
setOnCreation(!onCreation);
|
|
75
|
+
setFilterValue('');
|
|
76
|
+
} else {
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
console.log('selected', itemId);
|
|
79
|
+
setInputValue(itemId as string);
|
|
80
|
+
setFilterValue('');
|
|
81
|
+
setSelected(itemId as string);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setIsOpen(false);
|
|
86
|
+
setFocusedItemIndex(null);
|
|
87
|
+
setActiveItem(null);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
|
|
91
|
+
setInputValue(value);
|
|
92
|
+
setFilterValue(value);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const handleMenuArrowKeys = (key: string) => {
|
|
96
|
+
let indexToFocus;
|
|
97
|
+
|
|
98
|
+
if (isOpen) {
|
|
99
|
+
if (key === 'ArrowUp') {
|
|
100
|
+
// When no index is set or at the first index, focus to the last, otherwise decrement focus index
|
|
101
|
+
if (focusedItemIndex === null || focusedItemIndex === 0) {
|
|
102
|
+
indexToFocus = selectOptions.length - 1;
|
|
103
|
+
} else {
|
|
104
|
+
indexToFocus = focusedItemIndex - 1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (key === 'ArrowDown') {
|
|
109
|
+
// When no index is set or at the last index, focus to the first, otherwise increment focus index
|
|
110
|
+
if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
|
|
111
|
+
indexToFocus = 0;
|
|
112
|
+
} else {
|
|
113
|
+
indexToFocus = focusedItemIndex + 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
setFocusedItemIndex(indexToFocus);
|
|
118
|
+
const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus];
|
|
119
|
+
setActiveItem(`select-create-typeahead-${focusedItem.itemId.replace(' ', '-')}`);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
124
|
+
const enabledMenuItems = selectOptions.filter((option) => !option.isDisabled);
|
|
125
|
+
const [firstMenuItem] = enabledMenuItems;
|
|
126
|
+
const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
|
|
127
|
+
|
|
128
|
+
switch (event.key) {
|
|
129
|
+
// Select the first available option
|
|
130
|
+
case 'Enter':
|
|
131
|
+
if (isOpen) {
|
|
132
|
+
onSelect(undefined, focusedItem.itemId as string);
|
|
133
|
+
setIsOpen((prevIsOpen) => !prevIsOpen);
|
|
134
|
+
setFocusedItemIndex(null);
|
|
135
|
+
setActiveItem(null);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
setIsOpen((prevIsOpen) => !prevIsOpen);
|
|
139
|
+
setFocusedItemIndex(null);
|
|
140
|
+
setActiveItem(null);
|
|
141
|
+
|
|
142
|
+
break;
|
|
143
|
+
case 'Tab':
|
|
144
|
+
case 'Escape':
|
|
145
|
+
setIsOpen(false);
|
|
146
|
+
setActiveItem(null);
|
|
147
|
+
break;
|
|
148
|
+
case 'ArrowUp':
|
|
149
|
+
case 'ArrowDown':
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
handleMenuArrowKeys(event.key);
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
|
|
157
|
+
<MenuToggle ref={toggleRef} variant="typeahead" onClick={onToggleClick} isExpanded={isOpen} isFullWidth>
|
|
158
|
+
<TextInputGroup isPlain>
|
|
159
|
+
<TextInputGroupMain
|
|
160
|
+
value={inputValue}
|
|
161
|
+
onClick={onToggleClick}
|
|
162
|
+
onChange={onTextInputChange}
|
|
163
|
+
onKeyDown={onInputKeyDown}
|
|
164
|
+
id="create-typeahead-select-input"
|
|
165
|
+
autoComplete="off"
|
|
166
|
+
innerRef={textInputRef}
|
|
167
|
+
placeholder="Select a state"
|
|
168
|
+
{...(activeItem && { 'aria-activedescendant': activeItem })}
|
|
169
|
+
role="combobox"
|
|
170
|
+
isExpanded={isOpen}
|
|
171
|
+
aria-controls="select-create-typeahead-listbox"
|
|
172
|
+
/>
|
|
173
|
+
|
|
174
|
+
<TextInputGroupUtilities>
|
|
175
|
+
{!!inputValue && (
|
|
176
|
+
<Button
|
|
177
|
+
variant="plain"
|
|
178
|
+
onClick={() => {
|
|
179
|
+
setSelected('');
|
|
180
|
+
setInputValue('');
|
|
181
|
+
setFilterValue('');
|
|
182
|
+
textInputRef?.current?.focus();
|
|
183
|
+
}}
|
|
184
|
+
aria-label="Clear input value"
|
|
185
|
+
>
|
|
186
|
+
<TimesIcon aria-hidden />
|
|
187
|
+
</Button>
|
|
188
|
+
)}
|
|
189
|
+
</TextInputGroupUtilities>
|
|
190
|
+
</TextInputGroup>
|
|
191
|
+
</MenuToggle>
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<Select
|
|
196
|
+
id="create-typeahead-select"
|
|
197
|
+
isOpen={isOpen}
|
|
198
|
+
selected={selected}
|
|
199
|
+
onSelect={onSelect}
|
|
200
|
+
onOpenChange={() => {
|
|
201
|
+
setIsOpen(false);
|
|
202
|
+
}}
|
|
203
|
+
toggle={toggle}
|
|
204
|
+
>
|
|
205
|
+
<SelectList id="select-create-typeahead-listbox">
|
|
206
|
+
{selectOptions.map((option, index) => (
|
|
207
|
+
<SelectOption
|
|
208
|
+
key={option.itemId || option.children}
|
|
209
|
+
isFocused={focusedItemIndex === index}
|
|
210
|
+
className={option.className}
|
|
211
|
+
onClick={() => setSelected(option.itemId)}
|
|
212
|
+
id={`select-typeahead-${option.itemId.replace(' ', '-')}`}
|
|
213
|
+
{...option}
|
|
214
|
+
ref={null}
|
|
215
|
+
/>
|
|
216
|
+
))}
|
|
217
|
+
</SelectList>
|
|
218
|
+
</Select>
|
|
219
|
+
);
|
|
220
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Select, SelectOption, SelectList, MenuToggle, Spinner } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
export const SelectViewMore: React.FunctionComponent = () => {
|
|
5
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
6
|
+
const [selected, setSelected] = React.useState<string>('Select a value');
|
|
7
|
+
const [activeItem, setActiveItem] = React.useState<number | string>(0);
|
|
8
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
10
|
+
const [selectOptions, setSelectOptions] = React.useState([
|
|
11
|
+
<SelectOption key={0} itemId="Option 1">
|
|
12
|
+
Option 1
|
|
13
|
+
</SelectOption>,
|
|
14
|
+
<SelectOption key={1} itemId="Option 2">
|
|
15
|
+
Option 2
|
|
16
|
+
</SelectOption>,
|
|
17
|
+
<SelectOption key={2} itemId="Option 3">
|
|
18
|
+
Option 3
|
|
19
|
+
</SelectOption>,
|
|
20
|
+
<SelectOption key={3} itemId="Option 4">
|
|
21
|
+
Option 4
|
|
22
|
+
</SelectOption>,
|
|
23
|
+
<SelectOption key={4} itemId="Option 5">
|
|
24
|
+
Option 5
|
|
25
|
+
</SelectOption>,
|
|
26
|
+
<SelectOption key={5} itemId="Option 6">
|
|
27
|
+
Option 6
|
|
28
|
+
</SelectOption>,
|
|
29
|
+
<SelectOption key={6} itemId="Option 7">
|
|
30
|
+
Option 7
|
|
31
|
+
</SelectOption>,
|
|
32
|
+
<SelectOption key={7} itemId="Option 8">
|
|
33
|
+
Option 8
|
|
34
|
+
</SelectOption>,
|
|
35
|
+
<SelectOption key={8} itemId="Option 9">
|
|
36
|
+
Option 9
|
|
37
|
+
</SelectOption>,
|
|
38
|
+
<SelectOption key={9} itemId="Final Option 10">
|
|
39
|
+
Final Option 10
|
|
40
|
+
</SelectOption>
|
|
41
|
+
]);
|
|
42
|
+
const [numOptions, setNumOptions] = React.useState(3);
|
|
43
|
+
const [visibleOptions, setVisibleOptions] = React.useState(selectOptions.slice(0, numOptions));
|
|
44
|
+
const activeItemRef = React.useRef<HTMLElement>(null);
|
|
45
|
+
const viewMoreRef = React.useRef<HTMLLIElement>(null);
|
|
46
|
+
const toggleRef = React.useRef<HTMLButtonElement>(null);
|
|
47
|
+
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
activeItemRef.current?.focus();
|
|
50
|
+
}, [visibleOptions]);
|
|
51
|
+
|
|
52
|
+
const simulateNetworkCall = (networkCallback: () => void) => {
|
|
53
|
+
setTimeout(networkCallback, 2000);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const getNextValidItem = (startingIndex: number, maxLength: number) => {
|
|
57
|
+
let validItem;
|
|
58
|
+
for (let i = startingIndex; i < maxLength; i++) {
|
|
59
|
+
if (selectOptions[i].props.isDisabled) {
|
|
60
|
+
continue;
|
|
61
|
+
} else {
|
|
62
|
+
validItem = selectOptions[i];
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return validItem;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const loadMoreOptions = () => {
|
|
70
|
+
const newLength = numOptions + 3 <= selectOptions.length ? numOptions + 3 : selectOptions.length;
|
|
71
|
+
const prevPosition = numOptions;
|
|
72
|
+
const nextValidItem = getNextValidItem(prevPosition, newLength);
|
|
73
|
+
|
|
74
|
+
setNumOptions(newLength);
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
setActiveItem(nextValidItem.props.itemId);
|
|
77
|
+
setVisibleOptions(selectOptions.slice(0, newLength));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const onViewMoreClick = () => {
|
|
81
|
+
setIsLoading(true);
|
|
82
|
+
simulateNetworkCall(() => {
|
|
83
|
+
loadMoreOptions();
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const onToggleClick = () => {
|
|
88
|
+
setIsOpen(!isOpen);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, itemId: string | number | undefined) => {
|
|
92
|
+
// eslint-disable-next-line no-console
|
|
93
|
+
console.log('selected', itemId);
|
|
94
|
+
|
|
95
|
+
if (itemId !== 'loader') {
|
|
96
|
+
setSelected(itemId as string);
|
|
97
|
+
setIsOpen(false);
|
|
98
|
+
toggleRef?.current?.focus(); // Only focus the toggle when a non-loader option is selected
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const toggle = (
|
|
103
|
+
<MenuToggle
|
|
104
|
+
ref={toggleRef}
|
|
105
|
+
onClick={onToggleClick}
|
|
106
|
+
isExpanded={isOpen}
|
|
107
|
+
style={
|
|
108
|
+
{
|
|
109
|
+
width: '200px'
|
|
110
|
+
} as React.CSSProperties
|
|
111
|
+
}
|
|
112
|
+
>
|
|
113
|
+
{selected}
|
|
114
|
+
</MenuToggle>
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<Select
|
|
119
|
+
id="single-view-more-select"
|
|
120
|
+
isOpen={isOpen}
|
|
121
|
+
selected={selected}
|
|
122
|
+
onSelect={onSelect}
|
|
123
|
+
onOpenChange={(isOpen) => setIsOpen(isOpen)}
|
|
124
|
+
toggle={{ toggleNode: toggle, toggleRef }}
|
|
125
|
+
>
|
|
126
|
+
<SelectList>
|
|
127
|
+
{visibleOptions.map((option) => {
|
|
128
|
+
const props = option.props;
|
|
129
|
+
|
|
130
|
+
return <SelectOption key={option.key} ref={props.itemId === activeItem ? activeItemRef : null} {...props} />;
|
|
131
|
+
})}
|
|
132
|
+
{numOptions !== selectOptions.length && (
|
|
133
|
+
<SelectOption
|
|
134
|
+
{...(!isLoading && { isLoadButton: true })}
|
|
135
|
+
{...(isLoading && { isLoading: true })}
|
|
136
|
+
onClick={onViewMoreClick}
|
|
137
|
+
itemId="loader"
|
|
138
|
+
ref={viewMoreRef}
|
|
139
|
+
>
|
|
140
|
+
{isLoading ? <Spinner size="lg" /> : 'View more'}
|
|
141
|
+
</SelectOption>
|
|
142
|
+
)}
|
|
143
|
+
</SelectList>
|
|
144
|
+
</Select>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
@@ -32,36 +32,43 @@ Custom menus can be constructed using a composable approach by combining the [Me
|
|
|
32
32
|
### Composable simple dropdown
|
|
33
33
|
|
|
34
34
|
```ts file="./examples/ComposableSimpleDropdown.tsx"
|
|
35
|
+
|
|
35
36
|
```
|
|
36
37
|
|
|
37
38
|
### Composable actions menu
|
|
38
39
|
|
|
39
40
|
```ts file="./examples/ComposableActionsMenu.tsx"
|
|
41
|
+
|
|
40
42
|
```
|
|
41
43
|
|
|
42
44
|
### Composable simple select
|
|
43
45
|
|
|
44
46
|
```ts file="./examples/ComposableSimpleSelect.tsx"
|
|
47
|
+
|
|
45
48
|
```
|
|
46
49
|
|
|
47
50
|
### Composable simple checkbox select
|
|
48
51
|
|
|
49
52
|
```ts file="./examples/ComposableSimpleCheckboxSelect.tsx"
|
|
53
|
+
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
### Composable typeahead select
|
|
53
57
|
|
|
54
58
|
```ts file="./examples/ComposableTypeaheadSelect.tsx"
|
|
59
|
+
|
|
55
60
|
```
|
|
56
61
|
|
|
57
62
|
### Composable multiple typeahead select
|
|
58
63
|
|
|
59
64
|
```ts file="./examples/ComposableMultipleTypeaheadSelect.tsx"
|
|
65
|
+
|
|
60
66
|
```
|
|
61
67
|
|
|
62
68
|
### Composable drilldown menu
|
|
63
69
|
|
|
64
70
|
```ts isBeta file="./examples/ComposableDrilldownMenu.tsx"
|
|
71
|
+
|
|
65
72
|
```
|
|
66
73
|
|
|
67
74
|
### Composable tree view menu
|
|
@@ -69,6 +76,7 @@ Custom menus can be constructed using a composable approach by combining the [Me
|
|
|
69
76
|
When rendering a menu-like element that does not contain MenuItem components, [Panel](/components/panel) allows more flexible control and customization.
|
|
70
77
|
|
|
71
78
|
```ts file="./examples/ComposableTreeViewMenu.tsx"
|
|
79
|
+
|
|
72
80
|
```
|
|
73
81
|
|
|
74
82
|
### Composable flyout
|
|
@@ -76,29 +84,35 @@ When rendering a menu-like element that does not contain MenuItem components, [P
|
|
|
76
84
|
The flyout will automatically position to the left or top if it would otherwise go outside the window. The menu must be placed in a container outside the main content like Popper, [Popover](/components/popover) or [Tooltip](/components/tooltip) since it may go over the side nav.
|
|
77
85
|
|
|
78
86
|
```ts isBeta file="./examples/ComposableFlyout.tsx"
|
|
87
|
+
|
|
79
88
|
```
|
|
80
89
|
|
|
81
90
|
### Composable application launcher
|
|
82
91
|
|
|
83
92
|
```ts file="./examples/ComposableApplicationLauncher.tsx"
|
|
93
|
+
|
|
84
94
|
```
|
|
85
95
|
|
|
86
96
|
### Composable context selector
|
|
87
97
|
|
|
88
98
|
```ts file="./examples/ComposableContextSelector.tsx"
|
|
99
|
+
|
|
89
100
|
```
|
|
90
101
|
|
|
91
102
|
### Composable options menu variants
|
|
92
103
|
|
|
93
104
|
```ts file="./examples/ComposableOptionsMenuVariants.tsx"
|
|
105
|
+
|
|
94
106
|
```
|
|
95
107
|
|
|
96
108
|
### Composable dropdown variants
|
|
97
109
|
|
|
98
110
|
```ts file="./examples/ComposableDropdwnVariants.tsx"
|
|
111
|
+
|
|
99
112
|
```
|
|
100
113
|
|
|
101
114
|
### Composable date select
|
|
102
115
|
|
|
103
116
|
```ts file="./examples/ComposableDateSelect.tsx"
|
|
117
|
+
|
|
104
118
|
```
|
|
@@ -68,7 +68,7 @@ export interface PopperProps {
|
|
|
68
68
|
position?: 'right' | 'left' | 'center';
|
|
69
69
|
/** Instead of direction and position can set the placement of the popper */
|
|
70
70
|
placement?: Placement;
|
|
71
|
-
/**
|
|
71
|
+
/** Custom width of the popper. If the value is "trigger", it will set the width to the trigger element's width */
|
|
72
72
|
width?: string | 'trigger';
|
|
73
73
|
/** Minimum width of the popper. If the value is "trigger", it will set the min width to the trigger element's width */
|
|
74
74
|
minWidth?: string | 'trigger';
|