@patternfly/react-core 4.275.0 → 4.276.1

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 (57) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/components/Accordion/AccordionContent.d.ts +2 -0
  3. package/components/Accordion/AccordionContent.d.ts.map +1 -1
  4. package/components/Accordion/AccordionContent.js +13 -2
  5. package/components/Accordion/AccordionContent.js.map +1 -1
  6. package/components/Menu/Menu.d.ts +2 -2
  7. package/components/Menu/Menu.d.ts.map +1 -1
  8. package/components/Menu/Menu.js.map +1 -1
  9. package/components/Menu/MenuContext.d.ts +2 -2
  10. package/components/Menu/MenuContext.d.ts.map +1 -1
  11. package/components/Menu/MenuContext.js.map +1 -1
  12. package/components/Menu/MenuItem.d.ts.map +1 -1
  13. package/components/Menu/MenuItem.js +5 -5
  14. package/components/Menu/MenuItem.js.map +1 -1
  15. package/dist/esm/components/Accordion/AccordionContent.d.ts +2 -0
  16. package/dist/esm/components/Accordion/AccordionContent.d.ts.map +1 -1
  17. package/dist/esm/components/Accordion/AccordionContent.js +13 -2
  18. package/dist/esm/components/Accordion/AccordionContent.js.map +1 -1
  19. package/dist/esm/components/Menu/Menu.d.ts +2 -2
  20. package/dist/esm/components/Menu/Menu.d.ts.map +1 -1
  21. package/dist/esm/components/Menu/Menu.js.map +1 -1
  22. package/dist/esm/components/Menu/MenuContext.d.ts +2 -2
  23. package/dist/esm/components/Menu/MenuContext.d.ts.map +1 -1
  24. package/dist/esm/components/Menu/MenuContext.js.map +1 -1
  25. package/dist/esm/components/Menu/MenuItem.d.ts.map +1 -1
  26. package/dist/esm/components/Menu/MenuItem.js +5 -5
  27. package/dist/esm/components/Menu/MenuItem.js.map +1 -1
  28. package/dist/js/components/Accordion/AccordionContent.d.ts +2 -0
  29. package/dist/js/components/Accordion/AccordionContent.d.ts.map +1 -1
  30. package/dist/js/components/Accordion/AccordionContent.js +13 -2
  31. package/dist/js/components/Accordion/AccordionContent.js.map +1 -1
  32. package/dist/js/components/Menu/Menu.d.ts +2 -2
  33. package/dist/js/components/Menu/Menu.d.ts.map +1 -1
  34. package/dist/js/components/Menu/Menu.js.map +1 -1
  35. package/dist/js/components/Menu/MenuContext.d.ts +2 -2
  36. package/dist/js/components/Menu/MenuContext.d.ts.map +1 -1
  37. package/dist/js/components/Menu/MenuContext.js.map +1 -1
  38. package/dist/js/components/Menu/MenuItem.d.ts.map +1 -1
  39. package/dist/js/components/Menu/MenuItem.js +5 -5
  40. package/dist/js/components/Menu/MenuItem.js.map +1 -1
  41. package/dist/umd/react-core.min.js +2 -2
  42. package/package.json +2 -2
  43. package/src/components/Accordion/AccordionContent.tsx +45 -23
  44. package/src/components/Accordion/__tests__/AccordionContent.test.tsx +15 -2
  45. package/src/components/Accordion/examples/AccordionFixedWithMultipleExpandBehavior.tsx +15 -1
  46. package/src/components/Menu/Menu.tsx +7 -2
  47. package/src/components/Menu/MenuContext.ts +7 -2
  48. package/src/components/Menu/MenuItem.tsx +6 -5
  49. package/src/components/Menu/examples/Menu.md +96 -37
  50. package/src/components/Menu/examples/MenuFilterDrilldown.tsx +7 -2
  51. package/src/components/Menu/examples/MenuWithDrilldown.tsx +7 -2
  52. package/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx +51 -14
  53. package/src/components/Menu/examples/MenuWithDrilldownInitialState.tsx +7 -2
  54. package/src/components/Menu/examples/MenuWithDrilldownSubmenuFunctions.tsx +7 -2
  55. package/src/components/Nav/examples/NavDrilldown.tsx +7 -2
  56. package/src/demos/ComposableMenu/examples/ComposableDrilldownMenu.tsx +7 -2
  57. package/src/demos/examples/Nav/NavDrilldown.tsx +7 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "4.275.0",
3
+ "version": "4.276.1",
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",
@@ -71,5 +71,5 @@
71
71
  "react": "^16.8 || ^17 || ^18",
