@wordpress/components 24.0.0 → 25.0.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 (166) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/CONTRIBUTING.md +10 -0
  3. package/build/color-picker/styles.js +8 -8
  4. package/build/color-picker/styles.js.map +1 -1
  5. package/build/date-time/date-time/index.js +3 -84
  6. package/build/date-time/date-time/index.js.map +1 -1
  7. package/build/date-time/date-time/styles.js +4 -19
  8. package/build/date-time/date-time/styles.js.map +1 -1
  9. package/build/dropdown-menu/index.js +87 -11
  10. package/build/dropdown-menu/index.js.map +1 -1
  11. package/build/dropdown-menu/types.js +6 -0
  12. package/build/dropdown-menu/types.js.map +1 -0
  13. package/build/dropdown-menu-v2/index.js +195 -0
  14. package/build/dropdown-menu-v2/index.js.map +1 -0
  15. package/build/dropdown-menu-v2/styles.js +176 -0
  16. package/build/dropdown-menu-v2/styles.js.map +1 -0
  17. package/build/dropdown-menu-v2/types.js +6 -0
  18. package/build/dropdown-menu-v2/types.js.map +1 -0
  19. package/build/index.native.js +0 -9
  20. package/build/index.native.js.map +1 -1
  21. package/build/input-control/styles/input-control-styles.js +30 -23
  22. package/build/input-control/styles/input-control-styles.js.map +1 -1
  23. package/build/mobile/bottom-sheet/cell.native.js +16 -8
  24. package/build/mobile/bottom-sheet/cell.native.js.map +1 -1
  25. package/build/mobile/bottom-sheet/range-cell.native.js +3 -2
  26. package/build/mobile/bottom-sheet/range-cell.native.js.map +1 -1
  27. package/build/mobile/bottom-sheet/stepper-cell/index.native.js +4 -2
  28. package/build/mobile/bottom-sheet/stepper-cell/index.native.js.map +1 -1
  29. package/build/mobile/bottom-sheet/switch-cell.native.js +8 -2
  30. package/build/mobile/bottom-sheet/switch-cell.native.js.map +1 -1
  31. package/build/mobile/bottom-sheet-select-control/index.native.js +4 -2
  32. package/build/mobile/bottom-sheet-select-control/index.native.js.map +1 -1
  33. package/build/mobile/bottom-sheet-text-control/index.native.js +4 -2
  34. package/build/mobile/bottom-sheet-text-control/index.native.js.map +1 -1
  35. package/build/modal/index.js +1 -2
  36. package/build/modal/index.js.map +1 -1
  37. package/build/private-apis.js +13 -1
  38. package/build/private-apis.js.map +1 -1
  39. package/build/range-control/index.native.js +5 -2
  40. package/build/range-control/index.native.js.map +1 -1
  41. package/build/snackbar/list.js +0 -2
  42. package/build/snackbar/list.js.map +1 -1
  43. package/build/toggle-group-control/toggle-group-control/styles.js +7 -7
  44. package/build/toggle-group-control/toggle-group-control/styles.js.map +1 -1
  45. package/build-module/color-picker/styles.js +8 -8
  46. package/build-module/color-picker/styles.js.map +1 -1
  47. package/build-module/date-time/date-time/index.js +6 -81
  48. package/build-module/date-time/date-time/index.js.map +1 -1
  49. package/build-module/date-time/date-time/styles.js +3 -17
  50. package/build-module/date-time/date-time/styles.js.map +1 -1
  51. package/build-module/dropdown-menu/index.js +87 -10
  52. package/build-module/dropdown-menu/index.js.map +1 -1
  53. package/build-module/dropdown-menu/types.js +2 -0
  54. package/build-module/dropdown-menu/types.js.map +1 -0
  55. package/build-module/dropdown-menu-v2/index.js +149 -0
  56. package/build-module/dropdown-menu-v2/index.js.map +1 -0
  57. package/build-module/dropdown-menu-v2/styles.js +153 -0
  58. package/build-module/dropdown-menu-v2/styles.js.map +1 -0
  59. package/build-module/dropdown-menu-v2/types.js +2 -0
  60. package/build-module/dropdown-menu-v2/types.js.map +1 -0
  61. package/build-module/index.native.js +0 -1
  62. package/build-module/index.native.js.map +1 -1
  63. package/build-module/input-control/styles/input-control-styles.js +30 -23
  64. package/build-module/input-control/styles/input-control-styles.js.map +1 -1
  65. package/build-module/mobile/bottom-sheet/cell.native.js +16 -8
  66. package/build-module/mobile/bottom-sheet/cell.native.js.map +1 -1
  67. package/build-module/mobile/bottom-sheet/range-cell.native.js +3 -2
  68. package/build-module/mobile/bottom-sheet/range-cell.native.js.map +1 -1
  69. package/build-module/mobile/bottom-sheet/stepper-cell/index.native.js +4 -2
  70. package/build-module/mobile/bottom-sheet/stepper-cell/index.native.js.map +1 -1
  71. package/build-module/mobile/bottom-sheet/switch-cell.native.js +7 -2
  72. package/build-module/mobile/bottom-sheet/switch-cell.native.js.map +1 -1
  73. package/build-module/mobile/bottom-sheet-select-control/index.native.js +4 -2
  74. package/build-module/mobile/bottom-sheet-select-control/index.native.js.map +1 -1
  75. package/build-module/mobile/bottom-sheet-text-control/index.native.js +4 -2
  76. package/build-module/mobile/bottom-sheet-text-control/index.native.js.map +1 -1
  77. package/build-module/modal/index.js +1 -2
  78. package/build-module/modal/index.js.map +1 -1
  79. package/build-module/private-apis.js +12 -1
  80. package/build-module/private-apis.js.map +1 -1
  81. package/build-module/range-control/index.native.js +5 -2
  82. package/build-module/range-control/index.native.js.map +1 -1
  83. package/build-module/snackbar/list.js +0 -2
  84. package/build-module/snackbar/list.js.map +1 -1
  85. package/build-module/toggle-group-control/toggle-group-control/styles.js +7 -7
  86. package/build-module/toggle-group-control/toggle-group-control/styles.js.map +1 -1
  87. package/build-style/style-rtl.css +11 -14
  88. package/build-style/style.css +11 -14
  89. package/build-types/color-picker/styles.d.ts.map +1 -1
  90. package/build-types/date-time/date-time/index.d.ts +3 -4
  91. package/build-types/date-time/date-time/index.d.ts.map +1 -1
  92. package/build-types/date-time/date-time/styles.d.ts +0 -4
  93. package/build-types/date-time/date-time/styles.d.ts.map +1 -1
  94. package/build-types/date-time/stories/date-time.d.ts.map +1 -1
  95. package/build-types/date-time/types.d.ts +0 -14
  96. package/build-types/date-time/types.d.ts.map +1 -1
  97. package/build-types/dropdown-menu/index.d.ts +83 -1
  98. package/build-types/dropdown-menu/index.d.ts.map +1 -1
  99. package/build-types/dropdown-menu/stories/index.d.ts +13 -0
  100. package/build-types/dropdown-menu/stories/index.d.ts.map +1 -0
  101. package/build-types/dropdown-menu/test/index.d.ts +2 -0
  102. package/build-types/dropdown-menu/test/index.d.ts.map +1 -0
  103. package/build-types/dropdown-menu/types.d.ts +134 -0
  104. package/build-types/dropdown-menu/types.d.ts.map +1 -0
  105. package/build-types/dropdown-menu-v2/index.d.ts +17 -0
  106. package/build-types/dropdown-menu-v2/index.d.ts.map +1 -0
  107. package/build-types/dropdown-menu-v2/stories/index.d.ts +13 -0
  108. package/build-types/dropdown-menu-v2/stories/index.d.ts.map +1 -0
  109. package/build-types/dropdown-menu-v2/styles.d.ts +41 -0
  110. package/build-types/dropdown-menu-v2/styles.d.ts.map +1 -0
  111. package/build-types/dropdown-menu-v2/test/index.d.ts +2 -0
  112. package/build-types/dropdown-menu-v2/test/index.d.ts.map +1 -0
  113. package/build-types/dropdown-menu-v2/types.d.ts +242 -0
  114. package/build-types/dropdown-menu-v2/types.d.ts.map +1 -0
  115. package/build-types/input-control/styles/input-control-styles.d.ts.map +1 -1
  116. package/build-types/modal/index.d.ts.map +1 -1
  117. package/build-types/private-apis.d.ts.map +1 -1
  118. package/build-types/snackbar/list.d.ts.map +1 -1
  119. package/build-types/toggle-group-control/toggle-group-control/styles.d.ts.map +1 -1
  120. package/build-types/toolbar/stories/index.d.ts.map +1 -1
  121. package/build-types/ui/context/get-styled-class-name-from-key.d.ts +1 -10
  122. package/build-types/ui/context/get-styled-class-name-from-key.d.ts.map +1 -1
  123. package/package.json +21 -20
  124. package/src/button/style.scss +5 -12
  125. package/src/color-picker/styles.ts +7 -2
  126. package/src/date-time/README.md +0 -16
  127. package/src/date-time/date-time/index.tsx +17 -155
  128. package/src/date-time/date-time/styles.ts +0 -4
  129. package/src/date-time/stories/date-time.tsx +0 -4
  130. package/src/date-time/types.ts +0 -16
  131. package/src/dropdown-menu/README.md +12 -22
  132. package/src/dropdown-menu/{index.js → index.tsx} +111 -25
  133. package/src/dropdown-menu/stories/{index.js → index.tsx} +14 -22
  134. package/src/dropdown-menu/test/{index.js → index.tsx} +6 -5
  135. package/src/dropdown-menu/types.ts +143 -0
  136. package/src/dropdown-menu-v2/README.md +392 -0
  137. package/src/dropdown-menu-v2/index.tsx +241 -0
  138. package/src/dropdown-menu-v2/stories/index.tsx +193 -0
  139. package/src/dropdown-menu-v2/styles.ts +263 -0
  140. package/src/dropdown-menu-v2/test/index.tsx +816 -0
  141. package/src/dropdown-menu-v2/types.ts +250 -0
  142. package/src/index.native.js +0 -1
  143. package/src/input-control/styles/input-control-styles.tsx +7 -0
  144. package/src/mobile/bottom-sheet/cell.native.js +26 -5
  145. package/src/mobile/bottom-sheet/range-cell.native.js +2 -1
  146. package/src/mobile/bottom-sheet/stepper-cell/index.native.js +2 -0
  147. package/src/mobile/bottom-sheet/styles.native.scss +13 -1
  148. package/src/mobile/bottom-sheet/switch-cell.native.js +10 -2
  149. package/src/mobile/bottom-sheet-select-control/index.native.js +2 -0
  150. package/src/mobile/bottom-sheet-text-control/index.native.js +2 -0
  151. package/src/modal/index.tsx +1 -6
  152. package/src/private-apis.ts +22 -0
  153. package/src/range-control/index.native.js +3 -0
  154. package/src/search-control/style.scss +2 -0
  155. package/src/snackbar/list.tsx +0 -1
  156. package/src/toggle-group-control/test/__snapshots__/index.tsx.snap +6 -2
  157. package/src/toggle-group-control/toggle-group-control/styles.ts +6 -1
  158. package/src/toolbar/stories/index.tsx +25 -28
  159. package/src/tooltip/style.scss +2 -2
  160. package/tsconfig.tsbuildinfo +1 -1
  161. package/build/mobile/readable-content-view/index.native.js +0 -97
  162. package/build/mobile/readable-content-view/index.native.js.map +0 -1
  163. package/build-module/mobile/readable-content-view/index.native.js +0 -81
  164. package/build-module/mobile/readable-content-view/index.native.js.map +0 -1
  165. package/src/mobile/readable-content-view/index.native.js +0 -85
  166. package/src/mobile/readable-content-view/style.native.scss +0 -30
