@patternfly/react-core 5.4.9 → 5.4.11

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 (176) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/components/package.json +1 -1
  3. package/deprecated/package.json +1 -1
  4. package/dist/dynamic/components/AboutModal/package.json +1 -1
  5. package/dist/dynamic/components/Accordion/package.json +1 -1
  6. package/dist/dynamic/components/ActionList/package.json +1 -1
  7. package/dist/dynamic/components/Alert/package.json +1 -1
  8. package/dist/dynamic/components/Avatar/package.json +1 -1
  9. package/dist/dynamic/components/BackToTop/package.json +1 -1
  10. package/dist/dynamic/components/Backdrop/package.json +1 -1
  11. package/dist/dynamic/components/BackgroundImage/package.json +1 -1
  12. package/dist/dynamic/components/Badge/package.json +1 -1
  13. package/dist/dynamic/components/Banner/package.json +1 -1
  14. package/dist/dynamic/components/Brand/package.json +1 -1
  15. package/dist/dynamic/components/Breadcrumb/package.json +1 -1
  16. package/dist/dynamic/components/Button/package.json +1 -1
  17. package/dist/dynamic/components/CalendarMonth/package.json +1 -1
  18. package/dist/dynamic/components/Card/package.json +1 -1
  19. package/dist/dynamic/components/Checkbox/package.json +1 -1
  20. package/dist/dynamic/components/Chip/package.json +1 -1
  21. package/dist/dynamic/components/ClipboardCopy/package.json +1 -1
  22. package/dist/dynamic/components/CodeBlock/package.json +1 -1
  23. package/dist/dynamic/components/DataList/package.json +1 -1
  24. package/dist/dynamic/components/DatePicker/package.json +1 -1
  25. package/dist/dynamic/components/DescriptionList/package.json +1 -1
  26. package/dist/dynamic/components/Divider/package.json +1 -1
  27. package/dist/dynamic/components/DragDrop/package.json +1 -1
  28. package/dist/dynamic/components/Drawer/package.json +1 -1
  29. package/dist/dynamic/components/Dropdown/package.json +1 -1
  30. package/dist/dynamic/components/DualListSelector/package.json +1 -1
  31. package/dist/dynamic/components/EmptyState/package.json +1 -1
  32. package/dist/dynamic/components/ExpandableSection/package.json +1 -1
  33. package/dist/dynamic/components/FileUpload/package.json +1 -1
  34. package/dist/dynamic/components/Form/package.json +1 -1
  35. package/dist/dynamic/components/FormSelect/package.json +1 -1
  36. package/dist/dynamic/components/HelperText/package.json +1 -1
  37. package/dist/dynamic/components/Hint/package.json +1 -1
  38. package/dist/dynamic/components/Icon/package.json +1 -1
  39. package/dist/dynamic/components/InputGroup/package.json +1 -1
  40. package/dist/dynamic/components/JumpLinks/package.json +1 -1
  41. package/dist/dynamic/components/Label/package.json +1 -1
  42. package/dist/dynamic/components/List/package.json +1 -1
  43. package/dist/dynamic/components/LoginPage/package.json +1 -1
  44. package/dist/dynamic/components/Masthead/package.json +1 -1
  45. package/dist/dynamic/components/Menu/package.json +1 -1
  46. package/dist/dynamic/components/MenuToggle/package.json +1 -1
  47. package/dist/dynamic/components/Modal/package.json +1 -1
  48. package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
  49. package/dist/dynamic/components/Nav/package.json +1 -1
  50. package/dist/dynamic/components/NotificationBadge/package.json +1 -1
  51. package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
  52. package/dist/dynamic/components/NumberInput/package.json +1 -1
  53. package/dist/dynamic/components/OverflowMenu/package.json +1 -1
  54. package/dist/dynamic/components/Page/package.json +1 -1
  55. package/dist/dynamic/components/Pagination/package.json +1 -1
  56. package/dist/dynamic/components/Panel/package.json +1 -1
  57. package/dist/dynamic/components/Popover/package.json +1 -1
  58. package/dist/dynamic/components/Progress/package.json +1 -1
  59. package/dist/dynamic/components/ProgressStepper/package.json +1 -1
  60. package/dist/dynamic/components/Radio/package.json +1 -1
  61. package/dist/dynamic/components/SearchInput/package.json +1 -1
  62. package/dist/dynamic/components/Select/package.json +1 -1
  63. package/dist/dynamic/components/Sidebar/package.json +1 -1
  64. package/dist/dynamic/components/SimpleList/package.json +1 -1
  65. package/dist/dynamic/components/Skeleton/package.json +1 -1
  66. package/dist/dynamic/components/SkipToContent/package.json +1 -1
  67. package/dist/dynamic/components/Slider/package.json +1 -1
  68. package/dist/dynamic/components/Spinner/package.json +1 -1
  69. package/dist/dynamic/components/Switch/package.json +1 -1
  70. package/dist/dynamic/components/Tabs/package.json +1 -1
  71. package/dist/dynamic/components/Text/package.json +1 -1
  72. package/dist/dynamic/components/TextArea/package.json +1 -1
  73. package/dist/dynamic/components/TextInput/package.json +1 -1
  74. package/dist/dynamic/components/TextInputGroup/package.json +1 -1
  75. package/dist/dynamic/components/Tile/package.json +1 -1
  76. package/dist/dynamic/components/TimePicker/package.json +1 -1
  77. package/dist/dynamic/components/Timestamp/package.json +1 -1
  78. package/dist/dynamic/components/Title/package.json +1 -1
  79. package/dist/dynamic/components/ToggleGroup/package.json +1 -1
  80. package/dist/dynamic/components/Toolbar/package.json +1 -1
  81. package/dist/dynamic/components/Tooltip/package.json +1 -1
  82. package/dist/dynamic/components/TreeView/package.json +1 -1
  83. package/dist/dynamic/components/Truncate/package.json +1 -1
  84. package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
  85. package/dist/dynamic/components/Wizard/package.json +1 -1
  86. package/dist/dynamic/deprecated/components/ApplicationLauncher/package.json +1 -1
  87. package/dist/dynamic/deprecated/components/ContextSelector/package.json +1 -1
  88. package/dist/dynamic/deprecated/components/Dropdown/package.json +1 -1
  89. package/dist/dynamic/deprecated/components/OptionsMenu/package.json +1 -1
  90. package/dist/dynamic/deprecated/components/PageHeader/package.json +1 -1
  91. package/dist/dynamic/deprecated/components/Select/package.json +1 -1
  92. package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
  93. package/dist/dynamic/deprecated/components/package.json +1 -1
  94. package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
  95. package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
  96. package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
  97. package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
  98. package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
  99. package/dist/dynamic/helpers/constants/package.json +1 -1
  100. package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
  101. package/dist/dynamic/helpers/favorites/package.json +1 -1
  102. package/dist/dynamic/helpers/fileUtils/package.json +1 -1
  103. package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
  104. package/dist/dynamic/helpers/package.json +1 -1
  105. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  106. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  107. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  108. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  109. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  110. package/dist/dynamic/helpers/util/package.json +1 -1
  111. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  112. package/dist/dynamic/layouts/Flex/package.json +1 -1
  113. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  114. package/dist/dynamic/layouts/Grid/package.json +1 -1
  115. package/dist/dynamic/layouts/Level/package.json +1 -1
  116. package/dist/dynamic/layouts/Split/package.json +1 -1
  117. package/dist/dynamic/layouts/Stack/package.json +1 -1
  118. package/dist/dynamic/next/components/DualListSelector/package.json +1 -1
  119. package/dist/dynamic/next/components/Modal/package.json +1 -1
  120. package/dist/dynamic/next/components/package.json +1 -1
  121. package/dist/dynamic/styles/package.json +1 -1
  122. package/dist/dynamic-modules.json +1 -0
  123. package/dist/esm/components/Dropdown/Dropdown.d.ts +2 -0
  124. package/dist/esm/components/Dropdown/Dropdown.d.ts.map +1 -1
  125. package/dist/esm/components/Dropdown/Dropdown.js +12 -3
  126. package/dist/esm/components/Dropdown/Dropdown.js.map +1 -1
  127. package/dist/esm/components/Menu/MenuContainer.d.ts +4 -0
  128. package/dist/esm/components/Menu/MenuContainer.d.ts.map +1 -1
  129. package/dist/esm/components/Menu/MenuContainer.js +37 -15
  130. package/dist/esm/components/Menu/MenuContainer.js.map +1 -1
  131. package/dist/esm/components/Select/Select.d.ts +4 -0
  132. package/dist/esm/components/Select/Select.d.ts.map +1 -1
  133. package/dist/esm/components/Select/Select.js +12 -3
  134. package/dist/esm/components/Select/Select.js.map +1 -1
  135. package/dist/esm/components/Toolbar/ToolbarFilter.d.ts.map +1 -1
  136. package/dist/esm/components/Toolbar/ToolbarFilter.js +4 -2
  137. package/dist/esm/components/Toolbar/ToolbarFilter.js.map +1 -1
  138. package/dist/esm/helpers/KeyboardHandler.d.ts +7 -0
  139. package/dist/esm/helpers/KeyboardHandler.d.ts.map +1 -1
  140. package/dist/esm/helpers/KeyboardHandler.js +25 -0
  141. package/dist/esm/helpers/KeyboardHandler.js.map +1 -1
  142. package/dist/js/components/Dropdown/Dropdown.d.ts +2 -0
  143. package/dist/js/components/Dropdown/Dropdown.d.ts.map +1 -1
  144. package/dist/js/components/Dropdown/Dropdown.js +11 -2
  145. package/dist/js/components/Dropdown/Dropdown.js.map +1 -1
  146. package/dist/js/components/Menu/MenuContainer.d.ts +4 -0
  147. package/dist/js/components/Menu/MenuContainer.d.ts.map +1 -1
  148. package/dist/js/components/Menu/MenuContainer.js +38 -16
  149. package/dist/js/components/Menu/MenuContainer.js.map +1 -1
  150. package/dist/js/components/Select/Select.d.ts +4 -0
  151. package/dist/js/components/Select/Select.d.ts.map +1 -1
  152. package/dist/js/components/Select/Select.js +11 -2
  153. package/dist/js/components/Select/Select.js.map +1 -1
  154. package/dist/js/components/Toolbar/ToolbarFilter.d.ts.map +1 -1
  155. package/dist/js/components/Toolbar/ToolbarFilter.js +4 -2
  156. package/dist/js/components/Toolbar/ToolbarFilter.js.map +1 -1
  157. package/dist/js/helpers/KeyboardHandler.d.ts +7 -0
  158. package/dist/js/helpers/KeyboardHandler.d.ts.map +1 -1
  159. package/dist/js/helpers/KeyboardHandler.js +27 -1
  160. package/dist/js/helpers/KeyboardHandler.js.map +1 -1
  161. package/dist/umd/assets/{output-DasTFpST.css → output-CT5USS28.css} +5307 -5307
  162. package/dist/umd/react-core.min.js +2 -2
  163. package/helpers/package.json +1 -1
  164. package/layouts/package.json +1 -1
  165. package/next/package.json +1 -1
  166. package/package.json +3 -3
  167. package/src/components/Dropdown/Dropdown.tsx +13 -1
  168. package/src/components/Menu/MenuContainer.tsx +42 -12
  169. package/src/components/Select/Select.tsx +16 -1
  170. package/src/components/Select/examples/SelectMultiTypeahead.tsx +1 -1
  171. package/src/components/Select/examples/SelectMultiTypeaheadCheckbox.tsx +1 -1
  172. package/src/components/Select/examples/SelectMultiTypeaheadCreatable.tsx +1 -1
  173. package/src/components/Select/examples/SelectTypeahead.tsx +1 -1
  174. package/src/components/Select/examples/SelectTypeaheadCreatable.tsx +1 -1
  175. package/src/components/Toolbar/ToolbarFilter.tsx +2 -1
  176. package/src/helpers/KeyboardHandler.tsx +27 -0
