@patternfly/react-core 5.4.0-prerelease.7 → 5.4.0-prerelease.9
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 +16 -0
- package/CONTRIBUTING.md +0 -2
- package/components/package.json +1 -1
- package/deprecated/package.json +1 -1
- package/dist/dynamic/components/AboutModal/package.json +1 -1
- package/dist/dynamic/components/Accordion/package.json +1 -1
- package/dist/dynamic/components/ActionList/package.json +1 -1
- package/dist/dynamic/components/Alert/package.json +1 -1
- package/dist/dynamic/components/Avatar/package.json +1 -1
- package/dist/dynamic/components/BackToTop/package.json +1 -1
- package/dist/dynamic/components/Backdrop/package.json +1 -1
- package/dist/dynamic/components/BackgroundImage/package.json +1 -1
- package/dist/dynamic/components/Badge/package.json +1 -1
- package/dist/dynamic/components/Banner/package.json +1 -1
- package/dist/dynamic/components/Brand/package.json +1 -1
- package/dist/dynamic/components/Breadcrumb/package.json +1 -1
- package/dist/dynamic/components/Button/package.json +1 -1
- package/dist/dynamic/components/CalendarMonth/package.json +1 -1
- package/dist/dynamic/components/Card/package.json +1 -1
- package/dist/dynamic/components/Checkbox/package.json +1 -1
- package/dist/dynamic/components/Chip/package.json +1 -1
- package/dist/dynamic/components/ClipboardCopy/package.json +1 -1
- package/dist/dynamic/components/CodeBlock/package.json +1 -1
- package/dist/dynamic/components/DataList/package.json +1 -1
- package/dist/dynamic/components/DatePicker/package.json +1 -1
- package/dist/dynamic/components/DescriptionList/package.json +1 -1
- package/dist/dynamic/components/Divider/package.json +1 -1
- package/dist/dynamic/components/DragDrop/package.json +1 -1
- package/dist/dynamic/components/Drawer/package.json +1 -1
- package/dist/dynamic/components/Dropdown/package.json +1 -1
- package/dist/dynamic/components/DualListSelector/package.json +1 -1
- package/dist/dynamic/components/EmptyState/package.json +1 -1
- package/dist/dynamic/components/ExpandableSection/package.json +1 -1
- package/dist/dynamic/components/FileUpload/package.json +1 -1
- package/dist/dynamic/components/Form/package.json +1 -1
- package/dist/dynamic/components/FormSelect/package.json +1 -1
- package/dist/dynamic/components/HelperText/package.json +1 -1
- package/dist/dynamic/components/Hint/package.json +1 -1
- package/dist/dynamic/components/Icon/package.json +1 -1
- package/dist/dynamic/components/InputGroup/package.json +1 -1
- package/dist/dynamic/components/JumpLinks/package.json +1 -1
- package/dist/dynamic/components/Label/package.json +1 -1
- package/dist/dynamic/components/List/package.json +1 -1
- package/dist/dynamic/components/LoginPage/package.json +1 -1
- package/dist/dynamic/components/Masthead/package.json +1 -1
- package/dist/dynamic/components/Menu/package.json +1 -1
- package/dist/dynamic/components/MenuToggle/package.json +1 -1
- package/dist/dynamic/components/Modal/package.json +1 -1
- package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
- package/dist/dynamic/components/Nav/package.json +1 -1
- package/dist/dynamic/components/NotificationBadge/package.json +1 -1
- package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
- package/dist/dynamic/components/NumberInput/package.json +1 -1
- package/dist/dynamic/components/OverflowMenu/package.json +1 -1
- package/dist/dynamic/components/Page/package.json +1 -1
- package/dist/dynamic/components/Pagination/package.json +1 -1
- package/dist/dynamic/components/Panel/package.json +1 -1
- package/dist/dynamic/components/Popover/package.json +1 -1
- package/dist/dynamic/components/Progress/package.json +1 -1
- package/dist/dynamic/components/ProgressStepper/package.json +1 -1
- package/dist/dynamic/components/Radio/package.json +1 -1
- package/dist/dynamic/components/SearchInput/package.json +1 -1
- package/dist/dynamic/components/Select/package.json +1 -1
- package/dist/dynamic/components/Sidebar/package.json +1 -1
- package/dist/dynamic/components/SimpleList/package.json +1 -1
- package/dist/dynamic/components/Skeleton/package.json +1 -1
- package/dist/dynamic/components/SkipToContent/package.json +1 -1
- package/dist/dynamic/components/Slider/package.json +1 -1
- package/dist/dynamic/components/Spinner/package.json +1 -1
- package/dist/dynamic/components/Switch/package.json +1 -1
- package/dist/dynamic/components/Tabs/package.json +1 -1
- package/dist/dynamic/components/Text/package.json +1 -1
- package/dist/dynamic/components/TextArea/package.json +1 -1
- package/dist/dynamic/components/TextInput/package.json +1 -1
- package/dist/dynamic/components/TextInputGroup/package.json +1 -1
- package/dist/dynamic/components/Tile/package.json +1 -1
- package/dist/dynamic/components/TimePicker/package.json +1 -1
- package/dist/dynamic/components/Timestamp/package.json +1 -1
- package/dist/dynamic/components/Title/package.json +1 -1
- package/dist/dynamic/components/ToggleGroup/package.json +1 -1
- package/dist/dynamic/components/Toolbar/package.json +1 -1
- package/dist/dynamic/components/Tooltip/package.json +1 -1
- package/dist/dynamic/components/TreeView/package.json +1 -1
- package/dist/dynamic/components/Truncate/package.json +1 -1
- package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
- package/dist/dynamic/components/Wizard/package.json +1 -1
- package/dist/dynamic/deprecated/components/ApplicationLauncher/package.json +1 -1
- package/dist/dynamic/deprecated/components/ContextSelector/package.json +1 -1
- package/dist/dynamic/deprecated/components/Dropdown/package.json +1 -1
- package/dist/dynamic/deprecated/components/OptionsMenu/package.json +1 -1
- package/dist/dynamic/deprecated/components/PageHeader/package.json +1 -1
- package/dist/dynamic/deprecated/components/Select/package.json +1 -1
- package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
- package/dist/dynamic/deprecated/components/package.json +1 -1
- package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
- package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
- package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
- package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
- package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
- package/dist/dynamic/helpers/constants/package.json +1 -1
- package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
- package/dist/dynamic/helpers/favorites/package.json +1 -1
- package/dist/dynamic/helpers/fileUtils/package.json +1 -1
- package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
- package/dist/dynamic/helpers/package.json +1 -1
- package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
- package/dist/dynamic/helpers/typeUtils/package.json +1 -1
- package/dist/dynamic/helpers/useInterval/package.json +1 -1
- package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
- package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
- package/dist/dynamic/helpers/util/package.json +1 -1
- package/dist/dynamic/layouts/Bullseye/package.json +1 -1
- package/dist/dynamic/layouts/Flex/package.json +1 -1
- package/dist/dynamic/layouts/Gallery/package.json +1 -1
- package/dist/dynamic/layouts/Grid/package.json +1 -1
- package/dist/dynamic/layouts/Level/package.json +1 -1
- package/dist/dynamic/layouts/Split/package.json +1 -1
- package/dist/dynamic/layouts/Stack/package.json +1 -1
- package/dist/dynamic/next/components/DualListSelector/package.json +1 -1
- package/dist/dynamic/next/components/Modal/package.json +1 -1
- package/dist/dynamic/next/components/package.json +1 -1
- package/dist/dynamic/styles/package.json +1 -1
- package/dist/esm/components/MenuToggle/MenuToggle.d.ts.map +1 -1
- package/dist/esm/components/MenuToggle/MenuToggle.js +1 -1
- package/dist/esm/components/MenuToggle/MenuToggle.js.map +1 -1
- package/dist/esm/components/Pagination/Pagination.d.ts.map +1 -1
- package/dist/esm/components/Pagination/Pagination.js +2 -1
- package/dist/esm/components/Pagination/Pagination.js.map +1 -1
- package/dist/esm/components/Pagination/PaginationOptionsMenu.d.ts +1 -0
- package/dist/esm/components/Pagination/PaginationOptionsMenu.d.ts.map +1 -1
- package/dist/esm/components/Pagination/PaginationOptionsMenu.js +2 -4
- package/dist/esm/components/Pagination/PaginationOptionsMenu.js.map +1 -1
- package/dist/esm/components/Select/Select.d.ts +2 -0
- package/dist/esm/components/Select/Select.d.ts.map +1 -1
- package/dist/esm/components/Select/Select.js +3 -3
- package/dist/esm/components/Select/Select.js.map +1 -1
- package/dist/esm/components/Tabs/Tabs.d.ts +4 -0
- package/dist/esm/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/esm/components/Tabs/Tabs.js +17 -0
- package/dist/esm/components/Tabs/Tabs.js.map +1 -1
- package/dist/esm/components/Truncate/Truncate.d.ts.map +1 -1
- package/dist/esm/components/Truncate/Truncate.js +54 -12
- package/dist/esm/components/Truncate/Truncate.js.map +1 -1
- package/dist/js/components/MenuToggle/MenuToggle.d.ts.map +1 -1
- package/dist/js/components/MenuToggle/MenuToggle.js +1 -1
- package/dist/js/components/MenuToggle/MenuToggle.js.map +1 -1
- package/dist/js/components/Pagination/Pagination.d.ts.map +1 -1
- package/dist/js/components/Pagination/Pagination.js +2 -1
- package/dist/js/components/Pagination/Pagination.js.map +1 -1
- package/dist/js/components/Pagination/PaginationOptionsMenu.d.ts +1 -0
- package/dist/js/components/Pagination/PaginationOptionsMenu.d.ts.map +1 -1
- package/dist/js/components/Pagination/PaginationOptionsMenu.js +2 -4
- package/dist/js/components/Pagination/PaginationOptionsMenu.js.map +1 -1
- package/dist/js/components/Select/Select.d.ts +2 -0
- package/dist/js/components/Select/Select.d.ts.map +1 -1
- package/dist/js/components/Select/Select.js +3 -3
- package/dist/js/components/Select/Select.js.map +1 -1
- package/dist/js/components/Tabs/Tabs.d.ts +4 -0
- package/dist/js/components/Tabs/Tabs.d.ts.map +1 -1
- package/dist/js/components/Tabs/Tabs.js +17 -0
- package/dist/js/components/Tabs/Tabs.js.map +1 -1
- package/dist/js/components/Truncate/Truncate.d.ts.map +1 -1
- package/dist/js/components/Truncate/Truncate.js +54 -12
- package/dist/js/components/Truncate/Truncate.js.map +1 -1
- package/dist/umd/react-core.min.js +2 -2
- package/helpers/package.json +1 -1
- package/layouts/package.json +1 -1
- package/next/package.json +1 -1
- package/package.json +6 -6
- package/src/components/CodeBlock/examples/CodeBlock.md +6 -0
- package/src/components/CodeBlock/examples/CodeBlockBasic.tsx +1 -1
- package/src/components/CodeBlock/examples/CodeBlockExpandable.tsx +3 -3
- package/src/components/MenuToggle/MenuToggle.tsx +1 -0
- package/src/components/Pagination/Pagination.tsx +2 -0
- package/src/components/Pagination/PaginationOptionsMenu.tsx +13 -14
- package/src/components/Pagination/__tests__/Generated/__snapshots__/PaginationOptionsMenu.test.tsx.snap +36 -38
- package/src/components/Pagination/__tests__/__snapshots__/Pagination.test.tsx.snap +714 -754
- package/src/components/Select/Select.tsx +5 -2
- package/src/components/Select/examples/SelectMultiTypeahead.tsx +111 -66
- package/src/components/Select/examples/SelectMultiTypeaheadCheckbox.tsx +103 -60
- package/src/components/Select/examples/SelectMultiTypeaheadCreatable.tsx +125 -79
- package/src/components/Select/examples/SelectTypeahead.tsx +113 -69
- package/src/components/Select/examples/SelectTypeaheadCreatable.tsx +115 -75
- package/src/components/Tabs/Tabs.tsx +21 -0
- package/src/components/ToggleGroup/examples/ToggleGroup.md +33 -8
- package/src/components/Truncate/Truncate.tsx +73 -7
- package/src/components/Truncate/__tests__/Truncate.test.tsx +6 -0
|
@@ -29,10 +29,11 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
|
|
|
29
29
|
const [filterValue, setFilterValue] = React.useState<string>('');
|
|
30
30
|
const [selectOptions, setSelectOptions] = React.useState<SelectOptionProps[]>(initialSelectOptions);
|
|
31
31
|
const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
|
|
32
|
-
const [
|
|
33
|
-
const [onCreation, setOnCreation] = React.useState<boolean>(false); // Boolean to refresh filter state after new option is created
|
|
32
|
+
const [activeItemId, setActiveItemId] = React.useState<string | null>(null);
|
|
34
33
|
const textInputRef = React.useRef<HTMLInputElement>();
|
|
35
34
|
|
|
35
|
+
const CREATE_NEW = 'create';
|
|
36
|
+
|
|
36
37
|
React.useEffect(() => {
|
|
37
38
|
let newSelectOptions: SelectOptionProps[] = initialSelectOptions;
|
|
38
39
|
|
|
@@ -42,9 +43,9 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
|
|
|
42
43
|
String(menuItem.children).toLowerCase().includes(filterValue.toLowerCase())
|
|
43
44
|
);
|
|
44
45
|
|
|
45
|
-
//
|
|
46
|
-
if (!
|
|
47
|
-
newSelectOptions = [{
|
|
46
|
+
// If no option matches the filter exactly, display creation option
|
|
47
|
+
if (!initialSelectOptions.some((option) => option.value === filterValue)) {
|
|
48
|
+
newSelectOptions = [...newSelectOptions, { children: `Create new option "${filterValue}"`, value: CREATE_NEW }];
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
// Open the menu when the input value changes and the new value is not empty
|
|
@@ -54,96 +55,133 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
setSelectOptions(newSelectOptions);
|
|
57
|
-
|
|
58
|
+
}, [filterValue]);
|
|
59
|
+
|
|
60
|
+
const createItemId = (value: any) => `select-typeahead-${value.replace(' ', '-')}`;
|
|
61
|
+
|
|
62
|
+
const setActiveAndFocusedItem = (itemIndex: number) => {
|
|
63
|
+
setFocusedItemIndex(itemIndex);
|
|
64
|
+
const focusedItem = selectOptions[itemIndex];
|
|
65
|
+
setActiveItemId(createItemId(focusedItem.value));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const resetActiveAndFocusedItem = () => {
|
|
58
69
|
setFocusedItemIndex(null);
|
|
59
|
-
|
|
70
|
+
setActiveItemId(null);
|
|
71
|
+
};
|
|
60
72
|
|
|
61
|
-
const
|
|
62
|
-
setIsOpen(
|
|
73
|
+
const closeMenu = () => {
|
|
74
|
+
setIsOpen(false);
|
|
75
|
+
resetActiveAndFocusedItem();
|
|
63
76
|
};
|
|
64
77
|
|
|
65
|
-
const
|
|
78
|
+
const onInputClick = () => {
|
|
79
|
+
if (!isOpen) {
|
|
80
|
+
setIsOpen(true);
|
|
81
|
+
} else if (!inputValue) {
|
|
82
|
+
closeMenu();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const selectOption = (value: string | number, content: string | number) => {
|
|
66
87
|
// eslint-disable-next-line no-console
|
|
88
|
+
console.log('selected', content);
|
|
89
|
+
|
|
90
|
+
setInputValue(String(content));
|
|
91
|
+
setFilterValue('');
|
|
92
|
+
setSelected(String(value));
|
|
93
|
+
|
|
94
|
+
closeMenu();
|
|
95
|
+
};
|
|
67
96
|
|
|
97
|
+
const onSelect = (_event: React.MouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => {
|
|
68
98
|
if (value) {
|
|
69
|
-
if (value ===
|
|
70
|
-
if (!initialSelectOptions.some((item) => item.
|
|
99
|
+
if (value === CREATE_NEW) {
|
|
100
|
+
if (!initialSelectOptions.some((item) => item.children === filterValue)) {
|
|
71
101
|
initialSelectOptions = [...initialSelectOptions, { value: filterValue, children: filterValue }];
|
|
72
102
|
}
|
|
73
103
|
setSelected(filterValue);
|
|
74
|
-
setOnCreation(!onCreation);
|
|
75
104
|
setFilterValue('');
|
|
105
|
+
closeMenu();
|
|
76
106
|
} else {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
setInputValue(value as string);
|
|
80
|
-
setFilterValue('');
|
|
81
|
-
setSelected(value as string);
|
|
107
|
+
const optionText = selectOptions.find((option) => option.value === value)?.children;
|
|
108
|
+
selectOption(value, optionText as string);
|
|
82
109
|
}
|
|
83
110
|
}
|
|
84
|
-
|
|
85
|
-
setIsOpen(false);
|
|
86
|
-
setFocusedItemIndex(null);
|
|
87
|
-
setActiveItem(null);
|
|
88
111
|
};
|
|
89
112
|
|
|
90
113
|
const onTextInputChange = (_event: React.FormEvent<HTMLInputElement>, value: string) => {
|
|
91
114
|
setInputValue(value);
|
|
92
115
|
setFilterValue(value);
|
|
116
|
+
|
|
117
|
+
resetActiveAndFocusedItem();
|
|
118
|
+
|
|
119
|
+
if (value !== selected) {
|
|
120
|
+
setSelected('');
|
|
121
|
+
}
|
|
93
122
|
};
|
|
94
123
|
|
|
95
124
|
const handleMenuArrowKeys = (key: string) => {
|
|
96
|
-
let indexToFocus;
|
|
125
|
+
let indexToFocus = 0;
|
|
126
|
+
|
|
127
|
+
if (!isOpen) {
|
|
128
|
+
setIsOpen(true);
|
|
129
|
+
}
|
|
97
130
|
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
131
|
+
if (selectOptions.every((option) => option.isDisabled)) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (key === 'ArrowUp') {
|
|
136
|
+
// When no index is set or at the first index, focus to the last, otherwise decrement focus index
|
|
137
|
+
if (focusedItemIndex === null || focusedItemIndex === 0) {
|
|
138
|
+
indexToFocus = selectOptions.length - 1;
|
|
139
|
+
} else {
|
|
140
|
+
indexToFocus = focusedItemIndex - 1;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Skip disabled options
|
|
144
|
+
while (selectOptions[indexToFocus].isDisabled) {
|
|
145
|
+
indexToFocus--;
|
|
146
|
+
if (indexToFocus === -1) {
|
|
102
147
|
indexToFocus = selectOptions.length - 1;
|
|
103
|
-
} else {
|
|
104
|
-
indexToFocus = focusedItemIndex - 1;
|
|
105
148
|
}
|
|
106
149
|
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (key === 'ArrowDown') {
|
|
153
|
+
// When no index is set or at the last index, focus to the first, otherwise increment focus index
|
|
154
|
+
if (focusedItemIndex === null || focusedItemIndex === selectOptions.length - 1) {
|
|
155
|
+
indexToFocus = 0;
|
|
156
|
+
} else {
|
|
157
|
+
indexToFocus = focusedItemIndex + 1;
|
|
158
|
+
}
|
|
107
159
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
160
|
+
// Skip disabled options
|
|
161
|
+
while (selectOptions[indexToFocus].isDisabled) {
|
|
162
|
+
indexToFocus++;
|
|
163
|
+
if (indexToFocus === selectOptions.length) {
|
|
111
164
|
indexToFocus = 0;
|
|
112
|
-
} else {
|
|
113
|
-
indexToFocus = focusedItemIndex + 1;
|
|
114
165
|
}
|
|
115
166
|
}
|
|
116
|
-
|
|
117
|
-
setFocusedItemIndex(indexToFocus);
|
|
118
|
-
const focusedItem = selectOptions.filter((option) => !option.isDisabled)[indexToFocus];
|
|
119
|
-
setActiveItem(`select-create-typeahead-${focusedItem.value.replace(' ', '-')}`);
|
|
120
167
|
}
|
|
168
|
+
|
|
169
|
+
setActiveAndFocusedItem(indexToFocus);
|
|
121
170
|
};
|
|
122
171
|
|
|
123
172
|
const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
124
|
-
const
|
|
125
|
-
const [firstMenuItem] = enabledMenuItems;
|
|
126
|
-
const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
|
|
173
|
+
const focusedItem = focusedItemIndex !== null ? selectOptions[focusedItemIndex] : null;
|
|
127
174
|
|
|
128
175
|
switch (event.key) {
|
|
129
|
-
// Select the first available option
|
|
130
176
|
case 'Enter':
|
|
131
|
-
if (isOpen) {
|
|
177
|
+
if (isOpen && focusedItem && !focusedItem.isAriaDisabled) {
|
|
132
178
|
onSelect(undefined, focusedItem.value as string);
|
|
133
|
-
setIsOpen((prevIsOpen) => !prevIsOpen);
|
|
134
|
-
setFocusedItemIndex(null);
|
|
135
|
-
setActiveItem(null);
|
|
136
179
|
}
|
|
137
180
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
181
|
+
if (!isOpen) {
|
|
182
|
+
setIsOpen(true);
|
|
183
|
+
}
|
|
141
184
|
|
|
142
|
-
break;
|
|
143
|
-
case 'Tab':
|
|
144
|
-
case 'Escape':
|
|
145
|
-
setIsOpen(false);
|
|
146
|
-
setActiveItem(null);
|
|
147
185
|
break;
|
|
148
186
|
case 'ArrowUp':
|
|
149
187
|
case 'ArrowDown':
|
|
@@ -153,6 +191,19 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
|
|
|
153
191
|
}
|
|
154
192
|
};
|
|
155
193
|
|
|
194
|
+
const onToggleClick = () => {
|
|
195
|
+
setIsOpen(!isOpen);
|
|
196
|
+
textInputRef?.current?.focus();
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const onClearButtonClick = () => {
|
|
200
|
+
setSelected('');
|
|
201
|
+
setInputValue('');
|
|
202
|
+
setFilterValue('');
|
|
203
|
+
resetActiveAndFocusedItem();
|
|
204
|
+
textInputRef?.current?.focus();
|
|
205
|
+
};
|
|
206
|
+
|
|
156
207
|
const toggle = (toggleRef: React.Ref<MenuToggleElement>) => (
|
|
157
208
|
<MenuToggle
|
|
158
209
|
ref={toggleRef}
|
|
@@ -165,34 +216,23 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
|
|
|
165
216
|
<TextInputGroup isPlain>
|
|
166
217
|
<TextInputGroupMain
|
|
167
218
|
value={inputValue}
|
|
168
|
-
onClick={
|
|
219
|
+
onClick={onInputClick}
|
|
169
220
|
onChange={onTextInputChange}
|
|
170
221
|
onKeyDown={onInputKeyDown}
|
|
171
222
|
id="create-typeahead-select-input"
|
|
172
223
|
autoComplete="off"
|
|
173
224
|
innerRef={textInputRef}
|
|
174
225
|
placeholder="Select a state"
|
|
175
|
-
{...(
|
|
226
|
+
{...(activeItemId && { 'aria-activedescendant': activeItemId })}
|
|
176
227
|
role="combobox"
|
|
177
228
|
isExpanded={isOpen}
|
|
178
229
|
aria-controls="select-create-typeahead-listbox"
|
|
179
230
|
/>
|
|
180
231
|
|
|
181
|
-
<TextInputGroupUtilities>
|
|
182
|
-
{
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
onClick={() => {
|
|
186
|
-
setSelected('');
|
|
187
|
-
setInputValue('');
|
|
188
|
-
setFilterValue('');
|
|
189
|
-
textInputRef?.current?.focus();
|
|
190
|
-
}}
|
|
191
|
-
aria-label="Clear input value"
|
|
192
|
-
>
|
|
193
|
-
<TimesIcon aria-hidden />
|
|
194
|
-
</Button>
|
|
195
|
-
)}
|
|
232
|
+
<TextInputGroupUtilities {...(!inputValue ? { style: { display: 'none' } } : {})}>
|
|
233
|
+
<Button variant="plain" onClick={onClearButtonClick} aria-label="Clear input value">
|
|
234
|
+
<TimesIcon aria-hidden />
|
|
235
|
+
</Button>
|
|
196
236
|
</TextInputGroupUtilities>
|
|
197
237
|
</TextInputGroup>
|
|
198
238
|
</MenuToggle>
|
|
@@ -204,10 +244,11 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
|
|
|
204
244
|
isOpen={isOpen}
|
|
205
245
|
selected={selected}
|
|
206
246
|
onSelect={onSelect}
|
|
207
|
-
onOpenChange={() => {
|
|
208
|
-
|
|
247
|
+
onOpenChange={(isOpen) => {
|
|
248
|
+
!isOpen && closeMenu();
|
|
209
249
|
}}
|
|
210
250
|
toggle={toggle}
|
|
251
|
+
shouldFocusFirstItemOnOpen={false}
|
|
211
252
|
>
|
|
212
253
|
<SelectList id="select-create-typeahead-listbox">
|
|
213
254
|
{selectOptions.map((option, index) => (
|
|
@@ -215,8 +256,7 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
|
|
|
215
256
|
key={option.value || option.children}
|
|
216
257
|
isFocused={focusedItemIndex === index}
|
|
217
258
|
className={option.className}
|
|
218
|
-
|
|
219
|
-
id={`select-typeahead-${option.value.replace(' ', '-')}`}
|
|
259
|
+
id={createItemId(option.value)}
|
|
220
260
|
{...option}
|
|
221
261
|
ref={null}
|
|
222
262
|
/>
|
|
@@ -384,6 +384,27 @@ class Tabs extends React.Component<TabsProps, TabsState> {
|
|
|
384
384
|
this.direction = getLanguageDirection(this.tabList.current);
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
static getDerivedStateFromProps(nextProps: TabsProps, prevState: TabsState) {
|
|
388
|
+
if (prevState.uncontrolledActiveKey === undefined) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const childrenHasTabWithActiveEventKey = React.Children.toArray(nextProps.children)
|
|
393
|
+
.filter((child): child is TabElement => React.isValidElement(child))
|
|
394
|
+
.some(({ props }) => props.eventKey === prevState.uncontrolledActiveKey);
|
|
395
|
+
|
|
396
|
+
// if uncontrolledActiveKey is an existing eventKey of any Tab of nextProps.children --> don't update uncontrolledActiveKey
|
|
397
|
+
if (childrenHasTabWithActiveEventKey) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// otherwise update state derived from nextProps.defaultActiveKey
|
|
402
|
+
return {
|
|
403
|
+
uncontrolledActiveKey: nextProps.defaultActiveKey,
|
|
404
|
+
shownKeys: nextProps.defaultActiveKey !== undefined ? [nextProps.defaultActiveKey] : [nextProps.activeKey] // only for mountOnEnter case
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
387
408
|
render() {
|
|
388
409
|
const {
|
|
389
410
|
className,
|
|
@@ -13,22 +13,47 @@ import ShareSquareIcon from '@patternfly/react-icons/dist/esm/icons/share-square
|
|
|
13
13
|
|
|
14
14
|
## Examples
|
|
15
15
|
|
|
16
|
-
###
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
### Single select toggle group
|
|
17
|
+
|
|
18
|
+
A single select toggle group allows users to toggle between multiple items.
|
|
19
|
+
|
|
20
|
+
To indicate whether a `<ToggleGroupItem>` is selected or not, use the `isSelected` property.
|
|
19
21
|
|
|
20
|
-
### Default with single selectable
|
|
21
22
|
```ts file="./ToggleGroupDefaultSingle.tsx"
|
|
22
23
|
```
|
|
23
24
|
|
|
24
|
-
###
|
|
25
|
+
### Multi select toggle group
|
|
26
|
+
|
|
27
|
+
A multi select toggle group allows users to select multiple items at once.
|
|
28
|
+
|
|
29
|
+
When a toggle item is disabled it cannot be selected. Click the "Disable all" button in the following example to see this in action.
|
|
30
|
+
|
|
31
|
+
```ts file="./ToggleGroupDefaultMultiple.tsx"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### With icons
|
|
35
|
+
|
|
36
|
+
You can use a recognizable icon as a toggle item label.
|
|
37
|
+
|
|
38
|
+
To do this, pass an imported icon to the `icon` property of a `<ToggleGroupItem>`.
|
|
39
|
+
|
|
25
40
|
```ts file="./ToggleGroupIcon.tsx"
|
|
26
41
|
```
|
|
27
42
|
|
|
28
|
-
###
|
|
43
|
+
### With text and icons
|
|
44
|
+
|
|
45
|
+
Adding text to a toggle item with an icon helps clarify the button's function.
|
|
46
|
+
|
|
47
|
+
To do this, pass a descriptive label to the `text` property of a `<ToggleGroupItem>`.
|
|
48
|
+
|
|
29
49
|
```ts file="./ToggleGroupTextIcon.tsx"
|
|
30
50
|
```
|
|
31
51
|
|
|
32
|
-
### Compact
|
|
52
|
+
### Compact toggle group
|
|
53
|
+
|
|
54
|
+
When space in a UI is limited, you can use a compact toggle group.
|
|
55
|
+
|
|
56
|
+
To apply compact styling to a `<ToggleGroup>`, use `isCompact`.
|
|
57
|
+
|
|
33
58
|
```ts file="./ToggleGroupCompact.tsx"
|
|
34
|
-
```
|
|
59
|
+
```
|
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import styles from '@patternfly/react-styles/css/components/Truncate/truncate';
|
|
3
3
|
import { css } from '@patternfly/react-styles';
|
|
4
4
|
import { Tooltip, TooltipPosition } from '../Tooltip';
|
|
5
|
+
import { getResizeObserver } from '../../helpers/resizeObserver';
|
|
5
6
|
|
|
6
7
|
export enum TruncatePosition {
|
|
7
8
|
start = 'start',
|
|
@@ -52,11 +53,65 @@ export const Truncate: React.FunctionComponent<TruncateProps> = ({
|
|
|
52
53
|
trailingNumChars = 7,
|
|
53
54
|
content,
|
|
54
55
|
...props
|
|
55
|
-
}: TruncateProps) =>
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
}: TruncateProps) => {
|
|
57
|
+
const [isTruncated, setIsTruncated] = React.useState(true);
|
|
58
|
+
const [parentElement, setParentElement] = React.useState<HTMLElement>(null);
|
|
59
|
+
const [textElement, setTextElement] = React.useState<HTMLElement>(null);
|
|
60
|
+
|
|
61
|
+
const textRef = React.useRef<HTMLElement>(null);
|
|
62
|
+
const subParentRef = React.useRef<HTMLDivElement>(null);
|
|
63
|
+
const observer = React.useRef(null);
|
|
64
|
+
|
|
65
|
+
const getActualWidth = (element: Element) => {
|
|
66
|
+
const computedStyle = getComputedStyle(element);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
parseFloat(computedStyle.width) -
|
|
70
|
+
parseFloat(computedStyle.paddingLeft) -
|
|
71
|
+
parseFloat(computedStyle.paddingRight) -
|
|
72
|
+
parseFloat(computedStyle.borderRight) -
|
|
73
|
+
parseFloat(computedStyle.borderLeft)
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const calculateTotalTextWidth = (element: Element, trailingNumChars: number, content: string) => {
|
|
78
|
+
const firstTextWidth = element.scrollWidth;
|
|
79
|
+
const firstTextLength = content.length;
|
|
80
|
+
return (firstTextWidth / firstTextLength) * trailingNumChars + firstTextWidth;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
React.useEffect(() => {
|
|
84
|
+
if (textRef && textRef.current && !textElement) {
|
|
85
|
+
setTextElement(textRef.current);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (subParentRef && subParentRef.current.parentElement.parentElement && !parentElement) {
|
|
89
|
+
setParentElement(subParentRef.current.parentElement.parentElement);
|
|
90
|
+
}
|
|
91
|
+
}, [textRef, subParentRef]);
|
|
92
|
+
|
|
93
|
+
React.useEffect(() => {
|
|
94
|
+
if (textElement && parentElement && !observer.current) {
|
|
95
|
+
const totalTextWidth = calculateTotalTextWidth(textElement, trailingNumChars, content);
|
|
96
|
+
const textWidth = position === 'middle' ? totalTextWidth : textElement.scrollWidth;
|
|
97
|
+
|
|
98
|
+
const handleResize = () => {
|
|
99
|
+
const parentWidth = getActualWidth(parentElement);
|
|
100
|
+
setIsTruncated(textWidth >= parentWidth);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const observer = getResizeObserver(parentElement, handleResize);
|
|
104
|
+
|
|
105
|
+
return () => {
|
|
106
|
+
observer();
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}, [textElement, parentElement]);
|
|
110
|
+
|
|
111
|
+
const truncateBody = (
|
|
112
|
+
<span ref={subParentRef} className={css(styles.truncate, className)} {...props}>
|
|
58
113
|
{(position === TruncatePosition.end || position === TruncatePosition.start) && (
|
|
59
|
-
<span className={truncateStyles[position]}>
|
|
114
|
+
<span ref={textRef} className={truncateStyles[position]}>
|
|
60
115
|
{content}
|
|
61
116
|
{position === TruncatePosition.start && <React.Fragment>‎</React.Fragment>}
|
|
62
117
|
</span>
|
|
@@ -64,7 +119,9 @@ export const Truncate: React.FunctionComponent<TruncateProps> = ({
|
|
|
64
119
|
{position === TruncatePosition.middle &&
|
|
65
120
|
content.slice(0, content.length - trailingNumChars).length > minWidthCharacters && (
|
|
66
121
|
<React.Fragment>
|
|
67
|
-
<span className={styles.truncateStart}>
|
|
122
|
+
<span ref={textRef} className={styles.truncateStart}>
|
|
123
|
+
{sliceContent(content, trailingNumChars)[0]}
|
|
124
|
+
</span>
|
|
68
125
|
<span className={styles.truncateEnd}>{sliceContent(content, trailingNumChars)[1]}</span>
|
|
69
126
|
</React.Fragment>
|
|
70
127
|
)}
|
|
@@ -72,6 +129,15 @@ export const Truncate: React.FunctionComponent<TruncateProps> = ({
|
|
|
72
129
|
content.slice(0, content.length - trailingNumChars).length <= minWidthCharacters &&
|
|
73
130
|
content}
|
|
74
131
|
</span>
|
|
75
|
-
|
|
76
|
-
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
return isTruncated ? (
|
|
135
|
+
<Tooltip hidden={!isTruncated} position={tooltipPosition} content={content}>
|
|
136
|
+
{truncateBody}
|
|
137
|
+
</Tooltip>
|
|
138
|
+
) : (
|
|
139
|
+
truncateBody
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
77
143
|
Truncate.displayName = 'Truncate';
|
|
@@ -13,6 +13,12 @@ jest.mock('../../Tooltip', () => ({
|
|
|
13
13
|
)
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
|
+
global.ResizeObserver = jest.fn().mockImplementation(() => ({
|
|
17
|
+
observe: jest.fn(),
|
|
18
|
+
unobserve: jest.fn(),
|
|
19
|
+
disconnect: jest.fn()
|
|
20
|
+
}));
|
|
21
|
+
|
|
16
22
|
test(`renders with class ${styles.truncate}`, () => {
|
|
17
23
|
render(<Truncate content={''} aria-label="test-id" />);
|
|
18
24
|
|