@neo4j-ndl/react 4.16.8 → 4.16.9

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 (76) hide show
  1. package/package.json +5 -2
  2. package/skills/ndl-react/SKILL.md +152 -0
  3. package/skills/ndl-react/components/accordion.md +386 -0
  4. package/skills/ndl-react/components/ai/code-preview.md +128 -0
  5. package/skills/ndl-react/components/ai/file-tag.md +90 -0
  6. package/skills/ndl-react/components/ai/image-tag.md +91 -0
  7. package/skills/ndl-react/components/ai/more-files.md +73 -0
  8. package/skills/ndl-react/components/ai/presence.md +54 -0
  9. package/skills/ndl-react/components/ai/preview.md +498 -0
  10. package/skills/ndl-react/components/ai/prompt.md +362 -0
  11. package/skills/ndl-react/components/ai/reasoning.md +95 -0
  12. package/skills/ndl-react/components/ai/response.md +1109 -0
  13. package/skills/ndl-react/components/ai/suggestion.md +84 -0
  14. package/skills/ndl-react/components/ai/thinking.md +43 -0
  15. package/skills/ndl-react/components/ai/tool-call.md +43 -0
  16. package/skills/ndl-react/components/ai/user-bubble.md +187 -0
  17. package/skills/ndl-react/components/avatar.md +169 -0
  18. package/skills/ndl-react/components/banner.md +225 -0
  19. package/skills/ndl-react/components/box.md +39 -0
  20. package/skills/ndl-react/components/breadcrumbs.md +245 -0
  21. package/skills/ndl-react/components/checkbox.md +269 -0
  22. package/skills/ndl-react/components/clean-icon-button.md +294 -0
  23. package/skills/ndl-react/components/clipboard-button.md +154 -0
  24. package/skills/ndl-react/components/code-block.md +276 -0
  25. package/skills/ndl-react/components/code.md +68 -0
  26. package/skills/ndl-react/components/color-picker.md +131 -0
  27. package/skills/ndl-react/components/conditional-wrap.md +46 -0
  28. package/skills/ndl-react/components/data-grid.md +3121 -0
  29. package/skills/ndl-react/components/date-picker.md +652 -0
  30. package/skills/ndl-react/components/dialog.md +485 -0
  31. package/skills/ndl-react/components/dismissible-tag.md +136 -0
  32. package/skills/ndl-react/components/divider.md +96 -0
  33. package/skills/ndl-react/components/drawer.md +543 -0
  34. package/skills/ndl-react/components/dropdown-button.md +161 -0
  35. package/skills/ndl-react/components/dropzone.md +371 -0
  36. package/skills/ndl-react/components/filled-button.md +26 -0
  37. package/skills/ndl-react/components/flex.md +82 -0
  38. package/skills/ndl-react/components/graph-label.md +167 -0
  39. package/skills/ndl-react/components/icon-button-array.md +187 -0
  40. package/skills/ndl-react/components/icon-button.md +193 -0
  41. package/skills/ndl-react/components/inline-edit.md +209 -0
  42. package/skills/ndl-react/components/kbd.md +63 -0
  43. package/skills/ndl-react/components/loading-bar.md +43 -0
  44. package/skills/ndl-react/components/loading-spinner.md +57 -0
  45. package/skills/ndl-react/components/logo.md +57 -0
  46. package/skills/ndl-react/components/menu.md +993 -0
  47. package/skills/ndl-react/components/modal.md +27 -0
  48. package/skills/ndl-react/components/next/tree-view.md +966 -0
  49. package/skills/ndl-react/components/outlined-button.md +26 -0
  50. package/skills/ndl-react/components/popover.md +283 -0
  51. package/skills/ndl-react/components/progress-bar.md +70 -0
  52. package/skills/ndl-react/components/radio.md +150 -0
  53. package/skills/ndl-react/components/read-only-tag.md +96 -0
  54. package/skills/ndl-react/components/segmented-control.md +200 -0
  55. package/skills/ndl-react/components/select-icon-button.md +183 -0
  56. package/skills/ndl-react/components/select.md +530 -0
  57. package/skills/ndl-react/components/selectable-tag.md +203 -0
  58. package/skills/ndl-react/components/side-navigation.md +652 -0
  59. package/skills/ndl-react/components/skeleton.md +274 -0
  60. package/skills/ndl-react/components/slider.md +308 -0
  61. package/skills/ndl-react/components/spotlight.md +796 -0
  62. package/skills/ndl-react/components/status-indicator.md +57 -0
  63. package/skills/ndl-react/components/status-label.md +132 -0
  64. package/skills/ndl-react/components/switch.md +167 -0
  65. package/skills/ndl-react/components/tabs.md +433 -0
  66. package/skills/ndl-react/components/tag.md +91 -0
  67. package/skills/ndl-react/components/text-area.md +178 -0
  68. package/skills/ndl-react/components/text-button.md +25 -0
  69. package/skills/ndl-react/components/text-input.md +475 -0
  70. package/skills/ndl-react/components/text-link.md +119 -0
  71. package/skills/ndl-react/components/time-picker.md +137 -0
  72. package/skills/ndl-react/components/timezone-picker.md +496 -0
  73. package/skills/ndl-react/components/tooltip.md +478 -0
  74. package/skills/ndl-react/components/tree-view.md +545 -0
  75. package/skills/ndl-react/components/typography.md +72 -0
  76. package/skills/ndl-react/components/wizard.md +152 -0