@@ -1 +1 @@
1
- {"name":"@patternfly/react-core-helpers","main":"../dist/js/helpers/index.js","module":"../dist/esm/helpers/index.js","typings":"../dist/esm/helpers/index.d.ts","version":"5.4.8","private":true}
1
+ {"name":"@patternfly/react-core-helpers","main":"../dist/js/helpers/index.js","module":"../dist/esm/helpers/index.js","typings":"../dist/esm/helpers/index.d.ts","version":"5.4.10","private":true}
@@ -1 +1 @@
1
- {"name":"@patternfly/react-core-layouts","main":"../dist/js/layouts/index.js","module":"../dist/esm/layouts/index.js","typings":"../dist/esm/layouts/index.d.ts","version":"5.4.8","private":true}
1
+ {"name":"@patternfly/react-core-layouts","main":"../dist/js/layouts/index.js","module":"../dist/esm/layouts/index.js","typings":"../dist/esm/layouts/index.d.ts","version":"5.4.10","private":true}
package/next/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@patternfly/react-core-next","main":"../dist/js/next/index.js","module":"../dist/esm/next/index.js","typings":"../dist/esm/next/index.d.ts","version":"5.4.8","private":true}
1
+ {"name":"@patternfly/react-core-next","main":"../dist/js/next/index.js","module":"../dist/esm/next/index.js","typings":"../dist/esm/next/index.d.ts","version":"5.4.10","private":true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "5.4.9",
3
+ "version": "5.4.11",
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",
@@ -49,7 +49,7 @@
49
49
  "@patternfly/react-icons": "^5.4.2",
50
50
  "@patternfly/react-styles": "^5.4.1",
51
51
  "@patternfly/react-tokens": "^5.4.1",
52
- "focus-trap": "7.6.0",
52
+ "focus-trap": "7.6.2",
53
53
  "react-dropzone": "^14.2.3",
54
54
  "tslib": "^2.7.0"
55
55
  },
