@patternfly/react-core 4.227.7 → 4.228.0

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 (99) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/esm/components/Menu/MenuItem.d.ts +3 -1
  3. package/dist/esm/components/Menu/MenuItem.d.ts.map +1 -1
  4. package/dist/esm/components/Menu/MenuItem.js +2 -2
  5. package/dist/esm/components/Menu/MenuItem.js.map +1 -1
  6. package/dist/esm/components/MenuToggle/MenuToggle.d.ts +6 -12
  7. package/dist/esm/components/MenuToggle/MenuToggle.d.ts.map +1 -1
  8. package/dist/esm/components/MenuToggle/MenuToggle.js +12 -8
  9. package/dist/esm/components/MenuToggle/MenuToggle.js.map +1 -1
  10. package/dist/esm/components/Tabs/OverflowTab.d.ts +16 -0
  11. package/dist/esm/components/Tabs/OverflowTab.d.ts.map +1 -0
  12. package/dist/esm/components/Tabs/OverflowTab.js +74 -0
  13. package/dist/esm/components/Tabs/OverflowTab.js.map +1 -0
  14. package/dist/esm/components/Tabs/Tabs.d.ts +12 -0
  15. package/dist/esm/components/Tabs/Tabs.d.ts.map +1 -1
  16. package/dist/esm/components/Tabs/Tabs.js +36 -13
  17. package/dist/esm/components/Tabs/Tabs.js.map +1 -1
  18. package/dist/esm/components/TextInputGroup/TextInputGroup.d.ts +2 -0
  19. package/dist/esm/components/TextInputGroup/TextInputGroup.d.ts.map +1 -1
  20. package/dist/esm/components/TextInputGroup/TextInputGroup.js +2 -2
  21. package/dist/esm/components/TextInputGroup/TextInputGroup.js.map +1 -1
  22. package/dist/esm/components/TextInputGroup/TextInputGroupMain.d.ts +1 -1
  23. package/dist/esm/components/TextInputGroup/TextInputGroupMain.d.ts.map +1 -1
  24. package/dist/esm/components/TreeView/TreeView.d.ts +2 -0
  25. package/dist/esm/components/TreeView/TreeView.d.ts.map +1 -1
  26. package/dist/esm/components/TreeView/TreeView.js +3 -3
  27. package/dist/esm/components/TreeView/TreeView.js.map +1 -1
  28. package/dist/esm/components/TreeView/TreeViewListItem.d.ts +2 -0
  29. package/dist/esm/components/TreeView/TreeViewListItem.d.ts.map +1 -1
  30. package/dist/esm/components/TreeView/TreeViewListItem.js +23 -14
  31. package/dist/esm/components/TreeView/TreeViewListItem.js.map +1 -1
  32. package/dist/js/components/Menu/MenuItem.d.ts +3 -1
  33. package/dist/js/components/Menu/MenuItem.d.ts.map +1 -1
  34. package/dist/js/components/Menu/MenuItem.js +2 -2
  35. package/dist/js/components/Menu/MenuItem.js.map +1 -1
  36. package/dist/js/components/MenuToggle/MenuToggle.d.ts +6 -12
  37. package/dist/js/components/MenuToggle/MenuToggle.d.ts.map +1 -1
  38. package/dist/js/components/MenuToggle/MenuToggle.js +12 -8
  39. package/dist/js/components/MenuToggle/MenuToggle.js.map +1 -1
  40. package/dist/js/components/Tabs/OverflowTab.d.ts +16 -0
  41. package/dist/js/components/Tabs/OverflowTab.d.ts.map +1 -0
  42. package/dist/js/components/Tabs/OverflowTab.js +78 -0
  43. package/dist/js/components/Tabs/OverflowTab.js.map +1 -0
  44. package/dist/js/components/Tabs/Tabs.d.ts +12 -0
  45. package/dist/js/components/Tabs/Tabs.d.ts.map +1 -1
  46. package/dist/js/components/Tabs/Tabs.js +36 -13
  47. package/dist/js/components/Tabs/Tabs.js.map +1 -1
  48. package/dist/js/components/TextInputGroup/TextInputGroup.d.ts +2 -0
  49. package/dist/js/components/TextInputGroup/TextInputGroup.d.ts.map +1 -1
  50. package/dist/js/components/TextInputGroup/TextInputGroup.js +2 -2
  51. package/dist/js/components/TextInputGroup/TextInputGroup.js.map +1 -1
  52. package/dist/js/components/TextInputGroup/TextInputGroupMain.d.ts +1 -1
  53. package/dist/js/components/TextInputGroup/TextInputGroupMain.d.ts.map +1 -1
  54. package/dist/js/components/TreeView/TreeView.d.ts +2 -0
  55. package/dist/js/components/TreeView/TreeView.d.ts.map +1 -1
  56. package/dist/js/components/TreeView/TreeView.js +3 -3
  57. package/dist/js/components/TreeView/TreeView.js.map +1 -1
  58. package/dist/js/components/TreeView/TreeViewListItem.d.ts +2 -0
  59. package/dist/js/components/TreeView/TreeViewListItem.d.ts.map +1 -1
  60. package/dist/js/components/TreeView/TreeViewListItem.js +23 -14
  61. package/dist/js/components/TreeView/TreeViewListItem.js.map +1 -1
  62. package/dist/styles/base-no-reset.css +9 -0
  63. package/dist/styles/base.css +9 -0
  64. package/dist/umd/react-core.css +21 -31
  65. package/dist/umd/react-core.js +149 -48
  66. package/dist/umd/react-core.min.css +21 -31
  67. package/dist/umd/react-core.min.js +5 -5
  68. package/package.json +6 -6
  69. package/src/components/Menu/MenuItem.tsx +4 -0
  70. package/src/components/MenuToggle/MenuToggle.tsx +63 -25
  71. package/src/components/Page/examples/Page.md +10 -615
  72. package/src/components/Page/examples/PageCenteredSection.tsx +76 -0
  73. package/src/components/Page/examples/PageGroupSection.tsx +106 -0
  74. package/src/components/Page/examples/PageHorizontalNav.tsx +43 -0
  75. package/src/components/Page/examples/PageMainSectionPadding.tsx +72 -0
  76. package/src/components/Page/examples/PageTertiaryNav.tsx +42 -0
  77. package/src/components/Page/examples/PageUncontrolledNav.tsx +53 -0
  78. package/src/components/Page/examples/PageVerticalNav.tsx +65 -0
  79. package/src/components/Page/examples/PageVerticalNavUsingPageHeaderComponent.tsx +44 -0
  80. package/src/components/Page/examples/PageWithOrWithoutFill.tsx +67 -0
  81. package/src/components/Switch/examples/Switch.md +6 -153
  82. package/src/components/Switch/examples/SwitchBasic.tsx +20 -0
  83. package/src/components/Switch/examples/SwitchCheckedWithLabel.tsx +22 -0
  84. package/src/components/Switch/examples/SwitchDisabled.tsx +28 -0
  85. package/src/components/Switch/examples/SwitchReversed.tsx +21 -0
  86. package/src/components/Switch/examples/SwitchUncontrolled.tsx +14 -0
  87. package/src/components/Switch/examples/SwitchWithoutLabel.tsx +12 -0
  88. package/src/components/Tabs/OverflowTab.tsx +141 -0
  89. package/src/components/Tabs/Tabs.tsx +72 -25
  90. package/src/components/Tabs/examples/Tabs.md +6 -1
  91. package/src/components/Tabs/examples/TabsHorizontalOverflow.tsx +67 -0
  92. package/src/components/TextInputGroup/TextInputGroup.tsx +9 -1
  93. package/src/components/TextInputGroup/TextInputGroupMain.tsx +1 -1
  94. package/src/components/TreeView/TreeView.tsx +5 -0
  95. package/src/components/TreeView/TreeViewListItem.tsx +31 -17
  96. package/src/components/TreeView/__tests__/__snapshots__/TreeView.test.tsx.snap +299 -299
  97. package/src/components/TreeView/examples/TreeView.md +125 -7
  98. package/src/demos/ComposableMenu/ComposableMenu.md +8 -0
  99. package/src/demos/ComposableMenu/examples/ComposableTypeaheadSelect.tsx +196 -0
