@patternfly/react-core 4.274.1 → 4.276.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 (83) hide show
  1. package/CHANGELOG.md +23 -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/components/Select/Select.d.ts +2 -0
  16. package/components/Select/Select.d.ts.map +1 -1
  17. package/components/Select/Select.js +3 -3
  18. package/components/Select/Select.js.map +1 -1
  19. package/components/Select/SelectToggle.d.ts +4 -14
  20. package/components/Select/SelectToggle.d.ts.map +1 -1
  21. package/components/Select/SelectToggle.js +11 -5
  22. package/components/Select/SelectToggle.js.map +1 -1
  23. package/dist/esm/components/Accordion/AccordionContent.d.ts +2 -0
  24. package/dist/esm/components/Accordion/AccordionContent.d.ts.map +1 -1
  25. package/dist/esm/components/Accordion/AccordionContent.js +13 -2
  26. package/dist/esm/components/Accordion/AccordionContent.js.map +1 -1
  27. package/dist/esm/components/Menu/Menu.d.ts +2 -2
  28. package/dist/esm/components/Menu/Menu.d.ts.map +1 -1
  29. package/dist/esm/components/Menu/Menu.js.map +1 -1
  30. package/dist/esm/components/Menu/MenuContext.d.ts +2 -2
  31. package/dist/esm/components/Menu/MenuContext.d.ts.map +1 -1
  32. package/dist/esm/components/Menu/MenuContext.js.map +1 -1
  33. package/dist/esm/components/Menu/MenuItem.d.ts.map +1 -1
  34. package/dist/esm/components/Menu/MenuItem.js +5 -5
  35. package/dist/esm/components/Menu/MenuItem.js.map +1 -1
  36. package/dist/esm/components/Select/Select.d.ts +2 -0
  37. package/dist/esm/components/Select/Select.d.ts.map +1 -1
  38. package/dist/esm/components/Select/Select.js +3 -3
  39. package/dist/esm/components/Select/Select.js.map +1 -1
  40. package/dist/esm/components/Select/SelectToggle.d.ts +4 -14
  41. package/dist/esm/components/Select/SelectToggle.d.ts.map +1 -1
  42. package/dist/esm/components/Select/SelectToggle.js +11 -5
  43. package/dist/esm/components/Select/SelectToggle.js.map +1 -1
  44. package/dist/js/components/Accordion/AccordionContent.d.ts +2 -0
  45. package/dist/js/components/Accordion/AccordionContent.d.ts.map +1 -1
  46. package/dist/js/components/Accordion/AccordionContent.js +13 -2
  47. package/dist/js/components/Accordion/AccordionContent.js.map +1 -1
  48. package/dist/js/components/Menu/Menu.d.ts +2 -2
  49. package/dist/js/components/Menu/Menu.d.ts.map +1 -1
  50. package/dist/js/components/Menu/Menu.js.map +1 -1
  51. package/dist/js/components/Menu/MenuContext.d.ts +2 -2
  52. package/dist/js/components/Menu/MenuContext.d.ts.map +1 -1
  53. package/dist/js/components/Menu/MenuContext.js.map +1 -1
  54. package/dist/js/components/Menu/MenuItem.d.ts.map +1 -1
  55. package/dist/js/components/Menu/MenuItem.js +5 -5
  56. package/dist/js/components/Menu/MenuItem.js.map +1 -1
  57. package/dist/js/components/Select/Select.d.ts +2 -0
  58. package/dist/js/components/Select/Select.d.ts.map +1 -1
  59. package/dist/js/components/Select/Select.js +3 -3
  60. package/dist/js/components/Select/Select.js.map +1 -1
  61. package/dist/js/components/Select/SelectToggle.d.ts +4 -14
  62. package/dist/js/components/Select/SelectToggle.d.ts.map +1 -1
  63. package/dist/js/components/Select/SelectToggle.js +11 -6
  64. package/dist/js/components/Select/SelectToggle.js.map +1 -1
  65. package/dist/umd/react-core.min.js +2 -2
  66. package/package.json +2 -2
  67. package/src/components/Accordion/AccordionContent.tsx +45 -23
  68. package/src/components/Accordion/__tests__/AccordionContent.test.tsx +15 -2
  69. package/src/components/Accordion/examples/AccordionFixedWithMultipleExpandBehavior.tsx +15 -1
  70. package/src/components/Menu/Menu.tsx +7 -2
  71. package/src/components/Menu/MenuContext.ts +7 -2
  72. package/src/components/Menu/MenuItem.tsx +6 -5
  73. package/src/components/Menu/examples/MenuFilterDrilldown.tsx +7 -2
  74. package/src/components/Menu/examples/MenuWithDrilldown.tsx +7 -2
  75. package/src/components/Menu/examples/MenuWithDrilldownBreadcrumbs.tsx +51 -14
  76. package/src/components/Menu/examples/MenuWithDrilldownInitialState.tsx +7 -2
  77. package/src/components/Menu/examples/MenuWithDrilldownSubmenuFunctions.tsx +7 -2
  78. package/src/components/Nav/examples/NavDrilldown.tsx +7 -2
  79. package/src/components/Select/Select.tsx +4 -0
  80. package/src/components/Select/SelectToggle.tsx +14 -3
  81. package/src/components/Select/examples/Select.md +28 -0
  82. package/src/demos/ComposableMenu/examples/ComposableDrilldownMenu.tsx +7 -2
  83. 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.274.1",