@@ -0,0 +1,993 @@
1
+ # Menu
2
+
3
+ Import: `import { Menu } from '@neo4j-ndl/react'`
4
+
5
+ ## Props
6
+
7
+ ### Menu
8
+
9
+ | Prop | Type | Required | Default | Description |
10
+ |------|------|----------|---------|-------------|
11
+ | `anchorRef` | `RefObject<HTMLElement \| null>` | | | Anchor element ref for positioning |
12
+ | `as` | `ElementType<any, keyof IntrinsicElements>` | | | An override of the default HTML tag of the root of the component. Can also be another React component. |
13
+ | `children` | `ReactNode` | | | Content displayed inside the menu |
14
+ | `description` | `ReactNode` | | | Optional description for nested trigger rendering |
15
+ | `icon` | `ReactNode` | | | Optional icon for nested trigger rendering |
16
+ | `isDisabled` | `boolean` | | | Whether the menu trigger is disabled |
17
+ | `isOpen` | `boolean` | | | Whether the menu is open or closed (uncontrolled when omitted) |
18
+ | `isPortaled` | `boolean` | | `true` | Whether the menu should be wrapped in a floating portal |
19
+ | `isRoot` | `boolean` | | | Whether the menu is a root menu instance. This is set internally by the component based on nesting |
20
+ | `minWidth` | `number` | | | Minimum width in pixels for the menu component |
21
+ | `onClose` | `((event: Event, closeReason: { type: "backdropClick" \| "itemClick" \| "escapeKeyDown" \| "focusOut"; id?: string; }) => void) \| undefined` | | | Callback function that is called when the menu closes |
22
+ | `placement` | `'bottom-end-bottom-start' \| 'bottom-end-top-end' \| 'bottom-middle-top-middle' \| 'bottom-start-bottom-end' \| 'bottom-start-top-start' \| 'middle-end-middle-start' \| 'middle-start-middle-end' \| 'top-end-bottom-end' \| 'top-end-top-start' \| 'top-middle-bottom-middle' \| 'top-start-bottom-start' \| 'top-start-top-end'` | | | The placement of the floating element is determined by two sets of words. The first set of words specifies the point on the anchor element where the floating element will be attached. The second set of coordinates specifies the point on the floating element that will attach to the anchor element. |
23
+ | `portalTarget` | `HTMLElement \| null` | | | Portal target element (defaults to `document.body`) |
24
+ | `ref` | `any` | | | A ref to apply to the root element. |
25
+ | `strategy` | `'absolute' \| 'fixed'` | | | Positioning strategy used by Floating UI |
26
+ | `title` | `string` | | | Optional title for nested trigger rendering |
27
+
28
+ `isRoot` is determined internally by checking for a parent context menu. If no parent exists, the component renders as the root menu; otherwise, a nested menu item trigger is rendered. When `isRoot` is `false`, the `title`, `description`, `icon`, and `isDisabled` props are forwarded to the nested menu item trigger component.
29
+
30
+ The `placement` prop defaults to `bottom-start` when it is the root menu and `right-start` when it is a nested menu item trigger.
31
+
32
+ The `strategy` prop is set to `fixed` when the context menu is inside a `Dialog` otherwise it is set to `absolute`.
33
+
34
+ ### Menu.CategoryItem
35
+
36
+ | Prop | Type | Required | Default | Description |
37
+ |------|------|----------|---------|-------------|
38
+ | `as` | `ElementType<any, keyof IntrinsicElements>` | | | An override of the default HTML tag of the root of the component. Can also be another React component. |
39
+ | `children` | `ReactNode` | | | Content displayed inside the category item component |
40
+ | `ref` | `any` | | | A ref to apply to the root element. |
41
+
42
+ ### Menu.Divider
43
+
44
+ | Prop | Type | Required | Default | Description |
45
+ |------|------|----------|---------|-------------|
46
+ | `as` | `ElementType<any, keyof IntrinsicElements>` | | | An override of the default HTML tag of the root of the component. Can also be another React component. |
47
+ | `orientation` | `'horizontal' \| 'vertical'` | | `horizontal` | |
48
+ | `ref` | `any` | | | A ref to apply to the root element. |
49
+
50
+ ### Menu.Group
51
+
52
+ | Prop | Type | Required | Default | Description |
53
+ |------|------|----------|---------|-------------|
54
+ | `children` | `ReactNode` | | | Content displayed inside the group. |
55
+ | `ref` | `Ref<HTMLDivElement>` | | | A ref to apply to the root element. |
56
+
57
+ ### Menu.Item
58
+
59
+ | Prop | Type | Required | Default | Description |
60
+ |------|------|----------|---------|-------------|
61
+ | `as` | `ElementType<any, keyof IntrinsicElements>` | | | An override of the default HTML tag of the root of the component. Can also be another React component. |
62
+ | `children` | `ReactNode` | | | Content displayed inside the menu item component |
63
+ | `description` | `ReactNode` | | | Supporting text below title |
64
+ | `id` | `string` | | | Unique identifier for the menu item |
65
+ | `isDisabled` | `boolean` | | | Whether the menu item is disabled |
66
+ | `leadingVisual` | `ReactNode` | | | Leading icon for the menu item component |
67
+ | `onClick` | `(event: MouseEvent<HTMLButtonElement, MouseEvent>) => void` | | | Callback function that is called when the menu item is clicked and it closes the menu tree |
68
+ | `onFocus` | `(event: FocusEvent<HTMLButtonElement, Element>) => void` | | | Callback function that is called when the menu item is focused |
69
+ | `ref` | `any` | | | A ref to apply to the root element. |
70
+ | `title` | `ReactNode` | ✅ | | Title of the menu item component |
71
+ | `trailingContent` | `ReactNode` | | | |
72
+
73
+ ### Menu.Items
74
+
75
+ | Prop | Type | Required | Default | Description |
76
+ |------|------|----------|---------|-------------|
77
+ | `as` | `ElementType<any, keyof IntrinsicElements>` | | | An override of the default HTML tag of the root of the component. Can also be another React component. |
78
+ | `children` | `ReactNode` | | | Content displayed inside the menu items component |
79
+ | `ref` | `any` | | | A ref to apply to the root element. |
80
+
81
+ ### Menu.RadioItem
82
+
83
+ | Prop | Type | Required | Default | Description |
84
+ |------|------|----------|---------|-------------|
85
+ | `as` | `ElementType<any, keyof IntrinsicElements>` | | | An override of the default HTML tag of the root of the component. Can also be another React component. |
86
+ | `children` | `ReactNode` | | | Content displayed inside the menu item component |
87
+ | `description` | `ReactNode` | | | Supporting text below title |
88
+ | `id` | `string` | | | Unique identifier for the menu item |
89
+ | `isChecked` | `boolean` | | `false` | Whether the radio item is checked. Marks the item as aria-checked. |
90
+ | `isDisabled` | `boolean` | | | Whether the menu item is disabled |
91
+ | `leadingVisual` | `ReactNode` | | | Leading icon for the menu item component |
92
+ | `onClick` | `(event: MouseEvent<HTMLButtonElement, MouseEvent>) => void` | | | Callback function that is called when the menu item is clicked and it closes the menu tree |
93
+ | `onFocus` | `(event: FocusEvent<HTMLButtonElement, Element>) => void` | | | Callback function that is called when the menu item is focused |
94
+ | `ref` | `any` | | | A ref to apply to the root element. |
95
+ | `title` | `ReactNode` | ✅ | | Title of the menu item component |
96
+ | `trailingContent` | `ReactNode` | | | |
97
+
98
+ ## Accessibility
99
+
100
+ ## Keyboard interactions
101
+
102
+ Implements the keyboard interactions defined in the [WAI-ARIA menu pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu/).
103
+
104
+ | Key | Description |
105
+ |-----|-------------|
106
+ | `ArrowDown` | Moves focus to the next menu item |
107
+ | `ArrowUp` | Moves focus to the previous menu item |
108
+ | `ArrowRight` | Opens a submenu when focus is on a nested menu item |
109
+ | `ArrowLeft` | Closes the current submenu and returns focus to the parent item |
110
+ | `Enter` | Activates the focused menu item |
111
+ | `Space` | Activates the focused menu item |
112
+ | `Tab` | Closes the menu and moves focus to the next focusable element on the page |
113
+ | `Shift + Tab` | Closes the menu and moves focus to the previous focusable element on the page |
114
+ | `Escape` | Closes the menu and returns focus to the trigger |
115
+ | Type-ahead | Typing a character moves focus to the next item starting with that character |
116
+
117
+ ## WAI-ARIA roles and attributes
118
+
119
+ The Menu component follows the [WAI-ARIA menu pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu/).
120
+
121
+ - The menu container has `role="menu"`
122
+ - Menu items have `role="menuitem"`
123
+ - `Menu.RadioItem` has `role="menuitemradio"` and `aria-checked`
124
+ - `Menu.Group` has `role="group"`
125
+ - Nested menu triggers have `role="menuitem"` and manage `aria-expanded` internally
126
+ - When the menu opens, focus moves to the first item. When it closes, focus returns to the trigger
127
+ - The menu closes when focus leaves the menu area
128
+
129
+ ## Implementation guidelines
130
+
131
+ - If using a button as a trigger, set `aria-haspopup="menu"` and `aria-expanded` on the trigger button
132
+ - When mixing `Menu.RadioItem` with regular items, use `Menu.Group` to separate them. This creates the correct grouping semantics for assistive technologies.
133
+ - Only one `Menu.RadioItem` within a group should have `isChecked` set to `true` at a time
134
+ - Ensure menu items have descriptive `title` values. These are used for both the visible label and the type-ahead search.
135
+ - `role="menu"` only allows children with roles `menuitem`, `menuitemradio`, `menuitemcheckbox`, `group`, or `separator`. The built-in sub-components handle this automatically, but custom content passed without a valid role will break the menu semantics for assistive technologies
136
+
137
+ ### Related WCAG criteria
138
+
139
+ - [2.1.1 Keyboard](https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html) (A): All menu items and submenus are fully operable via keyboard
140
+ - [2.4.3 Focus Order](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html) (A): Focus is managed automatically — first item on open, trigger on close
141
+ - [4.1.2 Name, Role, Value](https://www.w3.org/WAI/WCAG22/Understanding/name-role-value.html) (A): Roles (`menu`, `menuitem`, `menuitemradio`, `group`), states (`aria-checked`, `aria-expanded`), and accessible names are set automatically by the component
142
+
143
+ ## Examples
144
+
145
+ ### Default
146
+
147
+ ```tsx
148
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
149
+
150
+ import { FilledButton, Menu } from '@neo4j-ndl/react';
151
+ import { useRef, useState } from 'react';
152
+
153
+ const Component = () => {
154
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
155
+ const [isOpen, setIsOpen] = useState(false);
156
+
157
+ return (
158
+ <>
159
+ <Menu
160
+ isOpen={isOpen}
161
+ anchorRef={anchorEl}
162
+ onClose={(_event, _data) => {
163
+ setIsOpen(false);
164
+ }}
165
+ htmlAttributes={{ id: 'default-menu' }}
166
+ >
167
+ <Menu.Item
168
+ title="Action 1"
169
+ onClick={() => {
170
+ alert('Action1');
171
+ setIsOpen(false);
172
+ }}
173
+ />
174
+ <Menu.Item
175
+ title="Action 2"
176
+ onClick={() => {
177
+ alert('Action2');
178
+ setIsOpen(false);
179
+ }}
180
+ />
181
+ <Menu.Item
182
+ title="Action 3"
183
+ description="Action 3 is disabled"
184
+ isDisabled={true}
185
+ onClick={() => {
186
+ alert('Action3');
187
+ setIsOpen(false);
188
+ }}
189
+ />
190
+ <Menu.Item
191
+ title="Action 4"
192
+ description="Action 4 is awesome"
193
+ onClick={() => {
194
+ alert('Action4');
195
+ setIsOpen(false);
196
+ }}
197
+ />
198
+ </Menu>
199
+
200
+ <FilledButton
201
+ onClick={() => setIsOpen(true)}
202
+ ref={anchorEl}
203
+ aria-haspopup="menu"
204
+ aria-expanded={isOpen}
205
+ >
206
+ Open Menu
207
+ </FilledButton>
208
+ </>
209
+ );
210
+ };
211
+
212
+ export default Component;
213
+ ```
214
+
215
+ ### Custom Items
216
+
217
+ ```tsx
218
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
219
+
220
+ import { FilledButton, Menu, Typography } from '@neo4j-ndl/react';
221
+ import { Square2StackIconOutline } from '@neo4j-ndl/react/icons';
222
+ import { useRef, useState } from 'react';
223
+
224
+ const Component = () => {
225
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
226
+ const [isOpen, setIsOpen] = useState(false);
227
+
228
+ return (
229
+ <>
230
+ <Menu
231
+ isOpen={isOpen}
232
+ anchorRef={anchorEl}
233
+ placement="bottom-start-top-start"
234
+ onClose={(_event, _data) => {
235
+ setIsOpen(false);
236
+ }}
237
+ htmlAttributes={{ id: 'custom-items-menu' }}
238
+ >
239
+ <Menu.Item
240
+ title={
241
+ <Typography variant="display">This can be any element</Typography>
242
+ }
243
+ />
244
+ <Menu.Item title="Danger action 1" className="n-text-danger-text" />
245
+ <Menu.Item
246
+ title="Danger action 2"
247
+ className="n-text-danger-text"
248
+ leadingVisual={
249
+ <Square2StackIconOutline className="n-text-danger-icon" />
250
+ }
251
+ />
252
+ <Menu title="More actions" description="Some additional actions">
253
+ <Menu.Item
254
+ title="Danger action 3"
255
+ className="n-text-danger-text"
256
+ isDisabled={true}
257
+ />
258
+ <Menu.Item title="Danger action 4" className="n-text-danger-text" />
259
+ </Menu>
260
+ </Menu>
261
+ <FilledButton
262
+ onClick={() => setIsOpen(true)}
263
+ ref={anchorEl}
264
+ aria-haspopup="menu"
265
+ aria-expanded={isOpen}
266
+ >
267
+ Open Menu (set with custom placement)
268
+ </FilledButton>
269
+ </>
270
+ );
271
+ };
272
+
273
+ export default Component;
274
+ ```
275
+
276
+ ### In Dialog
277
+
278
+ ```tsx
279
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
280
+
281
+ import { Dialog, FilledButton, Menu } from '@neo4j-ndl/react';
282
+ import { Square2StackIconOutline } from '@neo4j-ndl/react/icons';
283
+ import { useRef, useState } from 'react';
284
+
285
+ const Component = () => {
286
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
287
+ const [isOpen, setIsOpen] = useState(false);
288
+ const [isModalOpen, setIsModalOpen] = useState(false);
289
+
290
+ const handleClick = () => setIsModalOpen((prev) => !prev);
291
+ const handleClose = () => setIsModalOpen(false);
292
+ const handleMenuClick = () => setIsOpen((prev) => !prev);
293
+
294
+ return (
295
+ <>
296
+ <div className="n-flex n-justify-center">
297
+ <FilledButton onClick={handleClick}>Open Dialog</FilledButton>
298
+ </div>
299
+ <Dialog
300
+ htmlAttributes={{ id: 'modal-root' }}
301
+ isOpen={isModalOpen}
302
+ onClose={handleClose}
303
+ >
304
+ <div className="n-flex n-justify-center">
305
+ <FilledButton
306
+ onClick={handleMenuClick}
307
+ ref={anchorEl}
308
+ aria-haspopup="menu"
309
+ aria-expanded={isOpen}
310
+ >
311
+ Open Menu
312
+ </FilledButton>
313
+ </div>
314
+ <Menu
315
+ isOpen={isOpen}
316
+ anchorRef={anchorEl}
317
+ onClose={(event, _data) => {
318
+ setIsOpen(false);
319
+ event?.preventDefault();
320
+ event?.stopPropagation();
321
+ }}
322
+ portalTarget={document.getElementById('modal-root')}
323
+ htmlAttributes={{ id: 'dialog-menu' }}
324
+ >
325
+ <Menu.Item title="Undo" onClick={() => alert('Undo')} />
326
+ <Menu.Item title="Redo" isDisabled />
327
+ <Menu.Item title="Cut" description="Copy and remove" />
328
+ <Menu
329
+ title="Copy as"
330
+ description="Copying as something"
331
+ icon={<Square2StackIconOutline />}
332
+ >
333
+ <Menu.Item title="Text" />
334
+ <Menu.Item title="Video" />
335
+ <Menu title="Image">
336
+ <Menu.Item title=".png" />
337
+ <Menu.Item title=".jpg" />
338
+ <Menu.Item title=".svg" />
339
+ <Menu.Item title=".gif" />
340
+ </Menu>
341
+ <Menu.Item title="Audio" />
342
+ <Menu title="Share">
343
+ <Menu.Item title="Mail" />
344
+ <Menu.Item title="Instagram" />
345
+ </Menu>
346
+ </Menu>
347
+ </Menu>
348
+ </Dialog>
349
+ </>
350
+ );
351
+ };
352
+
353
+ export default Component;
354
+ ```
355
+
356
+ ### In Popover
357
+
358
+ ```tsx
359
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
360
+
361
+ import { FilledButton, Menu, Popover } from '@neo4j-ndl/react';
362
+ import { useRef, useState } from 'react';
363
+
364
+ const Component = () => {
365
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
366
+ const [isOpen, setIsOpen] = useState(false);
367
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
368
+
369
+ return (
370
+ <>
371
+ <Menu
372
+ isOpen={isOpen}
373
+ anchorRef={anchorEl}
374
+ onClose={(_event, _data) => {
375
+ setIsOpen(false);
376
+ }}
377
+ htmlAttributes={{ id: 'popover-menu' }}
378
+ >
379
+ <Menu.Item title="Action 1" onClick={() => alert('Action1')} />
380
+ <Menu.Item title="Action 2" onClick={() => alert('Action2')} />
381
+ <Menu.Item
382
+ title="Action 3"
383
+ description="Action 3 is disabled"
384
+ isDisabled={true}
385
+ onClick={() => alert('Action3')}
386
+ />
387
+ <Menu.Item
388
+ title="Action 4"
389
+ description="Action 4 is awesome"
390
+ onClick={() => alert('Action4')}
391
+ />
392
+ </Menu>
393
+
394
+ <Popover isOpen={isPopoverOpen}>
395
+ <Popover.Trigger>
396
+ <FilledButton onClick={() => setIsPopoverOpen(!isPopoverOpen)}>
397
+ {isPopoverOpen ? 'Close' : 'Open'} Popover
398
+ </FilledButton>
399
+ </Popover.Trigger>
400
+ <Popover.Content style={{ height: 150, overflow: 'auto', width: 400 }}>
401
+ <>
402
+ <p>
403
+ Scroll me to the bottom to open menu Lorem ipsum dolor sit amet,
404
+ consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
405
+ labore et dolore magna aliqua. Ut enim ad minim veniam, quis
406
+ nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
407
+ consequat. Duis aute irure dolor in reprehenderit in voluptate
408
+ velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
409
+ occaecat cupidatat non proident, sunt in culpa qui officia
410
+ deserunt mollit anim id est laborum.
411
+ </p>
412
+ <FilledButton
413
+ onClick={() => setIsOpen(true)}
414
+ ref={anchorEl}
415
+ aria-haspopup="menu"
416
+ aria-expanded={isOpen}
417
+ >
418
+ Open Context Menu
419
+ </FilledButton>
420
+ </>
421
+ </Popover.Content>
422
+ </Popover>
423
+ </>
424
+ );
425
+ };
426
+
427
+ export default Component;
428
+ ```
429
+
430
+ ### Nested
431
+
432
+ ```tsx
433
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
434
+
435
+ import { FilledButton, Menu } from '@neo4j-ndl/react';
436
+ import { Square2StackIconOutline } from '@neo4j-ndl/react/icons';
437
+ import { useRef, useState } from 'react';
438
+
439
+ const Component = () => {
440
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
441
+ const [isOpen, setIsOpen] = useState(false);
442
+
443
+ return (
444
+ <>
445
+ <Menu
446
+ isOpen={isOpen}
447
+ anchorRef={anchorEl}
448
+ onClose={(_event, _data) => {
449
+ setIsOpen(false);
450
+ }}
451
+ htmlAttributes={{ id: 'nested-menu' }}
452
+ >
453
+ <Menu.Item title="Undo" onClick={() => alert('Undo')} />
454
+ <Menu.Item title="Redo" isDisabled />
455
+ <Menu.Item title="Cut" description="Copy and remove" />
456
+ <Menu
457
+ title="Copy as"
458
+ description="Copying as something"
459
+ icon={<Square2StackIconOutline />}
460
+ >
461
+ <Menu.Item title="Text" />
462
+ <Menu.Item title="Video" />
463
+ <Menu title="Image">
464
+ <Menu.Item title=".png" />
465
+ <Menu.Item title=".jpg" />
466
+ <Menu.Item title=".svg" />
467
+ <Menu.Item title=".gif" />
468
+ </Menu>
469
+ <Menu.Item title="Audio" />
470
+ </Menu>
471
+ <Menu title="Share">
472
+ <Menu.Item title="Mail" />
473
+ <Menu.Item title="Instagram" />
474
+ </Menu>
475
+ </Menu>
476
+ <FilledButton
477
+ onClick={() => setIsOpen(true)}
478
+ ref={anchorEl}
479
+ aria-haspopup="menu"
480
+ aria-expanded={isOpen}
481
+ >
482
+ Open Menu
483
+ </FilledButton>
484
+ </>
485
+ );
486
+ };
487
+
488
+ export default Component;
489
+ ```
490
+
491
+ ### Placements
492
+
493
+ ```tsx
494
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
495
+
496
+ import { FilledButton, Menu } from '@neo4j-ndl/react';
497
+ import { useRef, useState } from 'react';
498
+
499
+ const Component = () => {
500
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
501
+ const [isOpen, setIsOpen] = useState(false);
502
+
503
+ return (
504
+ <>
505
+ <Menu
506
+ isOpen={isOpen}
507
+ anchorRef={anchorEl}
508
+ placement="middle-end-middle-start"
509
+ onClose={(_event, _data) => {
510
+ setIsOpen(false);
511
+ }}
512
+ htmlAttributes={{ id: 'placements-menu' }}
513
+ >
514
+ <Menu.Item
515
+ title="Action 1"
516
+ onClick={() => {
517
+ alert('Action1');
518
+ setIsOpen(false);
519
+ }}
520
+ />
521
+ <Menu.Item
522
+ title="Action 2"
523
+ onClick={() => {
524
+ alert('Action2');
525
+ setIsOpen(false);
526
+ }}
527
+ />
528
+ <Menu.Item
529
+ title="Action 3"
530
+ description="Action 3 is disabled"
531
+ isDisabled={true}
532
+ onClick={() => {
533
+ alert('Action3');
534
+ setIsOpen(false);
535
+ }}
536
+ />
537
+ <Menu.Item
538
+ title="Action 4"
539
+ description="Action 4 is awesome"
540
+ onClick={() => {
541
+ alert('Action4');
542
+ setIsOpen(false);
543
+ }}
544
+ />
545
+ <Menu.Item
546
+ id="action_5"
547
+ title="Action 5"
548
+ description="Action 5 does not close the menu"
549
+ />
550
+ </Menu>
551
+
552
+ <FilledButton
553
+ onClick={() => setIsOpen(true)}
554
+ ref={anchorEl}
555
+ aria-haspopup="menu"
556
+ aria-expanded={isOpen}
557
+ >
558
+ Open Menu
559
+ </FilledButton>
560
+ </>
561
+ );
562
+ };
563
+
564
+ export default Component;
565
+ ```
566
+
567
+ ### Radio And Normal
568
+
569
+ ```tsx
570
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
571
+
572
+ import { DropdownButton, Menu, Typography } from '@neo4j-ndl/react';
573
+ import { useRef, useState } from 'react';
574
+
575
+ const Component = () => {
576
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
577
+ const [isOpen, setIsOpen] = useState(false);
578
+ const [selectedOption, setSelectedOption] = useState<string | null>(
579
+ 'option-1',
580
+ );
581
+
582
+ return (
583
+ <>
584
+ <Menu
585
+ isOpen={isOpen}
586
+ anchorRef={anchorEl}
587
+ onClose={(_event, _data) => {
588
+ setIsOpen(false);
589
+ }}
590
+ htmlAttributes={{ id: 'radio-and-normal-menu' }}
591
+ >
592
+ <Menu.Group>
593
+ <Menu.RadioItem
594
+ title="Option 1"
595
+ isChecked={selectedOption === 'option-1'}
596
+ onClick={() => {
597
+ setSelectedOption('option-1');
598
+ }}
599
+ />
600
+ <Menu.RadioItem
601
+ title="Option 2"
602
+ isChecked={selectedOption === 'option-2'}
603
+ onClick={() => {
604
+ setSelectedOption('option-2');
605
+ }}
606
+ />
607
+ </Menu.Group>
608
+ <Menu.Divider />
609
+ <Menu.Group>
610
+ <Menu.Item
611
+ title="Action 1"
612
+ onClick={() => {
613
+ alert('Action 1');
614
+ }}
615
+ />
616
+ <Menu.Item
617
+ title="Action 2"
618
+ onClick={() => {
619
+ alert('Action 2');
620
+ }}
621
+ />
622
+ </Menu.Group>
623
+ </Menu>
624
+ <div className="n-flex n-flex-col n-gap-token-4 n-items-center">
625
+ <Typography variant="body-medium">
626
+ Selected option: <strong>{selectedOption}</strong>
627
+ </Typography>
628
+ <DropdownButton
629
+ isOpen={isOpen}
630
+ onClick={() => setIsOpen(true)}
631
+ ref={anchorEl}
632
+ aria-haspopup="menu"
633
+ >
634
+ Open menu
635
+ </DropdownButton>
636
+ </div>
637
+ </>
638
+ );
639
+ };
640
+
641
+ export default Component;
642
+ ```
643
+
644
+ ### Radio Items
645
+
646
+ ```tsx
647
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
648
+
649
+ import { DropdownButton, Kbd, Menu, Typography } from '@neo4j-ndl/react';
650
+ import { MoonIconOutline, SunIconOutline } from '@neo4j-ndl/react/icons';
651
+ import { useRef, useState } from 'react';
652
+
653
+ const Component = () => {
654
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
655
+ const [isOpen, setIsOpen] = useState(false);
656
+ const [selectedOption, setSelectedOption] = useState<string | null>(
657
+ 'option-1',
658
+ );
659
+
660
+ return (
661
+ <>
662
+ <Menu
663
+ isOpen={isOpen}
664
+ anchorRef={anchorEl}
665
+ onClose={(_event, _data) => {
666
+ setIsOpen(false);
667
+ }}
668
+ htmlAttributes={{ id: 'radio-items-menu' }}
669
+ >
670
+ <Menu.RadioItem
671
+ title="Option 1"
672
+ leadingVisual={<SunIconOutline />}
673
+ isChecked={selectedOption === 'option-1'}
674
+ onClick={() => {
675
+ setSelectedOption('option-1');
676
+ }}
677
+ />
678
+ <Menu.RadioItem
679
+ title="Option 2"
680
+ leadingVisual={<MoonIconOutline />}
681
+ isChecked={selectedOption === 'option-2'}
682
+ onClick={() => {
683
+ setSelectedOption('option-2');
684
+ }}
685
+ />
686
+ <Menu.RadioItem
687
+ title="Option 3"
688
+ description="Option 3 has a description"
689
+ isChecked={selectedOption === 'option-3'}
690
+ onClick={() => {
691
+ setSelectedOption('option-3');
692
+ }}
693
+ />
694
+ <Menu.RadioItem
695
+ title="Option 4"
696
+ trailingContent={<Kbd keys={['A']} />}
697
+ isChecked={selectedOption === 'option-4'}
698
+ onClick={() => {
699
+ setSelectedOption('option-4');
700
+ }}
701
+ />
702
+ </Menu>
703
+ <div className="n-flex n-flex-col n-gap-token-4 n-items-center">
704
+ <Typography variant="body-medium">
705
+ Selected option: <strong>{selectedOption}</strong>
706
+ </Typography>
707
+ <DropdownButton
708
+ isOpen={isOpen}
709
+ onClick={() => setIsOpen(true)}
710
+ ref={anchorEl}
711
+ aria-haspopup="menu"
712
+ >
713
+ Open menu
714
+ </DropdownButton>
715
+ </div>
716
+ </>
717
+ );
718
+ };
719
+
720
+ export default Component;
721
+ ```
722
+
723
+ ### With Category
724
+
725
+ ```tsx
726
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
727
+
728
+ import { FilledButton, Menu } from '@neo4j-ndl/react';
729
+ import { useRef, useState } from 'react';
730
+
731
+ const Component = () => {
732
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
733
+ const [isOpen, setIsOpen] = useState(false);
734
+
735
+ return (
736
+ <>
737
+ <Menu
738
+ isOpen={isOpen}
739
+ anchorRef={anchorEl}
740
+ onClose={(_event, _data) => {
741
+ setIsOpen(false);
742
+ }}
743
+ htmlAttributes={{ id: 'category-menu' }}
744
+ >
745
+ <Menu.Group>
746
+ <Menu.CategoryItem>Category 1</Menu.CategoryItem>
747
+ <Menu.Item title="Action 1" onClick={() => alert('Action1')} />
748
+ <Menu.Item title="Action 2" onClick={() => alert('Action2')} />
749
+ </Menu.Group>
750
+ <Menu.Divider />
751
+ <Menu.Group>
752
+ <Menu.Item
753
+ title="Action 3"
754
+ description="Action 3 is disabled"
755
+ isDisabled={true}
756
+ onClick={() => alert('Action3')}
757
+ />
758
+ <Menu.Item
759
+ title="Action 4"
760
+ description="Action 4 is awesome"
761
+ onClick={() => alert('Action4')}
762
+ />
763
+ </Menu.Group>
764
+ </Menu>
765
+ <FilledButton
766
+ onClick={() => setIsOpen(true)}
767
+ ref={anchorEl}
768
+ aria-haspopup="menu"
769
+ aria-expanded={isOpen}
770
+ >
771
+ Open Menu
772
+ </FilledButton>
773
+ </>
774
+ );
775
+ };
776
+
777
+ export default Component;
778
+ ```
779
+
780
+ ### With Disabled Item
781
+
782
+ ```tsx
783
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
784
+
785
+ import { FilledButton, Menu } from '@neo4j-ndl/react';
786
+ import { useRef, useState } from 'react';
787
+
788
+ const Component = () => {
789
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
790
+ const [isOpen, setIsOpen] = useState(false);
791
+
792
+ return (
793
+ <>
794
+ <Menu
795
+ isOpen={isOpen}
796
+ anchorRef={anchorEl}
797
+ onClose={(_event, _data) => {
798
+ setIsOpen(false);
799
+ }}
800
+ htmlAttributes={{ id: 'disabled-item-menu' }}
801
+ >
802
+ <Menu.Item
803
+ title="Action 1"
804
+ description="Action 1 is disabled"
805
+ isDisabled={true}
806
+ />
807
+ <Menu.Item title="Action 2" onClick={() => alert('Action2')} />
808
+ </Menu>
809
+ <FilledButton
810
+ onClick={() => setIsOpen(true)}
811
+ ref={anchorEl}
812
+ aria-haspopup="menu"
813
+ aria-expanded={isOpen}
814
+ >
815
+ Open Menu
816
+ </FilledButton>
817
+ </>
818
+ );
819
+ };
820
+
821
+ export default Component;
822
+ ```
823
+
824
+ ### With Divider
825
+
826
+ ```tsx
827
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
828
+
829
+ import { FilledButton, Menu } from '@neo4j-ndl/react';
830
+ import { useRef, useState } from 'react';
831
+
832
+ const Component = () => {
833
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
834
+ const [isOpen, setIsOpen] = useState(false);
835
+
836
+ return (
837
+ <>
838
+ <Menu
839
+ isOpen={isOpen}
840
+ anchorRef={anchorEl}
841
+ onClose={(_event, _data) => {
842
+ setIsOpen(false);
843
+ }}
844
+ htmlAttributes={{ id: 'divider-menu' }}
845
+ >
846
+ <Menu.Item title="Action 1" />
847
+ <Menu.Divider />
848
+ <Menu.Item title="Action 2" />
849
+ <Menu.Divider />
850
+ <Menu.Item title="Action 3" />
851
+ <Menu.Divider />
852
+ <Menu.Item title="Action 4" />
853
+ </Menu>
854
+ <FilledButton
855
+ onClick={() => setIsOpen(true)}
856
+ ref={anchorEl}
857
+ aria-haspopup="menu"
858
+ aria-expanded={isOpen}
859
+ >
860
+ Open Menu
861
+ </FilledButton>
862
+ </>
863
+ );
864
+ };
865
+
866
+ export default Component;
867
+ ```
868
+
869
+ ### With Icon
870
+
871
+ ```tsx
872
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
873
+
874
+ import { FilledButton, Menu } from '@neo4j-ndl/react';
875
+ import { PhotoIconOutline, PlusIconOutline } from '@neo4j-ndl/react/icons';
876
+ import { useRef, useState } from 'react';
877
+
878
+ const Component = () => {
879
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
880
+ const [isOpen, setIsOpen] = useState(false);
881
+
882
+ return (
883
+ <>
884
+ <Menu
885
+ isOpen={isOpen}
886
+ anchorRef={anchorEl}
887
+ onClose={(_event, _data) => {
888
+ setIsOpen(false);
889
+ }}
890
+ htmlAttributes={{ id: 'icon-menu' }}
891
+ >
892
+ <Menu.Item
893
+ leadingVisual={<PlusIconOutline />}
894
+ title="Action 1"
895
+ onClick={() => alert('Action1')}
896
+ />
897
+ <Menu.Item
898
+ leadingVisual={<PlusIconOutline />}
899
+ title="Action 2"
900
+ onClick={() => alert('Action2')}
901
+ />
902
+ <Menu.Item
903
+ leadingVisual={<PlusIconOutline />}
904
+ title="Action 3"
905
+ description="Action 3 is disabled"
906
+ isDisabled={true}
907
+ onClick={() => alert('Action3')}
908
+ />
909
+ <Menu
910
+ title="Action 3.1"
911
+ description="Action with sub menu"
912
+ icon={<PlusIconOutline />}
913
+ >
914
+ <Menu.Item title="Text" />
915
+ <Menu.Item title="Video" />
916
+ <Menu title="Image" icon={<PhotoIconOutline />}>
917
+ <Menu.Item title=".png" />
918
+ <Menu.Item title=".jpg" />
919
+ </Menu>
920
+ <Menu.Item title="Audio" />
921
+ </Menu>
922
+ <Menu.Item
923
+ leadingVisual={<PlusIconOutline />}
924
+ title="Action 4"
925
+ description="Action 4 is awesome"
926
+ onClick={() => alert('Action4')}
927
+ />
928
+ </Menu>
929
+ <FilledButton
930
+ onClick={() => setIsOpen(true)}
931
+ ref={anchorEl}
932
+ aria-haspopup="menu"
933
+ aria-expanded={isOpen}
934
+ >
935
+ Open Menu
936
+ </FilledButton>
937
+ </>
938
+ );
939
+ };
940
+
941
+ export default Component;
942
+ ```
943
+
944
+ ### With Keyboard Shortcuts
945
+
946
+ ```tsx
947
+ import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';
948
+
949
+ import { FilledButton, Kbd, Menu } from '@neo4j-ndl/react';
950
+ import { PlusIconOutline } from '@neo4j-ndl/react/icons';
951
+ import { useRef, useState } from 'react';
952
+
953
+ const Component = () => {
954
+ const anchorEl = useRef<HTMLButtonElement | null>(null);
955
+ const [isOpen, setIsOpen] = useState(false);
956
+
957
+ return (
958
+ <>
959
+ <Menu
960
+ isOpen={isOpen}
961
+ anchorRef={anchorEl}
962
+ onClose={(_event, _data) => {
963
+ setIsOpen(false);
964
+ }}
965
+ htmlAttributes={{ id: 'keyboard-shortcuts-menu' }}
966
+ >
967
+ <Menu.Item
968
+ leadingVisual={<PlusIconOutline />}
969
+ title="Action 1"
970
+ onClick={() => alert('Action1')}
971
+ />
972
+ <Menu.Item
973
+ leadingVisual={<PlusIconOutline />}
974
+ trailingContent={<Kbd keys={['A', ['B']]} />}
975
+ title="Action 2"
976
+ description="Action 4 has a keyboard shortcut"
977
+ onClick={() => alert('Action4')}
978
+ />
979
+ </Menu>
980
+ <FilledButton
981
+ onClick={() => setIsOpen(true)}
982
+ ref={anchorEl}
983
+ aria-haspopup="menu"
984
+ aria-expanded={isOpen}
985
+ >
986
+ Open Menu
987
+ </FilledButton>
988
+ </>
989
+ );
990
+ };
991
+
992
+ export default Component;
993
+ ```