@@ -88,7 +88,9 @@ class DefaultTreeView extends React.Component {
88
88
  {
89
89
  name: 'Sources',
90
90
  id: 'example1-Sources',
91
- children: [{ name: 'Application 4', id: 'example1-App4', children: [{ name: 'Settings', id: 'example1-App4Settings' }] }]
91
+ children: [
92
+ { name: 'Application 4', id: 'example1-App4', children: [{ name: 'Settings', id: 'example1-App4Settings' }] }
93
+ ]
92
94
  },
93
95
  {
94
96
  name: 'Really really really long folder name that overflows the container it is in',
@@ -109,6 +111,106 @@ class DefaultTreeView extends React.Component {
109
111
  }
110
112
  ```
111
113
 
114
+ ### With separate selection and expansion
115
+
116
+ The `hasSelectableNodes` modifier will separate the expansion and selection behaviors, allowing a parent node to be selected or deselected with toggling its expansion.
117
+
118
+ ```js
119
+ import React from 'react';
120
+ import { TreeView, Button } from '@patternfly/react-core';
121
+
122
+ class SelectableNodesTreeView extends React.Component {
123
+ constructor(props) {
124
+ super(props);
125
+
126
+ this.state = { activeItems: {} };
127
+
128
+ this.onSelect = (evt, treeViewItem) => {
129
+ this.setState({
130
+ activeItems: [treeViewItem]
131
+ });
132
+ };
133
+ }
134
+
135
+ render() {
136
+ const { activeItems, allExpanded } = this.state;
137
+
138
+ const options = [
139
+ {
140
+ name: 'Application launcher',
141
+ id: 'SelNodesTreeView-AppLaunch',
142
+ children: [
143
+ {
144
+ name: 'Application 1',
145
+ id: 'SelNodesTreeView-App1',
146
+ children: [
147
+ { name: 'Settings', id: 'SelNodesTreeView-App1Settings' },
148
+ { name: 'Current', id: 'SelNodesTreeView-App1Current' }
149
+ ]
150
+ },
151
+ {
152
+ name: 'Application 2',
153
+ id: 'SelNodesTreeView-App2',
154
+ children: [
155
+ { name: 'Settings', id: 'SelNodesTreeView-App2Settings' },
156
+ {
157
+ name: 'Loader',
158
+ id: 'SelNodesTreeView-App2Loader',
159
+ children: [
160
+ { name: 'Loading App 1', id: 'SelNodesTreeView-LoadApp1' },
161
+ { name: 'Loading App 2', id: 'SelNodesTreeView-LoadApp2' },
162
+ { name: 'Loading App 3', id: 'SelNodesTreeView-LoadApp3' }
163
+ ]
164
+ }
165
+ ]
166
+ }
167
+ ],
168
+ defaultExpanded: true
169
+ },
170
+ {
171
+ name: 'Cost management',
172
+ id: 'SelNodesTreeView-Cost',
173
+ children: [
174
+ {
175
+ name: 'Application 3',
176
+ id: 'SelNodesTreeView-App3',
177
+ children: [
178
+ { name: 'Settings', id: 'SelNodesTreeView-App3Settings' },
179
+ { name: 'Current', id: 'SelNodesTreeView-App3Current' }
180
+ ]
181
+ }
182
+ ]
183
+ },
184
+ {
185
+ name: 'Sources',
186
+ id: 'SelNodesTreeView-Sources',
187
+ children: [
188
+ {
189
+ name: 'Application 4',
190
+ id: 'SelNodesTreeView-App4',
191
+ children: [{ name: 'Settings', id: 'SelNodesTreeView-App4Settings' }]
192
+ }
193
+ ]
194
+ },
195
+ {
196
+ name: 'Really really really long folder name that overflows the container it is in',
197
+ id: 'SelNodesTreeView-Long',
198
+ children: [{ name: 'Application 5', id: 'SelNodesTreeView-App5' }]
199
+ }
200
+ ];
201
+ return (
202
+ <TreeView
203
+ hasSelectableNodes
204
+ data={options}
205
+ activeItems={activeItems}
206
+ onSelect={this.onSelect}
207
+ allExpanded={allExpanded}
208
+ />
209
+ );
210
+ }
211
+ }
212
+ ```
213
+
112
214
  ### With search
113
215
 
114
216
  ```js
@@ -168,7 +270,13 @@ class SearchTreeView extends React.Component {
168
270
  {
169
271
  name: 'Sources',
170
272
  id: 'example2-Sources',
171
- children: [{ name: 'Application 4', id: 'example2-App4', children: [{ name: 'Settingexample2-s', id: 'example2-App4Settings' }] }]
273
+ children: [
274
+ {
275
+ name: 'Application 4',
276
+ id: 'example2-App4',
277
+ children: [{ name: 'Settingexample2-s', id: 'example2-App4Settings' }]
278
+ }
279
+ ]
172
280
  },
173
281
  {
174
282
  name: 'Really really really long folder name that overflows the container it is in',
@@ -357,7 +465,9 @@ class CheckboxTreeView extends React.Component {
357
465
  name: 'Really really really long folder name that overflows the container it is in',
358
466
  id: 'example3-Long',
359
467
  checkProps: { 'aria-label': 'long-check', checked: false },
360
- children: [{ name: 'Application 5', id: 'example3-App5', checkProps: { 'aria-label': 'app-5-check', checked: false } }]
468
+ children: [
469
+ { name: 'Application 5', id: 'example3-App5', checkProps: { 'aria-label': 'app-5-check', checked: false } }
470
+ ]
361
471
  }
362
472
  ];
363
473
 
@@ -522,7 +632,9 @@ class IconTreeView extends React.Component {
522
632
  {
523
633
  name: 'Sources',
524
634
  id: 'example4-Sources',
525
- children: [{ name: 'Application 4', id: 'example4-App4', children: [{ name: 'Settings', id: 'example4-App4Settings' }] }]
635
+ children: [
636
+ { name: 'Application 4', id: 'example4-App4', children: [{ name: 'Settings', id: 'example4-App4Settings' }] }
637
+ ]
526
638
  },
527
639
  {
528
640
  name: 'Really really really long folder name that overflows the container it is in',
@@ -613,7 +725,9 @@ class BadgesTreeView extends React.Component {
613
725
  {
614
726
  name: 'Sources',
615
727
  id: 'example5-Sources',
616
- children: [{ name: 'Application 4', id: 'example5-App4', children: [{ name: 'Settings', id: 'example5-App4Settings' }] }]
728
+ children: [
729
+ { name: 'Application 4', id: 'example5-App4', children: [{ name: 'Settings', id: 'example5-App4Settings' }] }
730
+ ]
617
731
  },
618
732
  {
619
733
  name: 'Really really really long folder name that overflows the container it is in',
@@ -842,7 +956,9 @@ class IconTreeView extends React.Component {
842
956
  {
843
957
  name: 'Sources',
844
958
  id: 'example7-Sources',
845
- children: [{ name: 'Application 4', id: 'example7-App4', children: [{ name: 'Settings', id: 'example7-App4Settings' }] }]
959
+ children: [
960
+ { name: 'Application 4', id: 'example7-App4', children: [{ name: 'Settings', id: 'example7-App4Settings' }] }
961
+ ]
846
962
  },
847
963
  {
848
964
  name: 'Really really really long folder name that overflows the container it is in',
@@ -911,7 +1027,9 @@ const GuidesTreeView: React.FunctionComponent = () => {
911
1027
  {
912
1028
  name: 'Sources',
913
1029
  id: 'example8-Sources',
914
- children: [{ name: 'Application 4', id: 'example8-App4', children: [{ name: 'Settings', id: 'example8-App4Settings' }] }]
1030
+ children: [
1031
+ { name: 'Application 4', id: 'example8-App4', children: [{ name: 'Settings', id: 'example8-App4Settings' }] }
1032
+ ]
915
1033
  },
916
1034
  {
917
1035
  name: 'Really really really long folder name that overflows the container it is in',
@@ -20,6 +20,9 @@ import pfIcon from './examples/pf-logo-small.svg';
20
20
  import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
21
21
  import CaretDownIcon from '@patternfly/react-icons/dist/esm/icons/caret-down-icon';
22
22
  import avatarImg from './examples/avatarImg.svg';
23
+ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
24
+ import { css } from '@patternfly/react-styles';
25
+ import styles from '@patternfly/react-styles/css/components/Menu/menu';
23
26
 
24
27
  ## Demos
25
28
 
@@ -45,6 +48,11 @@ Composable menus currently require consumer keyboard handling and use of our und
45
48
  ```ts isBeta file="./examples/ComposableSimpleCheckboxSelect.tsx"
46
49
  ```
47
50
 
51
+ ### Composable typeahead select
52
+
53
+ ```ts file="./examples/ComposableTypeaheadSelect.tsx"
54
+ ```
55
+
48
56
  ### Composable drilldown menu
49
57
 
50
58
  ```ts isBeta file="./examples/ComposableDrilldownMenu.tsx"
@@ -0,0 +1,196 @@
1
+ import React from 'react';
2
+
3
+ import {
4
+ Menu,
5
+ MenuContent,
6
+ MenuList,
7
+ MenuItem,
8
+ Popper,
9
+ MenuToggle,
10
+ TextInputGroup,
11
+ MenuItemProps,
12
+ Button,
13
+ TextInputGroupUtilities,
14
+ TextInputGroupMain,
15
+ MenuToggleElement
16
+ } from '@patternfly/react-core';
17
+ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
18
+ import TableIcon from '@patternfly/react-icons/dist/esm/icons/table-icon';
19
+
20
+ const intitalMenuItems: MenuItemProps[] = [
21
+ { itemId: 'Option 1', children: 'Option 1' },
22
+ { itemId: 'Option 2', children: 'Option 2' },
23
+ { itemId: 'Option 3', children: 'Option 3', icon: <TableIcon aria-hidden /> }
24
+ ];
25
+
26
+ export const ComposableTypeaheadSelect: React.FunctionComponent = () => {
27
+ const [isMenuOpen, setIsMenuOpen] = React.useState(false);
28
+ const [inputValue, setInputValue] = React.useState<string>('');
29
+ const [menuItems, setMenuItems] = React.useState<MenuItemProps[]>(intitalMenuItems);
30
+ const [focusedItemIndex, setFocusedItemIndex] = React.useState<number | null>(null);
31
+ const [isSelected, setIsSelected] = React.useState(false);
32
+
33
+ const menuToggleRef = React.useRef<MenuToggleElement>({} as MenuToggleElement);
34
+ const textInputRef = React.useRef<HTMLInputElement>();
35
+
36
+ React.useEffect(() => {
37
+ let newMenuItems: MenuItemProps[] = intitalMenuItems;
38
+
39
+ // Filter menu items based on the text input value when one exists
40
+ if (inputValue) {
41
+ newMenuItems = intitalMenuItems.filter(menuItem =>
42
+ String(menuItem.children)
43
+ .toLowerCase()
44
+ .includes(inputValue.toLowerCase())
45
+ );
46
+
47
+ // When no options are found after filtering, display 'No results found'
48
+ if (!newMenuItems.length) {
49
+ newMenuItems = [{ isDisabled: true, children: 'No results found' }];
50
+ }
51
+ }
52
+
53
+ setMenuItems(newMenuItems);
54
+ }, [inputValue]);
55
+
56
+ const focusOnInput = () => menuToggleRef.current?.querySelector('input')?.focus();
57
+
58
+ const onMenuSelect = (_event: React.MouseEvent<Element, MouseEvent>, itemId: string | number) => {
59
+ if (itemId) {
60
+ setInputValue(itemId.toString());
61
+ setIsSelected(true);
62
+ }
63
+
64
+ setIsMenuOpen(false);
65
+ setFocusedItemIndex(null);
66
+ focusOnInput();
67
+ };
68
+
69
+ const handleMenuArrowKeys = (key: string) => {
70
+ let indexToFocus;
71
+
72
+ if (isMenuOpen) {
73
+ if (key === 'ArrowUp') {
74
+ // When no index is set or at the first index, focus to the last, otherwise decrement focus index
75
+ if (focusedItemIndex === null || focusedItemIndex === 0) {
76
+ indexToFocus = menuItems.length - 1;
77
+ } else {
78
+ indexToFocus = focusedItemIndex - 1;
79
+ }
80
+ }
81
+
82
+ if (key === 'ArrowDown') {
83
+ // When no index is set or at the last index, focus to the first, otherwise increment focus index
84
+ if (focusedItemIndex === null || focusedItemIndex === menuItems.length - 1) {
85
+ indexToFocus = 0;
86
+ } else {
87
+ indexToFocus = focusedItemIndex + 1;
88
+ }
89
+ }
90
+
91
+ setFocusedItemIndex(indexToFocus);
92
+ }
93
+ };
94
+
95
+ const onInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
96
+ const enabledMenuItems = menuItems.filter(menuItem => !menuItem.isDisabled);
97
+ const [firstMenuItem] = enabledMenuItems;
98
+ const focusedItem = focusedItemIndex ? enabledMenuItems[focusedItemIndex] : firstMenuItem;
99
+
100
+ switch (event.key) {
101
+ // Select the first available option
102
+ case 'Enter':
103
+ if (isMenuOpen) {
104
+ setInputValue(String(focusedItem.children));
105
+ setIsSelected(true);
106
+ }
107
+
108
+ setIsMenuOpen(prevIsOpen => !prevIsOpen);
109
+ setFocusedItemIndex(null);
110
+ focusOnInput();
111
+
112
+ break;
113
+ case 'Tab':
114
+ case 'Escape':
115
+ setIsMenuOpen(false);
116
+ break;
117
+ case 'ArrowUp':
118
+ case 'ArrowDown':
119
+ handleMenuArrowKeys(event.key);
120
+ break;
121
+ default:
122
+ !isMenuOpen && setIsMenuOpen(true);
123
+ }
124
+ };
125
+
126
+ // Close the menu when a click occurs outside of the menu toggle content
127
+ const onDocumentClick = (event: MouseEvent) => {
128
+ if (!menuToggleRef.current?.contains(event?.target as HTMLElement)) {
129
+ setIsMenuOpen(false);
130
+ }
131
+ };
132
+
133
+ const toggleMenuOpen = () => {
134
+ setIsMenuOpen(prevIsOpen => !prevIsOpen);
135
+ textInputRef.current?.focus();
136
+ };
137
+
138
+ const onTextInputChange = (value: string) => {
139
+ setInputValue(value);
140
+ setIsSelected(false);
141
+ };
142
+
143
+ return (
144
+ <Popper
145
+ trigger={
146
+ <MenuToggle
147
+ variant="typeahead"
148
+ onClick={toggleMenuOpen}
149
+ innerRef={menuToggleRef}
150
+ isExpanded={isMenuOpen}
151
+ isFullWidth
152
+ >
153
+ <TextInputGroup isPlain>
154
+ <TextInputGroupMain
155
+ value={inputValue}
156
+ onClick={toggleMenuOpen}
157
+ onChange={onTextInputChange}
158
+ onKeyDown={onInputKeyDown}
159
+ id="typeahead-select-input"
160
+ autoComplete="off"
161
+ innerRef={textInputRef}
162
+ />
163
+
164
+ <TextInputGroupUtilities>
165
+ {!!inputValue && (
166
+ <Button variant="plain" onClick={() => setInputValue('')} aria-label="Clear input value">
167
+ <TimesIcon aria-hidden />
168
+ </Button>
169
+ )}
170
+ </TextInputGroupUtilities>
171
+ </TextInputGroup>
172
+ </MenuToggle>
173
+ }
174
+ popper={
175
+ <Menu id="select-menu" onSelect={onMenuSelect} selected={isSelected && inputValue}>
176
+ <MenuContent>
177
+ <MenuList>
178
+ {menuItems.map((itemProps, index) => (
179
+ <MenuItem
180
+ key={itemProps.itemId || itemProps.children}
181
+ isFocused={focusedItemIndex === index}
182
+ className={itemProps.className}
183
+ onClick={() => setIsSelected(true)}
184
+ {...itemProps}
185
+ ref={null}
186
+ />
187
+ ))}
188
+ </MenuList>
189
+ </MenuContent>
190
+ </Menu>
191
+ }
192
+ isVisible={isMenuOpen}
193
+ onDocumentClick={onDocumentClick}
194
+ />
195
+ );
196
+ };