72
72
  "react-dom": "^16.8 || ^17 || ^18"
73
73
  },
74
- "gitHead": "86b7081cba20d2a2073aec0fcecda31aad54c5bc"
74
+ "gitHead": "fadcd39108e98e73cb899387f4a82f490807443a"
75
75
  }
@@ -17,6 +17,8 @@ export interface AccordionContentProps extends React.HTMLProps<HTMLDivElement> {
17
17
  isFixed?: boolean;
18
18
  /** Adds accessible text to the Accordion content */
19
19
  'aria-label'?: string;
20
+ /** Id of the controlling accordion toggle to label the content. */
21
+ 'aria-labelledby'?: string;
20
22
  /** Component to use as content container */
21
23
  component?: React.ElementType;
22
24
  /** Flag indicating content is custom. Expanded content Body wrapper will be removed from children. This allows multiple bodies to be rendered as content. */
@@ -31,29 +33,49 @@ export const AccordionContent: React.FunctionComponent<AccordionContentProps> =
31
33
  isFixed = false,
32
34
  isCustomContent = false,
33
35
  'aria-label': ariaLabel = '',
36
+ 'aria-labelledby': ariaLabelledby,
34
37
  component,
35
38
  ...props
36
- }: AccordionContentProps) => (
37
- <AccordionContext.Consumer>
38
- {({ ContentContainer }) => {
39
- const Container = component || ContentContainer;
40
- return (
41
- <Container
42
- id={id}
43
- className={css(
44
- styles.accordionExpandedContent,
45
- isFixed && styles.modifiers.fixed,
46
- !isHidden && styles.modifiers.expanded,
47
- className
48
- )}
49
- hidden={isHidden}
50
- aria-label={ariaLabel}
51
- {...props}
52
- >
53
- {isCustomContent ? children : <AccordionExpandedContentBody>{children}</AccordionExpandedContentBody>}
54
- </Container>
55
- );
56
- }}
57
- </AccordionContext.Consumer>
58
- );
39
+ }: AccordionContentProps) => {
40
+ const [hasScrollbar, setHasScrollbar] = React.useState(false);
41
+ const containerRef = React.useRef(null);
42
+
43
+ React.useEffect(() => {
44
+ if (containerRef?.current && isFixed && !isHidden) {
45
+ const { offsetHeight, scrollHeight } = containerRef.current;
46
+
47
+ setHasScrollbar(offsetHeight < scrollHeight);
48
+ } else if (!isFixed) {
49
+ setHasScrollbar(false);
50
+ }
51
+ }, [containerRef, isFixed, isHidden]);
52
+
53
+ return (
54
+ <AccordionContext.Consumer>
55
+ {({ ContentContainer }) => {
56
+ const Container = component || ContentContainer;
57
+ return (
58
+ <Container
59
+ ref={containerRef}
60
+ id={id}
61
+ className={css(
62
+ styles.accordionExpandedContent,
63
+ isFixed && styles.modifiers.fixed,
64
+ !isHidden && styles.modifiers.expanded,
65
+ className
66
+ )}
67
+ hidden={isHidden}
68
+ {...(ariaLabel && { 'aria-label': ariaLabel })}
69
+ {...(ariaLabelledby && { 'aria-labelledby': ariaLabelledby })}
70
+ {...(hasScrollbar && { tabIndex: 0 })}
71
+ {...(hasScrollbar && Container === 'div' && { role: 'region' })}
72
+ {...props}
73
+ >
74
+ {isCustomContent ? children : <AccordionExpandedContentBody>{children}</AccordionExpandedContentBody>}
75
+ </Container>
76
+ );
77
+ }}
78
+ </AccordionContext.Consumer>
79
+ );
80
+ };
59
81
  AccordionContent.displayName = 'AccordionContent';
@@ -59,6 +59,19 @@ test('Renders with the passed aria label', () => {
59
59
  expect(screen.getByRole('heading')).toHaveAccessibleName('Accordion content');
60
60
  });
61
61
 