3
+ "version": "4.276.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",
@@ -71,5 +71,5 @@
71
71
  "react": "^16.8 || ^17 || ^18",
72
72
  "react-dom": "^16.8 || ^17 || ^18"
73
73
  },
74
- "gitHead": "fd8b16d68a5bc20b711e26e0260066e1bc1bda2b"
74
+ "gitHead": "7b3cf4f9f882639970c7b885d72f7153be97e652"
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
  })}
@@ -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>
@@ -14,13 +14,18 @@ export const MenuDrilldownInitialState: React.FunctionComponent = () => {
14
14
  const [menuHeights, setMenuHeights] = React.useState<any>({ 'initial-state-rootMenu': 216 }); // The root menu height must be defined when starting from a drilled in state
15
15
  const [activeMenu, setActiveMenu] = React.useState<string>('initial-state-drilldownMenuStartGrouping');
16
16
 
17
- const drillIn = (fromMenuId: string, toMenuId: string, pathId: string) => {
17
+ const drillIn = (
18
+ _event: React.KeyboardEvent | React.MouseEvent,
19
+ fromMenuId: string,
20
+ toMenuId: string,
21
+ pathId: string
22
+ ) => {
18
23
  setMenuDrilledIn([...menuDrilledIn, fromMenuId]);
19
24
  setDrilldownPath([...drilldownPath, pathId]);
20
25
  setActiveMenu(toMenuId);
21
26
  };
22
27
 
23
- const drillOut = (toMenuId: string) => {
28
+ const drillOut = (_event: React.KeyboardEvent | React.MouseEvent, toMenuId: string) => {
24
29
  const menuDrilledInSansLast = menuDrilledIn.slice(0, menuDrilledIn.length - 1);
25
30
  const pathSansLast = drilldownPath.slice(0, drilldownPath.length - 1);
26
31
  setMenuDrilledIn(menuDrilledInSansLast);
@@ -11,13 +11,18 @@ export const MenuWithDrilldownSubmenuFunctions: React.FunctionComponent = () =>
11
11
  const [menuHeights, setMenuHeights] = React.useState<any>({});
12
12
  const [activeMenu, setActiveMenu] = React.useState<string>('functions-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);
@@ -51,13 +51,18 @@ export const NavigationDrilldown: React.FunctionComponent = () => {
51
51
  const [menuHeights, setMenuHeights] = React.useState<MenuHeights>({});
52
52
  const [activeMenu, setActiveMenu] = React.useState('rootMenu');
53
53
 
54
- const onDrillIn = (fromItemId: string, toItemId: string, itemId: string) => {
54
+ const onDrillIn = (
55
+ _event: React.KeyboardEvent | React.MouseEvent,
56
+ fromItemId: string,
57
+ toItemId: string,
58
+ itemId: string
59
+ ) => {
55
60
  setMenuDrilledIn(prevMenuDrilledIn => [...prevMenuDrilledIn, fromItemId]);
56
61
  setDrilldownPath(prevDrilldownPath => [...prevDrilldownPath, itemId]);
57
62
  setActiveMenu(toItemId);
58
63
  };
59
64
 
60
- const onDrillOut = (toItemId: string, _itemId: string) => {
65
+ const onDrillOut = (_event: React.KeyboardEvent | React.MouseEvent, toItemId: string, _itemId: string) => {
61
66
  setMenuDrilledIn(prevMenuDrilledIn => prevMenuDrilledIn.slice(0, prevMenuDrilledIn.length - 1));
62
67
  setDrilldownPath(prevDrilldownPath => prevDrilldownPath.slice(0, prevDrilldownPath.length - 1));
63
68
  setActiveMenu(toItemId);
@@ -95,6 +95,8 @@ export interface SelectProps
95
95
  isCheckboxSelectionBadgeHidden?: boolean;
96
96
  /** Id for select toggle element */
97
97
  toggleId?: string;
98
+ /** Ref for the select toggle element */
99
+ toggleRef?: React.Ref<HTMLButtonElement> | React.Ref<HTMLDivElement>;
98
100
  /** Adds accessible text to Select */
99
101
  'aria-label'?: string;
100
102
  /** Id of label for the Select aria-labelledby */
@@ -983,6 +985,7 @@ export class Select extends React.Component<SelectProps & OUIAProps, SelectState
983
985
  onClear,
984
986
  onBlur,
985
987
  toggleId,
988
+ toggleRef,
986
989
  isOpen,
987
990
  isGrouped,
988
991
  isPlain,
@@ -1325,6 +1328,7 @@ export class Select extends React.Component<SelectProps & OUIAProps, SelectState
1325
1328
  id={selectToggleId}
1326
1329
  parentRef={this.parentRef}
1327
1330
  menuRef={this.menuComponentRef}
1331
+ ref={toggleRef}
1328
1332
  {...(footer && { footerRef: this.footerRef })}
1329
1333
  isOpen={isOpen}
1330
1334
  isPlain={isPlain}
@@ -8,7 +8,7 @@ import { PickOptional } from '../../helpers/typeUtils';
8
8
  import { findTabbableElements } from '../../helpers/util';
9
9
  import { KeyTypes } from '../../helpers/constants';
10
10
 
11
- export interface SelectToggleProps extends React.HTMLProps<HTMLElement> {
11
+ export interface SelectToggleProps extends Omit<React.HTMLProps<HTMLElement>, 'ref'> {
12
12
  /** HTML ID of dropdown toggle */
13
13
  id: string;
14
14
  /** Anything which can be rendered as dropdown toggle */
@@ -59,9 +59,11 @@ export interface SelectToggleProps extends React.HTMLProps<HTMLElement> {
59
59
  hasFooter?: boolean;
60
60
  /** @hide Internal callback for handling focus when typeahead toggle button clicked. */
61
61
  onClickTypeaheadToggleButton?: () => void;
62
+ /** @hide Internal ref for the select toggle */
63
+ innerRef?: React.Ref<any>;
62
64
  }
63
65
 
64
- export class SelectToggle extends React.Component<SelectToggleProps> {
66
+ class SelectToggleBase extends React.Component<SelectToggleProps> {
65
67
  static displayName = 'SelectToggle';
66
68
  private toggle: React.RefObject<HTMLDivElement> | React.RefObject<HTMLButtonElement>;
67
69
 
@@ -88,7 +90,11 @@ export class SelectToggle extends React.Component<SelectToggleProps> {
88
90
  super(props);
89
91
  const { variant } = props;
90
92
  const isTypeahead = variant === SelectVariant.typeahead || variant === SelectVariant.typeaheadMulti;
91
- this.toggle = isTypeahead ? React.createRef<HTMLDivElement>() : React.createRef<HTMLButtonElement>();
93
+ if (this.props.innerRef) {
94
+ this.toggle = this.props.innerRef as React.RefObject<HTMLButtonElement> | React.RefObject<HTMLDivElement>;
95
+ } else {
96
+ this.toggle = isTypeahead ? React.createRef<HTMLDivElement>() : React.createRef<HTMLButtonElement>();
97
+ }
92
98
  }
93
99
 
94
100
  componentDidMount() {
@@ -265,6 +271,7 @@ export class SelectToggle extends React.Component<SelectToggleProps> {
265
271
  hasFooter,
266
272
  footerRef,
267
273
  toggleIndicator,
274
+ innerRef,
268
275
  ...props
269
276
  } = this.props;
270
277
  /* eslint-enable @typescript-eslint/no-unused-vars */
@@ -363,3 +370,7 @@ export class SelectToggle extends React.Component<SelectToggleProps> {
363
370
  );
364
371
  }
365
372
  }
373
+
374
+ export const SelectToggle = React.forwardRef((props: SelectToggleProps, ref: React.Ref<any>) => (
375
+ <SelectToggleBase innerRef={ref} {...props} />
376
+ ));