@@ -11,7 +11,3 @@ import { VStack } from '../../v-stack';
11
11
  export const Wrapper = styled( VStack )`
12
12
  box-sizing: border-box;
13
13
  `;
14
-
15
- export const CalendarHelp = styled.div`
16
- min-width: 260px;
17
- `;
@@ -52,10 +52,6 @@ const Template: ComponentStory< typeof DateTimePicker > = ( {
52
52
  export const Default: ComponentStory< typeof DateTimePicker > = Template.bind(
53
53
  {}
54
54
  );
55
- Default.args = {
56
- __nextRemoveHelpButton: true,
57
- __nextRemoveResetButton: true,
58
- };
59
55
 
60
56
  export const WithEvents: ComponentStory< typeof DateTimePicker > =
61
57
  Template.bind( {} );
@@ -73,20 +73,4 @@ export type DateTimePickerProps = Omit< DatePickerProps, 'onChange' > &
73
73
  * passed the date and time as an argument.
74
74
  */
75
75
  onChange?: ( date: string | null ) => void;
76
-
77
- /**
78
- * Start opting in to not displaying a Help button which will become the
79
- * default in a future version.
80
- *
81
- * @default false
82
- */
83
- __nextRemoveHelpButton?: boolean;
84
-
85
- /**
86
- * Start opting in to not displaying a Reset button which will become
87
- * the default in a future version.
88
- *
89
- * @default false
90
- */
91
- __nextRemoveResetButton?: boolean;
92
76
  };
@@ -131,80 +131,70 @@ const MyDropdownMenu = () => (
131
131
 
132
132
  The component accepts the following props:
133
133
 
134
- #### icon
134
+ #### `icon`: `string | null`
135
135
 
136
136
  The [Dashicon](https://developer.wordpress.org/resource/dashicons/) icon slug to be shown in the collapsed menu button.
137
137
 
138
- - Type: `String|null`
139
138
  - Required: No
140
139
  - Default: `"menu"`
141
140
 
142
141
  See also: [https://developer.wordpress.org/resource/dashicons/](https://developer.wordpress.org/resource/dashicons/)
143
142
 
144
- #### label
143
+ #### `label`: `string`
145
144
 
146
145
  A human-readable label to present as accessibility text on the focused collapsed menu button.
147
146
 
148
- - Type: `String`
149
147
  - Required: Yes
150
148
 
151
- #### controls
149
+ #### `controls:` `DropdownOption[] | DropdownOption[][]`
152
150
 
153
- An array of objects describing the options to be shown in the expanded menu.
151
+ An array or nested array of objects describing the options to be shown in the expanded menu.
154
152
 
155
153
  Each object should include an `icon` [Dashicon](https://developer.wordpress.org/resource/dashicons/) slug string, a human-readable `title` string, `isDisabled` boolean flag and an `onClick` function callback to invoke when the option is selected.
156
154
 
157
- A valid DropdownMenu must specify one or the other of a `controls` or `children` prop.
158
-
159
- - Type: `Array`
155
+ A valid DropdownMenu must specify a `controls` or `children` prop, or both.
160
156
  - Required: No
161
157
 
162
- #### children
158
+ #### `children`: `( callbackProps: DropdownCallbackProps ) => ReactNode`
163
159
 
164
160
  A [function render prop](https://reactjs.org/docs/render-props.html#using-props-other-than-render) which should return an element or elements valid for use in a DropdownMenu: `MenuItem`, `MenuItemsChoice`, or `MenuGroup`. Its first argument is a props object including the same values as given to a [`Dropdown`'s `renderContent`](/packages/components/src/dropdown#rendercontent) (`isOpen`, `onToggle`, `onClose`).
165
161
 
166
- A valid DropdownMenu must specify one or the other of a `controls` or `children` prop.
162
+ A valid DropdownMenu must specify a `controls` or `children` prop, or both.
167
163
 
168
- - Type: `Function`
169
164
  - Required: No
170
165
 
171
166
  See also: [https://developer.wordpress.org/resource/dashicons/](https://developer.wordpress.org/resource/dashicons/)
172
167
 
173
- #### className
168
+ #### `className`: `string`
174
169
 
175
170
  A class name to apply to the dropdown menu's toggle element wrapper.
176
171
 
177
- - Type: `String`
178
172
  - Required: No
179
173
 
180
- #### popoverProps
174
+ #### `popoverProps`: `DropdownProps[ 'popoverProps' ]`
181
175
 
182
176
  Properties of `popoverProps` object will be passed as props to the nested `Popover` component.
183
177
  Use this object to modify props available for the `Popover` component that are not already exposed in the `DropdownMenu` component, e.g.: the direction in which the popover should open relative to its parent node set with `position` prop.
184
178
 
185
- - Type: `Object`
186
179
  - Required: No
187
180
 
188
- #### toggleProps
181
+ #### `toggleProps`: `ToggleProps`
189
182
 
190
183
  Properties of `toggleProps` object will be passed as props to the nested `Button` component in the `renderToggle` implementation of the `Dropdown` component used internally.
191
184
  Use this object to modify props available for the `Button` component that are not already exposed in the `DropdownMenu` component, e.g.: the tooltip text displayed on hover set with `tooltip` prop.
192
185
 
193
- - Type: `Object`
194
186
  - Required: No
195
187
 
196
- #### menuProps
188
+ #### `menuProps`: `NavigableContainerProps`
197
189
 
198
190
  Properties of `menuProps` object will be passed as props to the nested `NavigableMenu` component in the `renderContent` implementation of the `Dropdown` component used internally.
199
191
  Use this object to modify props available for the `NavigableMenu` component that are not already exposed in the `DropdownMenu` component, e.g.: the orientation of the menu set with `orientation` prop.
200
192
 
201
- - Type: `Object`
202
193
  - Required: No
203
194
 
204
- #### disableOpenOnArrowDown
195
+ #### `disableOpenOnArrowDown`: `boolean`
205
196
 
206
197
  In some contexts, the arrow down key used to open the dropdown menu might need to be disabled—for example when that key is used to perform another action.
207
198
 
208
- - Type: `boolean`
209
199
  - Required: No
210
200
  - Default: `false`
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  /**
3
2
  * External dependencies
4
3
  */
@@ -15,9 +14,12 @@ import { menu } from '@wordpress/icons';
15
14
  import Button from '../button';
16
15
  import Dropdown from '../dropdown';
17
16
  import { NavigableMenu } from '../navigable-container';
17
+ import type { DropdownMenuProps, DropdownOption } from './types';
18
18
 
19
- function mergeProps( defaultProps = {}, props = {} ) {
20
- const mergedProps = {
19
+ function mergeProps<
20
+ T extends { className?: string; [ key: string ]: unknown }
21
+ >( defaultProps: Partial< T > = {}, props: T = {} as T ) {
22
+ const mergedProps: T = {
21
23
  ...defaultProps,
22
24
  ...props,
23
25
  };
@@ -32,17 +34,92 @@ function mergeProps( defaultProps = {}, props = {} ) {
32
34
  return mergedProps;
33
35
  }
34
36
 
37
+ function isFunction( maybeFunc: unknown ): maybeFunc is () => void {
38
+ return typeof maybeFunc === 'function';
39
+ }
40
+
35
41
  /**
36
- * Whether the argument is a function.
37
42
  *
38
- * @param {*} maybeFunc The argument to check.
39
- * @return {boolean} True if the argument is a function, false otherwise.
43
+ * The DropdownMenu displays a list of actions (each contained in a MenuItem,
44
+ * MenuItemsChoice, or MenuGroup) in a compact way. It appears in a Popover
45
+ * after the user has interacted with an element (a button or icon) or when
46
+ * they perform a specific action.
47
+ *
48
+ * Render a Dropdown Menu with a set of controls:
49
+ *
50
+ * ```jsx
51
+ * import { DropdownMenu } from '@wordpress/components';
52
+ * import {
53
+ * more,
54
+ * arrowLeft,
55
+ * arrowRight,
56
+ * arrowUp,
57
+ * arrowDown,
58
+ * } from '@wordpress/icons';
59
+ *
60
+ * const MyDropdownMenu = () => (
61
+ * <DropdownMenu
62
+ * icon={ more }
63
+ * label="Select a direction"
64
+ * controls={ [
65
+ * {
66
+ * title: 'Up',
67
+ * icon: arrowUp,
68
+ * onClick: () => console.log( 'up' ),
69
+ * },
70
+ * {
71
+ * title: 'Right',
72
+ * icon: arrowRight,
73
+ * onClick: () => console.log( 'right' ),
74
+ * },
75
+ * {
76
+ * title: 'Down',
77
+ * icon: arrowDown,
78
+ * onClick: () => console.log( 'down' ),
79
+ * },
80
+ * {
81
+ * title: 'Left',
82
+ * icon: arrowLeft,
83
+ * onClick: () => console.log( 'left' ),
84
+ * },
85
+ * ] }
86
+ * />
87
+ * );
88
+ * ```
89
+ *
90
+ * Alternatively, specify a `children` function which returns elements valid for
91
+ * use in a DropdownMenu: `MenuItem`, `MenuItemsChoice`, or `MenuGroup`.
92
+ *
93
+ * ```jsx
94
+ * import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components';
95
+ * import { more, arrowUp, arrowDown, trash } from '@wordpress/icons';
96
+ *
97
+ * const MyDropdownMenu = () => (
98
+ * <DropdownMenu icon={ more } label="Select a direction">
99
+ * { ( { onClose } ) => (
100
+ * <>
101
+ * <MenuGroup>
102
+ * <MenuItem icon={ arrowUp } onClick={ onClose }>
103
+ * Move Up
104
+ * </MenuItem>
105
+ * <MenuItem icon={ arrowDown } onClick={ onClose }>
106
+ * Move Down
107
+ * </MenuItem>
108
+ * </MenuGroup>
109
+ * <MenuGroup>
110
+ * <MenuItem icon={ trash } onClick={ onClose }>
111
+ * Remove
112
+ * </MenuItem>
113
+ * </MenuGroup>
114
+ * </>
115
+ * ) }
116
+ * </DropdownMenu>
117
+ * );
118
+ * ```
119
+ *
40
120
  */
41
- function isFunction( maybeFunc ) {
42
- return typeof maybeFunc === 'function';
43
- }
44
121
 
45
- function DropdownMenu( dropdownMenuProps ) {
122
+ function DropdownMenu( dropdownMenuProps: DropdownMenuProps ) {
46
123
  const {
47
124
  children,
48
125
  className,
@@ -62,13 +139,18 @@ function DropdownMenu( dropdownMenuProps ) {
62
139
  }
63
140
 
64
141
  // Normalize controls to nested array of objects (sets of controls)
65
- let controlSets;
142
+ let controlSets: DropdownOption[][];
66
143
  if ( controls?.length ) {
144
+ // @ts-expect-error The check below is needed because `DropdownMenus`
145
+ // rendered by `ToolBarGroup` receive controls as a nested array.
67
146
  controlSets = controls;
68
147
  if ( ! Array.isArray( controlSets[ 0 ] ) ) {
69
- controlSets = [ controlSets ];
148
+ // This is not ideal, but at this point we know that `controls` is
149
+ // not a nested array, even if TypeScript doesn't.
150
+ controlSets = [ controls as DropdownOption[] ];
70
151
  }
71
152
  }
153
+
72
154
  const mergedPopoverProps = mergeProps(
73
155
  {
74
156
  className: 'components-dropdown-menu__popover',
@@ -81,7 +163,7 @@ function DropdownMenu( dropdownMenuProps ) {
81
163
  className={ classnames( 'components-dropdown-menu', className ) }
82
164
  popoverProps={ mergedPopoverProps }
83
165
  renderToggle={ ( { isOpen, onToggle } ) => {
84
- const openOnArrowDown = ( event ) => {
166
+ const openOnArrowDown = ( event: React.KeyboardEvent ) => {
85
167
  if ( disableOpenOnArrowDown ) {
86
168
  return;
87
169
  }
@@ -110,18 +192,22 @@ function DropdownMenu( dropdownMenuProps ) {
110
192
  <Toggle
111
193
  { ...mergedToggleProps }
112
194
  icon={ icon }
113
- onClick={ ( event ) => {
114
- onToggle( event );
115
- if ( mergedToggleProps.onClick ) {
116
- mergedToggleProps.onClick( event );
117
- }
118
- } }
119
- onKeyDown={ ( event ) => {
120
- openOnArrowDown( event );
121
- if ( mergedToggleProps.onKeyDown ) {
122
- mergedToggleProps.onKeyDown( event );
123
- }
124
- } }
195
+ onClick={
196
+ ( ( event ) => {
197
+ onToggle();
198
+ if ( mergedToggleProps.onClick ) {
199
+ mergedToggleProps.onClick( event );
200
+ }
201
+ } ) as React.MouseEventHandler< HTMLButtonElement >
202
+ }
203
+ onKeyDown={
204
+ ( ( event ) => {
205
+ openOnArrowDown( event );
206
+ if ( mergedToggleProps.onKeyDown ) {
207
+ mergedToggleProps.onKeyDown( event );
208
+ }
209
+ } ) as React.KeyboardEventHandler< HTMLButtonElement >
210
+ }
125
211
  aria-haspopup="true"
126
212
  aria-expanded={ isOpen }
127
213
  label={ label }
@@ -1,8 +1,13 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { ComponentMeta, ComponentStory } from '@storybook/react';
1
5
  /**
2
6
  * Internal dependencies
3
7
  */
4
- import DropdownMenu from '../';
5
- import { MenuGroup, MenuItem } from '../../';
8
+ import DropdownMenu from '..';
9
+ import MenuItem from '../../menu-item';
10
+ import MenuGroup from '../../menu-group';
6
11
 
7
12
  /**
8
13
  * WordPress dependencies
@@ -16,37 +21,24 @@ import {
16
21
  trash,
17
22
  } from '@wordpress/icons';
18
23
 
19
- export default {
24
+ const meta: ComponentMeta< typeof DropdownMenu > = {
20
25
  title: 'Components/DropdownMenu',
21
26
  component: DropdownMenu,
27
+ parameters: {
28
+ controls: { expanded: true },
29
+ docs: { source: { state: 'open' } },
30
+ },
22
31
  argTypes: {
23
- className: { control: { type: 'text' } },
24
- children: { control: { type: null } },
25
- disableOpenOnArrowDown: { control: { type: 'boolean' } },
26
32
  icon: {
27
33
  options: [ 'menu', 'chevronDown', 'more' ],
28
34
  mapping: { menu, chevronDown, more },
29
35
  control: { type: 'select' },
30
36
  },
31
- menuProps: {
32
- control: { type: 'object' },
33
- },
34
- noIcons: { control: { type: 'boolean' } },
35
- popoverProps: {
36
- control: { type: 'object' },
37
- },
38
- text: { control: { type: 'text' } },
39
- toggleProps: {
40
- control: { type: 'object' },
41
- },
42
- },
43
- parameters: {
44
- controls: { expanded: true },
45
- docs: { source: { state: 'open' } },
46
37
  },
47
38
  };
39
+ export default meta;
48
40
 
49
- const Template = ( props ) => (
41
+ const Template: ComponentStory< typeof DropdownMenu > = ( props ) => (
50
42
  <div style={ { height: 150 } }>
51
43
  <DropdownMenu { ...props } />
52
44
  </div>
@@ -12,19 +12,19 @@ import { arrowLeft, arrowRight, arrowUp, arrowDown } from '@wordpress/icons';
12
12
  /**
13
13
  * Internal dependencies
14
14
  */
15
- import DropdownMenu from '../';
16
- import { MenuItem } from '../../';
15
+ import DropdownMenu from '..';
16
+ import MenuItem from '../../menu-item';
17
17
 
18
18
  describe( 'DropdownMenu', () => {
19
19
  it( 'should not render when neither controls nor children are assigned', () => {
20
- render( <DropdownMenu /> );
20
+ render( <DropdownMenu label="Open dropdown" /> );
21
21
 
22
22
  // The button toggle should not even be rendered
23
23
  expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
24
24
  } );
25
25
 
26
26
  it( 'should not render when controls are empty and children is not specified', () => {
27
- render( <DropdownMenu controls={ [] } /> );
27
+ render( <DropdownMenu label="Open dropdown" controls={ [] } /> );
28
28
 
29
29
  // The button toggle should not even be rendered
30
30
  expect( screen.queryByRole( 'button' ) ).not.toBeInTheDocument();
@@ -56,7 +56,7 @@ describe( 'DropdownMenu', () => {
56
56
  },
57
57
  ];
58
58
 
59
- render( <DropdownMenu controls={ controls } /> );
59
+ render( <DropdownMenu label="Open dropdown" controls={ controls } /> );
60
60
 
61
61
  // Move focus on the toggle button
62
62
  await user.tab();
@@ -78,6 +78,7 @@ describe( 'DropdownMenu', () => {
78
78
 
79
79
  render(
80
80
  <DropdownMenu
81
+ label="Open dropdown"
81
82
  children={ ( { onClose } ) => <MenuItem onClick={ onClose } /> }
82
83
  />
83
84
  );
@@ -0,0 +1,143 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { ReactNode } from 'react';
5
+ /**
6
+ * Internal dependencies
7
+ */
8
+ import type { ButtonAsButtonProps } from '../button/types';
9
+ import type { WordPressComponentProps } from '../ui/context';
10
+ import type { DropdownProps } from '../dropdown/types';
11
+ import type { Props as IconProps } from '../icon';
12
+ import type { NavigableMenuProps } from '../navigable-container/types';
13
+
14
+ export type DropdownOption = {
15
+ /**
16
+ * The Dashicon icon slug to be shown for the option.
17
+ */
18
+ icon?: IconProps[ 'icon' ];
19
+ /**
20
+ * A human-readable title to display for the option.
21
+ */
22
+ title: string;
23
+ /**
24
+ * Whether or not the option is disabled.
25
+ *
26
+ * @default false
27
+ */
28
+ isDisabled?: boolean;
29
+ /**
30
+ * A callback function to invoke when the option is selected.
31
+ */
32
+ onClick?: () => void;
33
+ /**
34
+ * Whether or not the control is currently active.
35
+ */
36
+ isActive?: boolean;
37
+ /**
38
+ * Text to use for the internal `Button` component's tooltip.
39
+ */
40
+ label?: string;
41
+ /**
42
+ * The role to apply to the option's HTML element
43
+ */
44
+ role?: HTMLElement[ 'role' ];
45
+ };
46
+
47
+ type DropdownCallbackProps = {
48
+ isOpen: boolean;
49
+ onToggle: () => void;
50
+ onClose: () => void;
51
+ };
52
+
53
+ // Manually including `as` prop because `WordPressComponentProps` polymorhpism
54
+ // creates a union that is too large for TypeScript to handle.
55
+ type ToggleProps = Partial<
56
+ Omit<
57
+ WordPressComponentProps< ButtonAsButtonProps, 'button', false >,
58
+ 'label' | 'text'
59
+ >
60
+ > & {
61
+ as?: React.ElementType | keyof JSX.IntrinsicElements;
62
+ };
63
+
64
+ export type DropdownMenuProps = {
65
+ /**
66
+ * The Dashicon icon slug to be shown in the collapsed menu button.
67
+ *
68
+ * @default "menu"
69
+ */
70
+ icon?: IconProps[ 'icon' ] | null;
71
+ /**
72
+ * A human-readable label to present as accessibility text on the focused
73
+ * collapsed menu button.
74
+ */
75
+ label: string;
76
+ /**
77
+ * A class name to apply to the dropdown menu's toggle element wrapper.
78
+ */
79
+ className?: string;
80
+ /**
81
+ * Properties of `popoverProps` object will be passed as props to the nested
82
+ * `Popover` component.
83
+ * Use this object to modify props available for the `Popover` component that
84
+ * are not already exposed in the `DropdownMenu` component, e.g.: the
85
+ * direction in which the popover should open relative to its parent node
86
+ * set with `position` prop.
87
+ */
88
+ popoverProps?: DropdownProps[ 'popoverProps' ];
89
+ /**
90
+ * Properties of `toggleProps` object will be passed as props to the nested
91
+ * `Button` component in the `renderToggle` implementation of the `Dropdown`
92
+ * component used internally.
93
+ * Use this object to modify props available for the `Button` component that
94
+ * are not already exposed in the `DropdownMenu` component, e.g.: the tooltip
95
+ * text displayed on hover set with `tooltip` prop.
96
+ */
97
+ toggleProps?: ToggleProps;
98
+ /**
99
+ * Properties of `menuProps` object will be passed as props to the nested
100
+ * `NavigableMenu` component in the `renderContent` implementation of the
101
+ * `Dropdown` component used internally.
102
+ * Use this object to modify props available for the `NavigableMenu`
103
+ * component that are not already exposed in the `DropdownMenu` component,
104
+ * e.g.: the orientation of the menu set with `orientation` prop.
105
+ */
106
+ menuProps?: Omit< Partial< NavigableMenuProps >, 'children' >;
107
+ /**
108
+ * In some contexts, the arrow down key used to open the dropdown menu might
109
+ * need to be disabled—for example when that key is used to perform another
110
+ * action.
111
+ *
112
+ * @default false
113
+ */
114
+ disableOpenOnArrowDown?: boolean;
115
+ /**
116
+ * Text to display on the nested `Button` component in the `renderToggle`
117
+ * implementation of the `Dropdown` component used internally.
118
+ */
119
+ text?: string;
120
+ /**
121
+ * Whether or not `no-icons` should be added to the menu's `className`.
122
+ */
123
+ noIcons?: boolean;
124
+ /**
125
+ * A [function render prop](https://reactjs.org/docs/render-props.html#using-props-other-than-render)
126
+ * which should return an element or elements valid for use in a DropdownMenu:
127
+ * `MenuItem`, `MenuItemsChoice`, or `MenuGroup`. Its first argument is a
128
+ * props object including the same values as given to a `Dropdown`'s
129
+ * `renderContent` (`isOpen`, `onToggle`, `onClose`).
130
+ *
131
+ * A valid DropdownMenu must specify a `controls` or `children` prop, or both.
132
+ */
133
+ children?: ( callbackProps: DropdownCallbackProps ) => ReactNode;
134
+ /**
135
+ * An array or nested array of objects describing the options to be shown in
136
+ * the expanded menu. Each object should include an `icon` Dashicon slug
137
+ * string, a human-readable `title` string, `isDisabled` boolean flag, and
138
+ * an `onClick` function callback to invoke when the option is selected.
139
+ *
140
+ * A valid DropdownMenu must specify a `controls` or `children` prop, or both.
141
+ */
142
+ controls?: DropdownOption[] | DropdownOption[][];
143
+ };