@patternfly/react-core 4.266.3 → 4.267.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.266.3",
3
+ "version": "4.267.0",
4
4
  "description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
5
5
  "main": "dist/js/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -77,5 +77,5 @@
77
77
  "react": "^16.8 || ^17 || ^18",
78
78
  "react-dom": "^16.8 || ^17 || ^18"
79
79
  },
80
- "gitHead": "8b3c9550659c5433b24f71d7af75028a8508da00"
80
+ "gitHead": "b224b7b6d5b36770ee02671dc33db62da2a79e0a"
81
81
  }
@@ -71,6 +71,7 @@ export interface MenuState {
71
71
  transitionMoveTarget: HTMLElement;
72
72
  flyoutRef: React.Ref<HTMLLIElement> | null;
73
73
  disableHover: boolean;
74
+ currentDrilldownMenuId: string;
74
75
  }
75
76
 
76
77
  class MenuBase extends React.Component<MenuProps, MenuState> {
@@ -98,7 +99,8 @@ class MenuBase extends React.Component<MenuProps, MenuState> {
98
99
  searchInputValue: '',
99
100
  transitionMoveTarget: null,
100
101
  flyoutRef: null,
101
- disableHover: false
102
+ disableHover: false,
103
+ currentDrilldownMenuId: this.props.id
102
104
  };
103
105
 
104
106
  allowTabFirstItem() {
@@ -155,9 +157,19 @@ class MenuBase extends React.Component<MenuProps, MenuState> {
155
157
  this.setState({ transitionMoveTarget: null });
156
158
  } else {
157
159
  const nextMenu = current.querySelector('#' + this.props.activeMenu) || current || null;
158
- const nextTarget = Array.from(nextMenu.getElementsByTagName('UL')[0].children).filter(
160
+ const nextMenuChildren = Array.from(nextMenu.getElementsByTagName('UL')[0].children);
161
+
162
+ if (!this.state.currentDrilldownMenuId || nextMenu.id !== this.state.currentDrilldownMenuId) {
163
+ this.setState({ currentDrilldownMenuId: nextMenu.id });
164
+ } else {
165
+ // if the drilldown transition ends on the same menu, do not focus the first item
166
+ return;
167
+ }
168
+
169
+ const nextTarget = nextMenuChildren.filter(
159
170
  el => !(el.classList.contains('pf-m-disabled') || el.classList.contains('pf-c-divider'))
160
171
  )[0].firstChild;
172
+
161
173
  (nextTarget as HTMLElement).focus();
162
174
  (nextTarget as HTMLElement).tabIndex = 0;
163
175
  }
@@ -284,12 +296,16 @@ class MenuBase extends React.Component<MenuProps, MenuState> {
284
296
  additionalKeyHandler={this.handleExtraKeys}
285
297
  createNavigableElements={this.createNavigableElements}
286
298
  isActiveElement={(element: Element) =>
287
- document.activeElement.closest('li') === element ||
299
+ document.activeElement.closest('li') === element || // if element is a basic MenuItem
288
300
  document.activeElement.parentElement === element ||
301
+ document.activeElement.closest('.pf-c-menu__search') === element || // if element is a MenuInput
289
302
  (document.activeElement.closest('ol') && document.activeElement.closest('ol').firstChild === element)
290
303
  }
291
304
  getFocusableElement={(navigableElement: Element) =>
292
- navigableElement.querySelector('input') || (navigableElement.firstChild as Element)
305
+ (navigableElement.tagName === 'DIV' && navigableElement.querySelector('input')) || // for MenuInput
306
+ ((navigableElement.firstChild as Element).tagName === 'LABEL' &&
307
+ navigableElement.querySelector('input')) || // for MenuItem checkboxes
308
+ (navigableElement.firstChild as Element)
293
309
  }
294
310
  noHorizontalArrowHandling={
295
311
  document.activeElement &&
@@ -105,6 +105,11 @@ To render an initially drilled in menu, the `menuDrilledIn`, `drilldownPath`, an
105
105
  ```ts file="MenuWithDrilldownBreadcrumbs.tsx" isBeta
106
106
  ```
107
107
 
108
+ ### With drilldown and inline filter
109
+
110
+ ```ts file="MenuFilterDrilldown.tsx"
111
+ ```
112
+
108
113
  ### Scrollable
109
114
 
110
115
  ```ts file="MenuScrollable.tsx"
@@ -0,0 +1,123 @@
1
+ import React from 'react';
2
+ import {
3
+ Menu,
4
+ MenuContent,
5
+ MenuList,
6
+ MenuItem,
7
+ Divider,
8
+ DrilldownMenu,
9
+ MenuInput,
10
+ SearchInput
11
+ } from '@patternfly/react-core';
12
+
13
+ export const MenuWithDrilldown: React.FunctionComponent = () => {
14
+ const [menuDrilledIn, setMenuDrilledIn] = React.useState<string[]>([]);
15
+ const [drilldownPath, setDrilldownPath] = React.useState<string[]>([]);
16
+ const [menuHeights, setMenuHeights] = React.useState<any>({});
17
+ const [activeMenu, setActiveMenu] = React.useState<string>('filter_drilldown-rootMenu');
18
+
19
+ const drillIn = (fromMenuId: string, toMenuId: string, pathId: string) => {
20
+ setMenuDrilledIn([...menuDrilledIn, fromMenuId]);
21
+ setDrilldownPath([...drilldownPath, pathId]);
22
+ setActiveMenu(toMenuId);
23
+ };
24
+
25
+ const drillOut = (toMenuId: string) => {
26
+ const menuDrilledInSansLast = menuDrilledIn.slice(0, menuDrilledIn.length - 1);
27
+ const pathSansLast = drilldownPath.slice(0, drilldownPath.length - 1);
28
+ setMenuDrilledIn(menuDrilledInSansLast);
29
+ setDrilldownPath(pathSansLast);
30
+ setActiveMenu(toMenuId);
31
+ };
32
+
33
+ const setHeight = (menuId: string, height: number) => {
34
+ if (
35
+ menuHeights[menuId] === undefined ||
36
+ (menuId !== 'filter_drilldown-rootMenu' && menuHeights[menuId] !== height)
37
+ ) {
38
+ setMenuHeights({ ...menuHeights, [menuId]: height });
39
+ }
40
+ };
41
+
42
+ const searchRef = React.createRef<HTMLInputElement>();
43
+ const [startInput, setStartInput] = React.useState('');
44
+
45
+ const handleStartTextInputChange = (value: string) => {
46
+ setStartInput(value);
47
+ searchRef?.current?.focus();
48
+ };
49
+
50
+ const startDrillItems = [
51
+ {
52
+ item: 'Application grouping',
53
+ rest: { description: 'Description text' }
54
+ },
55
+ { item: 'Labels' },
56
+ { item: 'Annotations' },
57
+ { item: 'Count' },
58
+ { item: 'Count 2' },
59
+ { item: 'Count 3' },
60
+ { item: 'Other' }
61
+ ];
62
+
63
+ const mapped = startDrillItems
64
+ .filter(opt => !startInput || opt.item.toLowerCase().includes(startInput.toString().toLowerCase()))
65
+ .map((opt, index) => (
66
+ <MenuItem key={opt.item} itemId={index} {...opt.rest}>
67
+ {opt.item}
68
+ </MenuItem>
69
+ ));
70
+ if (startInput && mapped.length === 0) {
71
+ mapped.push(
72
+ <MenuItem isDisabled key="no result">
73
+ No results found
74
+ </MenuItem>
75
+ );
76
+ }
77
+
78
+ return (
79
+ <Menu
80
+ id="filter_drilldown-rootMenu"
81
+ containsDrilldown
82
+ drilldownItemPath={drilldownPath}
83
+ drilledInMenus={menuDrilledIn}
84
+ activeMenu={activeMenu}
85
+ onDrillIn={drillIn}
86
+ onDrillOut={drillOut}
87
+ onGetMenuHeight={setHeight}
88
+ >
89
+ <MenuContent menuHeight={`${menuHeights[activeMenu]}px`}>
90
+ <MenuList>
91
+ <MenuItem
92
+ itemId="filter_group:start_rollout"
93
+ direction="down"
94
+ drilldownMenu={
95
+ <DrilldownMenu id="filter_drilldown-drilldownMenuStart">
96
+ <MenuItem itemId="filter_group:start_rollout_breadcrumb" direction="up">
97
+ Start rollout
98
+ </MenuItem>
99
+ <Divider component="li" />
100
+ <MenuInput>
101
+ <SearchInput
102
+ ref={searchRef}
103
+ value={startInput}
104
+ aria-label="Filter menu items"
105
+ type="search"
106
+ onChange={value => handleStartTextInputChange(value)}
107
+ />
108
+ </MenuInput>
109
+ <Divider component="li" />
110
+ {mapped}
111
+ </DrilldownMenu>
112
+ }
113
+ >
114
+ Start rollout
115
+ </MenuItem>
116
+ <MenuItem itemId="item-a">Item B</MenuItem>
117
+ <MenuItem itemId="item-b">Item C</MenuItem>
118
+ <MenuItem itemId="item-c">Item D</MenuItem>
119
+ </MenuList>
120
+ </MenuContent>
121
+ </Menu>
122
+ );
123
+ };