@@ -62,5 +62,5 @@
62
62
  "react": "^17 || ^18",
63
63
  "react-dom": "^17 || ^18"
64
64
  },
65
- "gitHead": "9f4b31f42061a72ad67816d5a896a14b005f7c14"
65
+ "gitHead": "2c6037ef6cfae35bdd0377fdc0f59020729b5138"
66
66
  }
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { css } from '@patternfly/react-styles';
3
3
  import { Menu, MenuContent, MenuProps } from '../Menu';
4
4
  import { Popper } from '../../helpers/Popper/Popper';
5
- import { useOUIAProps, OUIAProps } from '../../helpers';
5
+ import { useOUIAProps, OUIAProps, onToggleArrowKeydownDefault } from '../../helpers';
6
6
 
7
7
  export interface DropdownPopperProps {
8
8
  /** Vertical direction of the popper. If enableFlip is set to true, this will set the initial direction before the popper flips. */
@@ -51,6 +51,8 @@ export interface DropdownProps extends MenuProps, OUIAProps {
51
51
  onOpenChange?: (isOpen: boolean) => void;
52
52
  /** @beta Keys that trigger onOpenChange, defaults to tab and escape. It is highly recommended to include Escape in the array, while Tab may be omitted if the menu contains non-menu items that are focusable. */
53
53
  onOpenChangeKeys?: string[];
54
+ /** Callback to override the toggle keydown behavior. By default, when the toggle has focus and the menu is open, pressing the up/down arrow keys will focus a valid non-disabled menu item - the first item for the down arrow key and last item for the up arrow key. */
55
+ onToggleKeydown?: (event: KeyboardEvent) => void;
54
56
  /** Indicates if the menu should be without the outer box-shadow. */
55
57
  isPlain?: boolean;
56
58
  /** Indicates if the menu should be scrollable. */
@@ -85,6 +87,7 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
85
87
  toggle,
86
88
  shouldFocusToggleOnSelect = false,
87
89
  onOpenChange,
90
+ onToggleKeydown,
88
91
  isPlain,
89
92
  isScrollable,
90
93
  innerRef,
@@ -123,6 +126,14 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
123
126
  toggleRef.current?.focus();
124
127
  }
125
128
  }