62
+ test('Renders with the passed aria-labelledby', () => {
63
+ render(
64
+ <>
65
+ <div id="accordion-label1">Accordion content</div>
66
+ <AccordionContext.Provider value={{ ContentContainer: 'h3' }}>
67
+ <AccordionContent aria-labelledby="accordion-label1">Test</AccordionContent>
68
+ </AccordionContext.Provider>
69
+ </>
70
+ );
71
+
72
+ expect(screen.getByRole('heading')).toHaveAccessibleName('Accordion content');
73
+ });
74
+
62
75
  test('Renders with inherited element props spread to the component', () => {
63
76
  render(
64
77
  <>
@@ -151,7 +164,7 @@ test('Renders unhidden and with pf-m-expanded by default', () => {
151
164
  </AccordionContext.Provider>
152
165
  );
153
166
 
154
- const contentContainer = screen.getByRole('heading')
167
+ const contentContainer = screen.getByRole('heading');
155
168
 
156
169
  expect(contentContainer).not.toHaveAttribute('hidden');
157
170
  expect(contentContainer).toHaveClass('pf-m-expanded');
@@ -164,7 +177,7 @@ test('Renders aria-hidden and without pf-m-expanded when isHidden is true', () =
164
177
  </AccordionContext.Provider>
165
178
  );
166
179
 
167
- const contentContainer = screen.getByRole('heading', { hidden: true })
180
+ const contentContainer = screen.getByRole('heading', { hidden: true });
168
181
 
169
182
  expect(contentContainer).toHaveAttribute('hidden');
170
183
  expect(contentContainer).not.toHaveClass('pf-m-expanded');
@@ -63,7 +63,21 @@ export const AccordionFixedWithMultipleExpandBehavior: React.FunctionComponent =
63
63
  >
64
64
  Item four
65
65
  </AccordionToggle>
66
- <AccordionContent id="ex2-expand4" isHidden={!expanded.includes('ex2-toggle4')} isFixed>
66
+ <AccordionContent
67
+ aria-labelledby="ex2-toggle4"
68
+ id="ex2-expand4"
69
+ isHidden={!expanded.includes('ex2-toggle4')}
70
+ isFixed
71
+ >
72
+ <p>
73
+ Donec vel posuere orci. Phasellus quis tortor a ex hendrerit efficitur. Aliquam lacinia ligula pharetra,
74
+ sagittis ex ut, pellentesque diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
75
+ cubilia Curae; Vestibulum ultricies nulla nibh. Etiam vel dui fermentum ligula ullamcorper eleifend non quis
76
+ tortor. Morbi tempus ornare tempus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur
77
+ ridiculus mus. Mauris et velit neque. Donec ultricies condimentum mauris, pellentesque imperdiet libero
78
+ convallis convallis. Aliquam erat volutpat. Donec rutrum semper tempus. Proin dictum imperdiet nibh, quis
79
+ dapibus nulla. Integer sed tincidunt lectus, sit amet auctor eros.
80
+ </p>
67
81
  <p>
68
82
  Donec vel posuere orci. Phasellus quis tortor a ex hendrerit efficitur. Aliquam lacinia ligula pharetra,
69
83
  sagittis ex ut, pellentesque diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
@@ -40,9 +40,14 @@ export interface MenuProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'r
40
40
  /** @beta Array of menus that are drilled in */
41
41
  drilledInMenus?: string[];
42
42
  /** @beta Callback for drilling into a submenu */
43
- onDrillIn?: (fromItemId: string, toItemId: string, itemId: string) => void;
43
+ onDrillIn?: (
44
+ event: React.KeyboardEvent | React.MouseEvent,
45
+ fromItemId: string,
46
+ toItemId: string,
47
+ itemId: string
48
+ ) => void;
44
49
  /** @beta Callback for drilling out of a submenu */
45
- onDrillOut?: (toItemId: string, itemId: string) => void;
50
+ onDrillOut?: (event: React.KeyboardEvent | React.MouseEvent, toItemId: string, itemId: string) => void;
46
51
  /** @beta Callback for collecting menu heights */
47
52
  onGetMenuHeight?: (menuId: string, height: number) => void;
48
53
  /** @beta ID of parent menu for drilldown menus */
@@ -9,8 +9,13 @@ export const MenuContext = React.createContext<{
9
9
  selected?: any | any[];
10
10
  drilldownItemPath?: string[];
11
11
  drilledInMenus?: string[];
12
- onDrillIn?: (fromItemId: string, toItemId: string, itemId: string) => void;
13
- onDrillOut?: (toItemId: string, itemId: string) => void;
12
+ onDrillIn?: (
13
+ event: React.KeyboardEvent | React.MouseEvent,
14
+ fromItemId: string,
15
+ toItemId: string,
16
+ itemId: string
17
+ ) => void;
18
+ onDrillOut?: (event: React.KeyboardEvent | React.MouseEvent, toItemId: string, itemId: string) => void;
14
19
  onGetMenuHeight?: (menuId: string, height: number) => void;
15
20
  flyoutRef?: React.Ref<HTMLLIElement>;
16
21
  setFlyoutRef?: (ref: React.Ref<HTMLLIElement>) => void;
@@ -224,12 +224,13 @@ const MenuItemBase: React.FunctionComponent<MenuItemProps> = ({
224
224
  onClick && onClick(event);
225
225
  };
226
226
  const _isOnPath = (isOnPath && isOnPath) || (drilldownItemPath && drilldownItemPath.includes(itemId)) || false;
227
- let _drill: () => void;
227
+ let drill: (event: React.KeyboardEvent | React.MouseEvent) => void;
228
228
  if (direction) {
229
229
  if (direction === 'down') {
230
- _drill = () =>
230
+ drill = event =>
231
231
  onDrillIn &&
232
232
  onDrillIn(
233
+ event,
233
234
  menuId,
234
235
  typeof drilldownMenu === 'function'
235
236
  ? (drilldownMenu() as any).props.id
@@ -237,7 +238,7 @@ const MenuItemBase: React.FunctionComponent<MenuItemProps> = ({
237
238
  itemId
238
239
  );
239
240
  } else {
240
- _drill = () => onDrillOut && onDrillOut(parentMenu, itemId);
241
+ drill = event => onDrillOut && onDrillOut(event, parentMenu, itemId);
241
242
  }
242
243
  }
243
244
  let additionalProps = {} as any;
@@ -318,9 +319,9 @@ const MenuItemBase: React.FunctionComponent<MenuItemProps> = ({
318
319
  {...(!hasCheck && !flyoutMenu && { role: 'menuitem' })}
319
320
  ref={innerRef}
320
321
  {...(!hasCheck && {
321
- onClick: (event: any) => {
322
+ onClick: (event: React.KeyboardEvent | React.MouseEvent) => {
322
323
  onItemSelect(event, onSelect);
323
- _drill && _drill();
324
+ drill && drill(event);
324
325
  flyoutMenu && handleFlyout(event);
325
326
  }
326
327
  })}
@@ -18,114 +18,173 @@ import AngleLeftIcon from '@patternfly/react-icons/dist/esm/icons/angle-left-ico
18
18
 
19
19
  ## Examples
20
20
 
21
- ### Basic
21
+ ### Basic menus
22
+
23
+ A menu may contain multiple variations of `<MenuItem>` components. The following example shows a few different states of menu items, where they may:
24
+
25
+ - Use the `itemId` property to link to callbacks. In this example, the `onSelect` property logs information to the console when a menu item is selected. In practice, specific actions can be linked to `onSelect` callbacks.
26
+ - Use the `to` property to direct users to other resources or webpages after selecting a menu item, and the `onClick` property to pass in a callback for specific menu items.
27
+ - Use the `isDisabled` property to disable a menu item.
28
+ - Use the `isPlain` property to remove the outer box shadow and style the menu plainly instead.
22
29
 
23
30
  ```ts file="MenuBasic.tsx"
24
31
  ```
25
32
 
26
33
  ### Danger menu item
27
34
 
35
+ To indicate that a `<MenuItem>` is connected to a potentially destructive action, use the `isDanger` property. The "Delete" item in the following example demonstrates how danger items look.
36
+
28
37
  ```ts file="MenuDangerMenuItem.tsx"
29
38
  ```
30
39
 
31
40
  ### With icons
32
41
 
42
+ Use the `icon` property to add a familiar icon before a `<MenuItem>` to accelerate text label recognition.
43
+
33
44
  ```ts file="MenuWithIcons.tsx"
34
45
  ```
35
46
 
36
- ### With checkbox
47
+ ### With actions
48
+
49
+ To connect a menu item to an action icon, add a `<MenuItemAction>` to a `<MenuItem>`, and use the `icon` property to load an easily recognizable icon.
37
50
 
38
- ```ts file="./MenuWithCheckbox.tsx"
39
- ```
51
+ To trigger an action when any menu action icon is selected, pass a callback to the `onActionClick` property of the `<Menu>`. The following example logs to the console any time any action icon is selected.
40
52
 
41
- ### Filtering with search input
53
+ To trigger an action when a specific item's action icon is selected, pass in the `onClick` property to that `<MenuItemAction>`. The following example logs "clicked on code icon" to the console when the "code" icon is selected.
42
54
 
43
- ```ts file="MenuFilteringWithSearchInput.tsx"
55
+ ```ts file="MenuWithActions.tsx"
44
56
  ```
45
57
 
46
58
  ### With links
47
59
 
60
+ Use the `to` property to add a link to a `<MenuItem>` that directs users to a new page when the item is selected. Use the `isExternalLink` property when linking to external resources. This will annotate a menu item link with an external link icon when they navigate to the link or hover over it.
61
+
48
62
  ```ts file="MenuWithLinks.tsx"
49
63
  ```
50
64
 
51
- ### With separator(s)
65
+ ### With descriptions
52
66
 
53
- ```ts file="MenuWithSeparators.tsx"
67
+ Use the `description` property to add short descriptive text below any menu item that needs additional context.
68
+
69
+ ```ts file="MenuWithDescription.tsx"
54
70
  ```
55
71
 
56
- ### With titled groups
72
+ ### Item checkbox
57
73
 
58
- ```ts file="MenuWithTitledGroups.tsx"
74
+ Use the `hasCheck` property to add a checkbox to a `<MenuItem>`. Use the `isSelected` property to indicate when a `<MenuItem>` is selected.
75
+
76
+ ```ts file="./MenuWithCheckbox.tsx" isBeta
59
77
  ```
60
78
 
61
- ### With description
79
+ ### Menu footer
62
80
 
63
- ```ts file="MenuWithDescription.tsx"
81
+ Add a `<MenuFooter>` that contains separate, but related actions at the bottom of a menu.
82
+
83
+ ```ts file="MenuWithFooter.tsx"
64
84
  ```
65
85
 
66
- ### With actions
86
+ ### Separated items
67
87
 
68
- ```ts file="MenuWithActions.tsx"
88
+ Use a [divider](/components/divider) to visually separate `<MenuContent>`. Use the `component` property to specify the type of divider component to use.
89
+
90
+ ```ts file="MenuWithSeparators.tsx"
91
+ ```
92
+
93
+ ### Titled groups of items
94
+
95
+ Add a `<MenuGroup>` to organize `<MenuContent>` and use the `label` property to title a group of menu items. Use the `labelHeadingLevel` property to assign a heading level to the menu group label.
96
+
97
+ ```ts file="MenuWithTitledGroups.tsx"
69
98
  ```
70
99
 
71
100
  ### With favorites
72
101
 
102
+ The following menu example allows users to favorite menu items, an action that duplicates a menu item and places it in a separate group at the top of the menu. The `isFavorited` property identifies items that a user has favorited.
103
+
73
104
  ```ts file="MenuWithFavorites.tsx"
74
105
  ```
75
106
 
76
- ### Option single select
107
+ ### Filtering with search input
108
+
109
+ A [search input](/components/search-input) component can be placed within `<MenuInput>` to render a search input at the top of the menu. In the following example, the `onChange` property of the text input is passed a callback that filters menu items as a user types.
110
+
111
+ ```ts file="MenuFilteringWithSearchInput.tsx"
112
+ ```
113
+
114
+ ### Option single select menu
115
+
116
+ The following example demonstrates a single option select menu that persists a selected menu item. Use the `selected` property on the `<Menu>` to label a selected item with a checkmark. You can also use the `isSelected` property on a `<MenuItem>` to indicate that it is selected.
77
117
 
78
118
  ```ts file="MenuOptionSingleSelect.tsx"
79
119
  ```
80
120
 
81
- ### Option multi select
121
+ ### Option multi select menu
122
+
123
+ To persist multiple selections that a user makes, use a multiple option select menu. To enable multi select, pass an array containing each selected `itemId` to the `selected` property.
82
124
 
83
125
  ```ts file="MenuOptionMultiSelect.tsx"
84
126
  ```
85
127
 
86
- ### With drilldown
128
+ ### Scrollable menus
87
129
 
88
- ```ts file="./MenuWithDrilldown.tsx" isBeta
130
+ Use the `isScrollable` property to make a long `<Menu>` scrollable and visually condensed.
131
+
132
+ ```ts file="MenuScrollable.tsx"
89
133
  ```
90
134
 
91
- ### With drilldown - initial drill in state
135
+ ### Scrollable menu with custom height
92
136
 
93
- To render an initially drilled in menu, the `menuDrilledIn`, `drilldownPath`, and `activeMenu` states must be set to an initial state. The `menuHeights` state must also be set, defining the height of the root menu. The `setHeight` function passed into the `onGetMenuHeight` property must also account for updating heights, other than the root menu, as menus drill in and out of view.
137
+ Adjust the visual size of a scrollable menu by using the `menuHeight` property within `<MenuContent>`. This example adjusts the height to 200px.
94
138
 
95
- ```ts file="./MenuWithDrilldownInitialState.tsx" isBeta
139
+ ```ts file="MenuScrollableCustomMenuHeight.tsx"
96
140
  ```
97
141
 
98
- ### With drilldown - submenu functions
142
+ ### With view more
99
143
 
100
- ```ts file="./MenuWithDrilldownSubmenuFunctions.tsx" isBeta
101
- ```
144
+ If you want to initially render only a certain number of menu items within a large menu, you can add a "view more" menu item with a callback passed into its `onClick` property that will render additional menu items.
102
145
 
103
- ### With drilldown breadcrumbs
146
+ In this example, 3 additional menu items are revealed each time the "view more" option is selected, with a loading icon simulating a network call to fetch more items. After all items are visible, the "view more" link disappears.
104
147
 
105
- ```ts file="MenuWithDrilldownBreadcrumbs.tsx" isBeta
148
+ ```ts file="MenuWithViewMore.tsx"
106
149
  ```
107
150
 
108
- ### With drilldown and inline filter
151
+ ### With drilldown
109
152
 
110
- ```ts file="MenuFilterDrilldown.tsx"
111
- ```
153
+ Use a drilldown menu to contain different levels of menu items. When a parent menu item (an item that has a submenu of children) is selected, the menu is replaced with the children items.
112
154
 
113
- ### Scrollable
155
+ - To indicate that a menu contains a drilldown, use the `containsDrilldown` property.
156
+ - To indicate the path of drilled-in menu item ids, use the `drilldownItemPath` property.
157
+ - Pass in an array of drilled-in menus with the `drilledInMenus` property.
158
+ - Use the `onDrillIn` and `onDrillOut` properties to contain callbacks for drilling into and drilling out of a submenu, respectively.
159
+ - To account for updated heights as menus drill in and out of use, use the `onGetMenuHeight` property. When starting from a drilled-in state, the `onGetMenuHeight` property must define the height of the root menu.
114
160
 
115
- ```ts file="MenuScrollable.tsx"
161
+ ```ts file="./MenuWithDrilldown.tsx" isBeta
116
162
  ```
117
163
 
118
- ### Scrollable with custom menu height
164
+ ### Initially drilled-in menu
119
165
 
120
- ```ts file="MenuScrollableCustomMenuHeight.tsx"
166
+ To render an initially drilled-in menu, the `drilldownItemPath`, `drilledInMenus`, and `activeMenu` properties must be set to default values.
167
+
168
+ ```ts file="./MenuWithDrilldownInitialState.tsx" isBeta
121
169
  ```
122
170
 
123
- ### With footer
171
+ ### With drilldown - submenu functions
124
172
 
125
- ```ts file="MenuWithFooter.tsx"
173
+ For added flexibility with large menus, you may create a menu by passing a function to `drilldownMenu`. This approach allows you to create menu items dynamically, rather than creating everything up front.
174
+
175
+ ```ts file="./MenuWithDrilldownSubmenuFunctions.tsx" isBeta
126
176
  ```
127
177
 
128
- ### With view more
178
+ ### With drilldown breadcrumbs
129
179
 
130
- ```ts file="MenuWithViewMore.tsx"
180
+ Use [breadcrumbs](/components/breadcrumb) when a drilldown menu has more than 2 levels to offer users better navigation.
181
+
182
+ To control the height of a menu, use the `maxMenuHeight` property. Selecting the "Set max menu height" checkbox in the following example sets the menu height to "100px" and makes the menu scrollable.
183
+
184
+ ```ts file="MenuWithDrilldownBreadcrumbs.tsx" isBeta
131
185
  ```
186
+
187
+ ### With drilldown and inline filter
188
+
189
+ ```ts file="MenuFilterDrilldown.tsx"
190
+ ```
@@ -16,13 +16,18 @@ export const MenuWithDrilldown: React.FunctionComponent = () => {
16
16
  const [menuHeights, setMenuHeights] = React.useState<any>({});
17
17
  const [activeMenu, setActiveMenu] = React.useState<string>('filter_drilldown-rootMenu');
18
18
 
19
- const drillIn = (fromMenuId: string, toMenuId: string, pathId: string) => {
19
+ const drillIn = (
20
+ _event: React.KeyboardEvent | React.MouseEvent,
21
+ fromMenuId: string,
22
+ toMenuId: string,
23
+ pathId: string
24
+ ) => {
20
25
  setMenuDrilledIn([...menuDrilledIn, fromMenuId]);
21
26
  setDrilldownPath([...drilldownPath, pathId]);
22
27
  setActiveMenu(toMenuId);
23
28
  };
24
29
 
25
- const drillOut = (toMenuId: string) => {
30
+ const drillOut = (_event: React.KeyboardEvent | React.MouseEvent, toMenuId: string) => {
26
31
  const menuDrilledInSansLast = menuDrilledIn.slice(0, menuDrilledIn.length - 1);
27
32
  const pathSansLast = drilldownPath.slice(0, drilldownPath.length - 1);
28
33
  setMenuDrilledIn(menuDrilledInSansLast);
@@ -11,13 +11,18 @@ export const MenuWithDrilldown: React.FunctionComponent = () => {
11
11
  const [menuHeights, setMenuHeights] = React.useState<any>({});
12
12
  const [activeMenu, setActiveMenu] = React.useState<string>('drilldown-rootMenu');
13
13
 
14
- const drillIn = (fromMenuId: string, toMenuId: string, pathId: string) => {
14
+ const drillIn = (
15
+ _event: React.KeyboardEvent | React.MouseEvent,
16
+ fromMenuId: string,
17
+ toMenuId: string,
18
+ pathId: string
19
+ ) => {
15
20
  setMenuDrilledIn([...menuDrilledIn, fromMenuId]);
16
21
  setDrilldownPath([...drilldownPath, pathId]);
17
22
  setActiveMenu(toMenuId);
18
23
  };
19
24
 
20
- const drillOut = (toMenuId: string) => {
25
+ const drillOut = (_event: React.KeyboardEvent | React.MouseEvent, toMenuId: string) => {
21
26
  const menuDrilledInSansLast = menuDrilledIn.slice(0, menuDrilledIn.length - 1);
22
27
  const pathSansLast = drilldownPath.slice(0, drilldownPath.length - 1);
23
28
  setMenuDrilledIn(menuDrilledInSansLast);
@@ -52,7 +52,12 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
52
52
  setWithMaxMenuHeight(checked);
53
53
  };
54
54
 
55
- const drillOut = (toMenuId: string, fromPathId: string, breadcrumb: JSX.Element | null) => {
55
+ const drillOut = (
56
+ _event: React.KeyboardEvent<Element> | MouseEvent | React.MouseEvent<any, MouseEvent>,
57
+ toMenuId: string,
58
+ fromPathId: string,
59
+ breadcrumb: JSX.Element | null
60
+ ) => {
56
61
  setMenuDrilledIn(prevMenuDrilledIn => {
57
62
  const indexOfMenuId = prevMenuDrilledIn.indexOf(toMenuId);
58
63
  return prevMenuDrilledIn.slice(0, indexOfMenuId);
@@ -71,7 +76,12 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
71
76
  }
72
77
  };
73
78
 
74
- const drillIn = (fromMenuId: string, toMenuId: string, pathId: string) => {
79
+ const drillIn = (
80
+ _event: React.KeyboardEvent | React.MouseEvent,
81
+ fromMenuId: string,
82
+ toMenuId: string,
83
+ pathId: string
84
+ ) => {
75
85
  setMenuDrilledIn([...menuDrilledIn, fromMenuId]);
76
86
  setDrilldownPath([...drilldownPath, pathId]);
77
87
  setActiveMenu(toMenuId);
@@ -79,7 +89,10 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
79
89
 
80
90
  const startRolloutBreadcrumb = (
81
91
  <Breadcrumb>
82
- <BreadcrumbItem component="button" onClick={() => drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
92
+ <BreadcrumbItem
93
+ component="button"
94
+ onClick={event => drillOut(event, 'breadcrumbs-rootMenu', 'group:start_rollout', null)}
95
+ >
83
96
  Root
84
97
  </BreadcrumbItem>
85
98
  <BreadcrumbHeading component="button">Start rollout</BreadcrumbHeading>
@@ -88,7 +101,10 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
88
101
 
89
102
  const appGroupingBreadcrumb = (isOpen: boolean) => (
90
103
  <Breadcrumb>
91
- <BreadcrumbItem component="button" onClick={() => drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
104
+ <BreadcrumbItem
105
+ component="button"
106
+ onClick={event => drillOut(event, 'breadcrumbs-rootMenu', 'group:start_rollout', null)}
107
+ >
92
108
  Root
93
109
  </BreadcrumbItem>
94
110
  <BreadcrumbItem isDropdown>
@@ -104,8 +120,8 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
104
120
  key="dropdown-start"
105
121
  component="button"
106
122
  icon={<AngleLeftIcon />}
107
- onClick={() =>
108
- drillOut('breadcrumbs-drilldownMenuStart', 'group:app_grouping_start', startRolloutBreadcrumb)
123
+ onClick={event =>
124
+ drillOut(event, 'breadcrumbs-drilldownMenuStart', 'group:app_grouping_start', startRolloutBreadcrumb)
109
125
  }
110
126
  >
111
127
  Start rollout
@@ -119,7 +135,10 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
119
135
 
120
136
  const labelsBreadcrumb = (isOpen: boolean) => (
121
137
  <Breadcrumb>
122
- <BreadcrumbItem component="button" onClick={() => drillOut('breadcrumbs-rootMenu', 'group:start_rollout', null)}>
138
+ <BreadcrumbItem
139
+ component="button"
140
+ onClick={event => drillOut(event, 'breadcrumbs-rootMenu', 'group:start_rollout', null)}
141
+ >
123
142
  Root
124
143
  </BreadcrumbItem>
125
144
  <BreadcrumbItem isDropdown>
@@ -135,7 +154,9 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
135
154
  key="dropdown-start"
136
155
  component="button"
137
156
  icon={<AngleLeftIcon />}
138
- onClick={() => drillOut('breadcrumbs-drilldownMenuStart', 'group:labels_start', startRolloutBreadcrumb)}
157
+ onClick={event =>
158
+ drillOut(event, 'breadcrumbs-drilldownMenuStart', 'group:labels_start', startRolloutBreadcrumb)
159
+ }
139
160
  >
140
161
  Start rollout
141
162
  </DropdownItem>
@@ -148,7 +169,10 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
148
169
 
149
170
  const pauseRolloutsBreadcrumb = (
150
171
  <Breadcrumb>
151
- <BreadcrumbItem component="button" onClick={() => drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
172
+ <BreadcrumbItem
173
+ component="button"
174
+ onClick={event => drillOut(event, 'breadcrumbs-rootMenu', 'group:pause_rollout', null)}
175
+ >
152
176
  Root
153
177
  </BreadcrumbItem>
154
178
  <BreadcrumbHeading component="button">Pause rollouts</BreadcrumbHeading>
@@ -157,7 +181,10 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
157
181
 
158
182
  const pauseRolloutsAppGrpBreadcrumb = (isOpen: boolean) => (
159
183
  <Breadcrumb>
160
- <BreadcrumbItem component="button" onClick={() => drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
184
+ <BreadcrumbItem
185
+ component="button"
186
+ onClick={event => drillOut(event, 'breadcrumbs-rootMenu', 'group:pause_rollout', null)}
187
+ >
161
188
  Root
162
189
  </BreadcrumbItem>
163
190
  <BreadcrumbItem isDropdown>
@@ -173,7 +200,9 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
173
200
  key="dropdown-pause"
174
201
  component="button"
175
202
  icon={<AngleLeftIcon />}
176
- onClick={() => drillOut('breadcrumbs-drilldownMenuPause', 'group:app_grouping', pauseRolloutsBreadcrumb)}
203
+ onClick={event =>
204
+ drillOut(event, 'breadcrumbs-drilldownMenuPause', 'group:app_grouping', pauseRolloutsBreadcrumb)
205
+ }
177
206
  >
178
207
  Pause rollouts
179
208
  </DropdownItem>
@@ -186,7 +215,10 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
186
215
 
187
216
  const pauseRolloutsLabelsBreadcrumb = (isOpen: boolean) => (
188
217
  <Breadcrumb>
189
- <BreadcrumbItem component="button" onClick={() => drillOut('breadcrumbs-rootMenu', 'group:pause_rollout', null)}>
218
+ <BreadcrumbItem
219
+ component="button"
220
+ onClick={event => drillOut(event, 'breadcrumbs-rootMenu', 'group:pause_rollout', null)}
221
+ >
190
222
  Root
191
223
  </BreadcrumbItem>
192
224
  <BreadcrumbItem isDropdown>
@@ -202,7 +234,9 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
202
234
  key="dropdown-pause"
203
235
  component="button"
204
236
  icon={<AngleLeftIcon />}
205
- onClick={() => drillOut('breadcrumbs-drilldownMenuPause', 'group:labels', pauseRolloutsBreadcrumb)}
237
+ onClick={event =>
238
+ drillOut(event, 'breadcrumbs-drilldownMenuPause', 'group:labels', pauseRolloutsBreadcrumb)
239
+ }
206
240
  >
207
241
  Pause rollouts
208
242
  </DropdownItem>
@@ -215,7 +249,10 @@ export const MenuWithDrilldownBreadcrumbs: React.FunctionComponent = () => {
215
249
 
216
250
  const addStorageBreadcrumb = (
217
251
  <Breadcrumb>
218
- <BreadcrumbItem component="button" onClick={() => drillOut('breadcrumbs-rootMenu', 'group:storage', null)}>
252
+ <BreadcrumbItem
253
+ component="button"
254
+ onClick={event => drillOut(event, 'breadcrumbs-rootMenu', 'group:storage', null)}
255
+ >
219
256
  Root
220
257
  </BreadcrumbItem>
221
258
  <BreadcrumbHeading component="button">Add storage</BreadcrumbHeading>