Skip to content

Commit ad9b443

Browse files
authored
feat(Menu): submenu support (#744)
1 parent 00b726a commit ad9b443

File tree

12 files changed

+1902
-23
lines changed

12 files changed

+1902
-23
lines changed

.size-limit.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module.exports = [
2020
}),
2121
);
2222
},
23-
limit: '282kB',
23+
limit: '284kB',
2424
},
2525
{
2626
name: 'Tree shaking (just a Button)',

src/components/actions/Menu/Menu.docs.mdx

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,25 @@ The `mods` property accepts the following modifiers you can override:
158158
|----------|------|---------|-------------|
159159
| aria-label | `string` | - | Accessibility label for sections without titles |
160160

161+
## Menu.SubMenuTrigger Properties
162+
163+
| Property | Type | Default | Description |
164+
|----------|------|---------|-------------|
165+
| children | `[ReactNode, ReactElement]` | - | Must be exactly two elements: trigger (Menu.Item) and submenu (Menu) |
166+
| disabled | `boolean` | `false` | Whether the submenu trigger is disabled |
167+
| onAction | `(key: Key) => void` | - | Callback that bubbles onAction events from the submenu |
168+
| autoFocus | `boolean \| 'first' \| 'last'` | `'first'` | Whether and how the submenu should receive focus on open |
169+
170+
### Menu.SubMenuTrigger Positioning Properties
171+
172+
| Property | Type | Default | Description |
173+
|----------|------|---------|-------------|
174+
| placement | `Placement` | `'right start'` | Placement of the submenu relative to trigger |
175+
| offset | `number` | `0` | Distance between trigger and submenu |
176+
| crossOffset | `number` | `0` | Cross-axis offset for submenu positioning |
177+
| shouldFlip | `boolean` | `true` | Whether submenu should flip when it doesn't fit |
178+
| containerPadding | `number` | `8` | Padding from viewport edges |
179+
161180
## Selection Modes
162181

163182
### None (Default)
@@ -274,24 +293,95 @@ const items = [
274293
</Menu>
275294
```
276295

296+
### Basic Submenu
297+
298+
```jsx
299+
<Menu onAction={(key) => console.log(key)}>
300+
<Menu.Item key="copy">Copy</Menu.Item>
301+
<Menu.Item key="paste">Paste</Menu.Item>
302+
303+
<Menu.SubMenuTrigger>
304+
<Menu.Item key="share">Share</Menu.Item>
305+
<Menu onAction={(key) => console.log(key)}>
306+
<Menu.Item key="share-link">Copy link</Menu.Item>
307+
<Menu.Item key="share-email">Email</Menu.Item>
308+
<Menu.Item key="share-sms">SMS</Menu.Item>
309+
</Menu>
310+
</Menu.SubMenuTrigger>
311+
312+
<Menu.Item key="delete">Delete</Menu.Item>
313+
</Menu>
314+
```
315+
316+
### Nested Submenus
317+
318+
```jsx
319+
<Menu onAction={(key) => console.log(key)}>
320+
<Menu.Item key="new">New File</Menu.Item>
321+
322+
<Menu.SubMenuTrigger>
323+
<Menu.Item key="export">Export</Menu.Item>
324+
<Menu onAction={(key) => console.log(key)}>
325+
<Menu.Item key="export-pdf">Export as PDF</Menu.Item>
326+
<Menu.Item key="export-image">Export as Image</Menu.Item>
327+
328+
<Menu.SubMenuTrigger>
329+
<Menu.Item key="export-formats">More formats</Menu.Item>
330+
<Menu onAction={(key) => console.log(key)}>
331+
<Menu.Item key="export-docx">DOCX Document</Menu.Item>
332+
<Menu.Item key="export-xlsx">Excel Spreadsheet</Menu.Item>
333+
<Menu.Item key="export-pptx">PowerPoint</Menu.Item>
334+
</Menu>
335+
</Menu.SubMenuTrigger>
336+
</Menu>
337+
</Menu.SubMenuTrigger>
338+
</Menu>
339+
```
340+
341+
### Submenu with Sections
342+
343+
```jsx
344+
<Menu onAction={(key) => console.log(key)}>
345+
<Menu.Item key="copy">Copy</Menu.Item>
346+
347+
<Menu.SubMenuTrigger>
348+
<Menu.Item key="settings">Settings</Menu.Item>
349+
<Menu onAction={(key) => console.log(key)}>
350+
<Menu.Section title="General">
351+
<Menu.Item key="preferences">Preferences</Menu.Item>
352+
<Menu.Item key="account">Account Settings</Menu.Item>
353+
</Menu.Section>
354+
<Menu.Section title="Display">
355+
<Menu.Item key="theme">Theme</Menu.Item>
356+
<Menu.Item key="layout">Layout</Menu.Item>
357+
</Menu.Section>
358+
</Menu>
359+
</Menu.SubMenuTrigger>
360+
</Menu>
361+
```
362+
277363
## Accessibility
278364

279365
### Keyboard Navigation
280366

281367
- `Tab` - Moves focus to the menu
282368
- `Arrow Keys` - Navigate between items
369+
- `Right Arrow` - Open submenu when focused on submenu trigger
370+
- `Left Arrow` - Close submenu and return to parent
283371
- `Home/End` - Move to first/last item
284-
- `Enter/Space` - Activate the focused item
285-
- `Escape` - Close the menu
372+
- `Enter/Space` - Activate the focused item or open submenu
373+
- `Escape` - Close the menu or submenu
286374
- `A-Z` - Typeahead search for items
287375

288376
### Screen Reader Support
289377

290378
- Menu announces as "menu" with proper role
291379
- Items announce as "menuitem", "menuitemcheckbox", or "menuitemradio"
380+
- Submenu triggers announce with "has submenu" indication
292381
- Selection state is announced for selectable items
293382
- Keyboard shortcuts are announced when present
294383
- Section headings provide context for grouped items
384+
- Submenu opening and closing is announced to screen readers
295385

296386
### ARIA Properties
297387

@@ -333,6 +423,17 @@ const items = [
333423
</Menu>
334424
```
335425

426+
6. **Submenu Structure**: Use clear hierarchy for submenus
427+
```jsx
428+
<Menu.SubMenuTrigger>
429+
<Menu.Item key="share">Share</Menu.Item>
430+
<Menu onAction={handleAction}>
431+
<Menu.Item key="link">Copy Link</Menu.Item>
432+
<Menu.Item key="email">Email</Menu.Item>
433+
</Menu>
434+
</Menu.SubMenuTrigger>
435+
```
436+
336437
## Integration with MenuTrigger
337438

338439
The Menu component is typically used with MenuTrigger for dropdown functionality:
@@ -349,11 +450,11 @@ The Menu component is typically used with MenuTrigger for dropdown functionality
349450

350451
## Suggested Improvements
351452

352-
- Enhancement 1: Add support for nested submenus
353-
- Enhancement 2: Implement virtual scrolling for large item lists
354-
- Enhancement 3: Add animation presets for menu transitions
355-
- Enhancement 4: Support for item templates and custom renderers
356-
- Enhancement 5: Built-in search/filter functionality
453+
- Enhancement 1: Implement virtual scrolling for large item lists
454+
- Enhancement 2: Add animation presets for menu transitions
455+
- Enhancement 3: Support for item templates and custom renderers
456+
- Enhancement 4: Built-in search/filter functionality
457+
- Enhancement 5: Add support for icons in section headers
357458

358459
## Related Components
359460

0 commit comments

Comments
 (0)