129
+
130
+ if (toggleRef.current?.contains(event.target as Node)) {
131
+ if (onToggleKeydown) {
132
+ onToggleKeydown(event);
133
+ } else if (isOpen) {
134
+ onToggleArrowKeydownDefault(event, menuRef);
135
+ }
136
+ }
126
137
  };
127
138
 
128
139
  const handleClick = (event: MouseEvent) => {
@@ -157,6 +168,7 @@ const DropdownBase: React.FunctionComponent<DropdownProps> = ({
157
168
  toggleRef,
158
169
  onOpenChange,
159
170
  onOpenChangeKeys,
171
+ onToggleKeydown,
160
172
  shouldPreventScrollOnItemFocus,
161
173
  shouldFocusFirstItemOnOpen,
162
174
  focusTimeoutDelay
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Popper } from '../../helpers/Popper/Popper';
2
+ import { onToggleArrowKeydownDefault, Popper } from '../../helpers';
3
3
 
4
4
  export interface MenuPopperProps {
5
5
  /** Vertical direction of the popper. If enableFlip is set to true, this will set the initial direction before the popper flips. */
@@ -17,6 +17,7 @@ export interface MenuPopperProps {
17
17
  /** Flag to prevent the popper from overflowing its container and becoming partially obscured. */
18
18
  preventOverflow?: boolean;
19
19
  }
20
+
20
21
  export interface MenuContainerProps {
21
22
  /** Menu to be rendered */
22
23
  menu: React.ReactElement<any, string | React.JSXElementConstructor<any>>;
@@ -33,10 +34,14 @@ export interface MenuContainerProps {
33
34
  onOpenChange?: (isOpen: boolean) => void;
34
35
  /** Keys that trigger onOpenChange, defaults to tab and escape. It is highly recommended to include Escape in the array, while Tab may be omitted if the menu contains non-menu items that are focusable. */
35
36
  onOpenChangeKeys?: string[];
37
+ /** Callback to override the toggle keydown behavior. By default, when the toggle has focus and the menu is open, pressing the up/down arrow keys will focus a valid non-disabled menu item - the first item for the down arrow key and last item for the up arrow key. */
38
+ onToggleKeydown?: (event: KeyboardEvent) => void;
36
39
  /** z-index of the dropdown menu */
37
40
  zIndex?: number;
38
41
  /** Additional properties to pass to the Popper */
39
42
  popperProps?: MenuPopperProps;
43
+ /** @beta Flag indicating the first menu item should be focused after opening the dropdown. */
44
+ shouldFocusFirstItemOnOpen?: boolean;
40
45
  /** Flag indicating if scroll on focus of the first menu item should occur. */
41
46
  shouldPreventScrollOnItemFocus?: boolean;
42
47
  /** Time in ms to wait before firing the toggles' focus event. Defaults to 0 */
@@ -54,12 +59,30 @@ export const MenuContainer: React.FunctionComponent<MenuContainerProps> = ({
54
59
  toggle,
55
60
  toggleRef,
56
61
  onOpenChange,
62
+ onToggleKeydown,
57
63
  zIndex = 9999,
58
64
  popperProps,
59
65
  onOpenChangeKeys = ['Escape', 'Tab'],
66
+ shouldFocusFirstItemOnOpen = false,
60
67
  shouldPreventScrollOnItemFocus = true,
61
68
  focusTimeoutDelay = 0
62
69
  }: MenuContainerProps) => {
70
+ const prevIsOpen = React.useRef<boolean>(isOpen);
71
+ React.useEffect(() => {
72
+ // menu was opened, focus on first menu item
73
+ if (prevIsOpen.current === false && isOpen === true && shouldFocusFirstItemOnOpen) {
74
+ setTimeout(() => {
75
+ const firstElement = menuRef?.current?.querySelector(
76
+ 'li button:not(:disabled),li input:not(:disabled),li a:not([aria-disabled="true"])'
77
+ );
78
+ firstElement && (firstElement as HTMLElement).focus({ preventScroll: shouldPreventScrollOnItemFocus });
79
+ }, focusTimeoutDelay);
80
+ }
81
+
82
+ prevIsOpen.current = isOpen;
83
+ // eslint-disable-next-line react-hooks/exhaustive-deps
84
+ }, [isOpen]);
85
+
63
86
  React.useEffect(() => {
64
87
  const handleMenuKeys = (event: KeyboardEvent) => {
65
88
  // Close the menu on tab or escape if onOpenChange is provided
@@ -72,19 +95,17 @@ export const MenuContainer: React.FunctionComponent<MenuContainerProps> = ({
72
95
  toggleRef.current?.focus();
73
96
  }
74
97
  }
75
- };
76
98
 
77
- const handleClick = (event: MouseEvent) => {
78
- // toggle was opened, focus on first menu item
79
- if (isOpen && toggleRef.current?.contains(event.target as Node)) {
80
- setTimeout(() => {
81
- const firstElement = menuRef?.current?.querySelector(
82
- 'li button:not(:disabled),li input:not(:disabled),li a:not([aria-disabled="true"])'
83
- );
84
- firstElement && (firstElement as HTMLElement).focus({ preventScroll: shouldPreventScrollOnItemFocus });
85
- }, focusTimeoutDelay);
99
+ if (toggleRef.current?.contains(event.target as Node)) {
100
+ if (onToggleKeydown) {
101
+ onToggleKeydown(event);
102
+ } else if (isOpen) {
103
+ onToggleArrowKeydownDefault(event, menuRef);
104
+ }
86
105
  }
106
+ };
87
107
 
108
+ const handleClick = (event: MouseEvent) => {
88
109
  // If the event is not on the toggle and onOpenChange callback is provided, close the menu
89
110
  if (isOpen && onOpenChange && !toggleRef?.current?.contains(event.target as Node)) {
90
111
  if (isOpen && !menuRef.current?.contains(event.target as Node)) {
@@ -100,7 +121,16 @@ export const MenuContainer: React.FunctionComponent<MenuContainerProps> = ({
100
121
  window.removeEventListener('keydown', handleMenuKeys);
101
122
  window.removeEventListener('click', handleClick);
102
123
  };
103
- }, [focusTimeoutDelay, isOpen, menuRef, onOpenChange, onOpenChangeKeys, shouldPreventScrollOnItemFocus, toggleRef]);
124
+ }, [
125
+ focusTimeoutDelay,
126
+ isOpen,
127
+ menuRef,
128
+ onOpenChange,
129
+ onOpenChangeKeys,
130
+ onToggleKeydown,
131
+ shouldPreventScrollOnItemFocus,
132
+ toggleRef
133
+ ]);
104
134
 
105
135
  return (
106
136
  <Popper
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { css } from '@patternfly/react-styles';
3
3
  import { Menu, MenuContent, MenuProps } from '../Menu';
4
4
  import { Popper } from '../../helpers/Popper/Popper';
5
- import { getOUIAProps, OUIAProps, getDefaultOUIAId } from '../../helpers';
5
+ import { getOUIAProps, OUIAProps, getDefaultOUIAId, onToggleArrowKeydownDefault } from '../../helpers';
6
6
 
7
7
  export interface SelectPopperProps {
8
8
  /** Vertical direction of the popper. If enableFlip is set to true, this will set the initial direction before the popper flips. */
@@ -62,6 +62,10 @@ export interface SelectProps extends MenuProps, OUIAProps {
62
62
  onOpenChange?: (isOpen: boolean) => void;
63
63
  /** @beta Keys that trigger onOpenChange, defaults to tab and escape. It is highly recommended to include Escape in the array, while Tab may be omitted if the menu contains non-menu items that are focusable. */
64
64
  onOpenChangeKeys?: string[];
65
+ /** Callback to override the toggle keydown behavior. By default, when the toggle has focus and the menu is open, pressing the up/down arrow keys will focus a valid non-disabled menu item - the first item for the down arrow key and last item for the up arrow key. */
66
+ onToggleKeydown?: (event: KeyboardEvent) => void;
67
+ /** Select variant. For typeahead variant focus won't shift to menu items when pressing up/down arrows. */
68
+ variant?: 'default' | 'typeahead';
65
69
  /** Indicates if the select should be without the outer box-shadow */
66
70
  isPlain?: boolean;
67
71
  /** @hide Forwarded ref */
@@ -95,6 +99,8 @@ const SelectBase: React.FunctionComponent<SelectProps & OUIAProps> = ({
95
99
  shouldFocusFirstItemOnOpen = false,
96
100
  onOpenChange,
97
101
  onOpenChangeKeys = ['Escape', 'Tab'],
102
+ onToggleKeydown,
103
+ variant,
98
104
  isPlain,
99
105
  innerRef,
100
106
  zIndex = 9999,
@@ -130,6 +136,14 @@ const SelectBase: React.FunctionComponent<SelectProps & OUIAProps> = ({
130
136
  toggleRef.current?.focus();
131
137
  }
132
138
  }
139
+
140
+ if (toggleRef.current?.contains(event.target as Node)) {
141
+ if (onToggleKeydown) {
142
+ onToggleKeydown(event);
143
+ } else if (isOpen && variant !== 'typeahead') {
144
+ onToggleArrowKeydownDefault(event, menuRef);
145
+ }
146
+ }
133
147
  };
134
148
 
135
149
  const handleClick = (event: MouseEvent) => {
@@ -162,6 +176,7 @@ const SelectBase: React.FunctionComponent<SelectProps & OUIAProps> = ({
162
176
  toggleRef,
163
177
  onOpenChange,
164
178
  onOpenChangeKeys,
179
+ onToggleKeydown,
165
180
  shouldPreventScrollOnItemFocus,
166
181
  shouldFocusFirstItemOnOpen,
167
182
  focusTimeoutDelay
@@ -245,7 +245,7 @@ export const SelectMultiTypeahead: React.FunctionComponent = () => {
245
245
  !isOpen && closeMenu();
246
246
  }}
247
247
  toggle={toggle}
248
- shouldFocusFirstItemOnOpen={false}
248
+ variant="typeahead"
249
249
  >
250
250
  <SelectList isAriaMultiselectable id="select-multi-typeahead-listbox">
251
251
  {selectOptions.map((option, index) => (
@@ -238,7 +238,7 @@ export const SelectMultiTypeaheadCheckbox: React.FunctionComponent = () => {
238
238
  !isOpen && closeMenu();
239
239
  }}
240
240
  toggle={toggle}
241
- shouldFocusFirstItemOnOpen={false}
241
+ variant="typeahead"
242
242
  >
243
243
  <SelectList isAriaMultiselectable id="select-multi-typeahead-checkbox-listbox">
244
244
  {selectOptions.map((option, index) => (
@@ -256,7 +256,7 @@ export const SelectMultiTypeaheadCreatable: React.FunctionComponent = () => {
256
256
  !isOpen && closeMenu();
257
257
  }}
258
258
  toggle={toggle}
259
- shouldFocusFirstItemOnOpen={false}
259
+ variant="typeahead"
260
260
  >
261
261
  <SelectList isAriaMultiselectable id="select-multi-create-typeahead-listbox">
262
262
  {selectOptions.map((option, index) => (
@@ -241,7 +241,7 @@ export const SelectTypeahead: React.FunctionComponent = () => {
241
241
  !isOpen && closeMenu();
242
242
  }}
243
243
  toggle={toggle}
244
- shouldFocusFirstItemOnOpen={false}
244
+ variant="typeahead"
245
245
  >
246
246
  <SelectList id="select-typeahead-listbox">
247
247
  {selectOptions.map((option, index) => (
@@ -248,7 +248,7 @@ export const SelectTypeaheadCreatable: React.FunctionComponent = () => {
248
248
  !isOpen && closeMenu();
249
249
  }}
250
250
  toggle={toggle}
251
- shouldFocusFirstItemOnOpen={false}
251
+ variant="typeahead"
252
252
  >
253
253
  <SelectList id="select-create-typeahead-listbox">
254
254
  {selectOptions.map((option, index) => (
@@ -134,7 +134,8 @@ class ToolbarFilter extends React.Component<ToolbarFilterProps, ToolbarFilterSta
134
134
  return (
135
135
  <React.Fragment>
136
136
  {showToolbarItem && <ToolbarItem {...props}>{children}</ToolbarItem>}
137
- {ReactDOM.createPortal(chipGroup, chipGroupContentRef.current.firstElementChild)}
137
+ {chipGroupContentRef?.current?.firstElementChild &&
138
+ ReactDOM.createPortal(chipGroup, chipGroupContentRef.current.firstElementChild)}
138
139
  </React.Fragment>
139
140
  );
140
141
  }
@@ -170,6 +170,33 @@ export const setTabIndex = (options: HTMLElement[]) => {
170
170
  }
171
171
  };
172
172
 
173
+ /**
174
+ * This function is used in Dropdown, Select and MenuContainer as a default toggle keydown behavior. When the toggle has focus and the menu is open, pressing the up/down arrow keys will focus a valid non-disabled menu item - the first item for the down arrow key and last item for the up arrow key.
175
+ *
176
+ * @param event Event triggered by the keyboard
177
+ * @param menuRef Menu reference
178
+ */
179
+ export const onToggleArrowKeydownDefault = (event: KeyboardEvent, menuRef: React.RefObject<HTMLDivElement>) => {
180
+ if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') {
181
+ return;
182
+ }
183
+
184
+ event.preventDefault();
185
+
186
+ const listItems = Array.from(menuRef.current?.querySelectorAll('li'));
187
+ const focusableElements = listItems
188
+ .map((li) => li.querySelector('button:not(:disabled),input:not(:disabled),a:not([aria-disabled="true"])'))
189
+ .filter((el) => el !== null);
190
+
191
+ let focusableElement: Element;
192
+ if (event.key === 'ArrowDown') {
193
+ focusableElement = focusableElements[0];
194
+ } else {
195
+ focusableElement = focusableElements[focusableElements.length - 1];
196
+ }
197
+ focusableElement && (focusableElement as HTMLElement).focus();
198
+ };
199
+
173
200
  class KeyboardHandler extends React.Component<KeyboardHandlerProps> {
174
201
  static displayName = 'KeyboardHandler';
175
202
  static defaultProps: KeyboardHandlerProps = {