@splunk/react-ui 5.7.1 → 5.9.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 (152) hide show
  1. package/Accordion.js +6 -6
  2. package/Anchor.js +2 -1
  3. package/Box.js +83 -34
  4. package/CHANGELOG.md +51 -0
  5. package/Calendar.js +134 -134
  6. package/Clickable.js +131 -94
  7. package/CollapsiblePanel.js +175 -137
  8. package/ComboBox.js +32 -27
  9. package/ControlGroup.js +92 -91
  10. package/DefinitionList.js +9 -9
  11. package/Drawer.d.ts +2 -0
  12. package/Drawer.js +679 -0
  13. package/Dropdown.js +27 -18
  14. package/DualListbox.js +1 -1
  15. package/File.js +35 -35
  16. package/JSONTree.js +73 -72
  17. package/Link.js +2 -2
  18. package/MIGRATION.md +10 -0
  19. package/Menu.js +403 -261
  20. package/Modal.js +263 -252
  21. package/Monogram.js +2 -2
  22. package/Multiselect.js +551 -385
  23. package/Number.js +2 -1
  24. package/Paginator.js +14 -12
  25. package/Popover.js +4 -1
  26. package/README.md +11 -0
  27. package/RadioBar.js +1 -1
  28. package/Search.js +111 -95
  29. package/Select.js +42 -40
  30. package/SelectBase.js +819 -715
  31. package/SidePanel.js +346 -167
  32. package/SlidingPanels.js +11 -11
  33. package/StepBar.js +7 -7
  34. package/Switch.js +5 -5
  35. package/Table.js +116 -119
  36. package/Text.js +48 -48
  37. package/TextArea.js +7 -7
  38. package/TransitionOpen.js +188 -169
  39. package/docs-llm/Accordion.md +267 -0
  40. package/docs-llm/Anchor Menu.md +115 -0
  41. package/docs-llm/Anchor.md +54 -0
  42. package/docs-llm/AnimationToggle.md +254 -0
  43. package/docs-llm/Avatar.md +292 -0
  44. package/docs-llm/Badge.md +212 -0
  45. package/docs-llm/Breadcrumbs.md +306 -0
  46. package/docs-llm/Button Group.md +53 -0
  47. package/docs-llm/Button.md +361 -0
  48. package/docs-llm/Card Layout.md +286 -0
  49. package/docs-llm/Card.md +619 -0
  50. package/docs-llm/Checkbox.md +218 -0
  51. package/docs-llm/Chip.md +291 -0
  52. package/docs-llm/Clickable.md +160 -0
  53. package/docs-llm/Code.md +292 -0
  54. package/docs-llm/Collapsible Panel.md +744 -0
  55. package/docs-llm/Color.md +253 -0
  56. package/docs-llm/Column Layout.md +391 -0
  57. package/docs-llm/Combo Box.md +540 -0
  58. package/docs-llm/Control Group.md +594 -0
  59. package/docs-llm/Date.md +270 -0
  60. package/docs-llm/Definition List.md +278 -0
  61. package/docs-llm/Divider.md +216 -0
  62. package/docs-llm/Drawer.md +414 -0
  63. package/docs-llm/Dropdown.md +472 -0
  64. package/docs-llm/Dual Listbox.md +325 -0
  65. package/docs-llm/File.md +653 -0
  66. package/docs-llm/Form Rows.md +374 -0
  67. package/docs-llm/Heading.md +179 -0
  68. package/docs-llm/Image.md +109 -0
  69. package/docs-llm/JSON Tree.md +260 -0
  70. package/docs-llm/Layer.md +74 -0
  71. package/docs-llm/Layout.md +50 -0
  72. package/docs-llm/Link.md +318 -0
  73. package/docs-llm/List.md +189 -0
  74. package/docs-llm/Markdown.md +179 -0
  75. package/docs-llm/Menu.md +735 -0
  76. package/docs-llm/Message Bar.md +236 -0
  77. package/docs-llm/Message.md +248 -0
  78. package/docs-llm/Modal.md +443 -0
  79. package/docs-llm/Monogram.md +159 -0
  80. package/docs-llm/Multiselect.md +939 -0
  81. package/docs-llm/Notifications.md +46 -0
  82. package/docs-llm/Number.md +298 -0
  83. package/docs-llm/Paginator.md +395 -0
  84. package/docs-llm/Paragraph.md +148 -0
  85. package/docs-llm/Phone Number.md +254 -0
  86. package/docs-llm/Popover.md +166 -0
  87. package/docs-llm/Progress.md +141 -0
  88. package/docs-llm/Radio Bar.md +303 -0
  89. package/docs-llm/Radio List.md +350 -0
  90. package/docs-llm/Resize.md +362 -0
  91. package/docs-llm/Screen Reader Content.md +73 -0
  92. package/docs-llm/Scroll Container Context.md +155 -0
  93. package/docs-llm/Scroll.md +152 -0
  94. package/docs-llm/Search.md +381 -0
  95. package/docs-llm/Select.md +985 -0
  96. package/docs-llm/Side Panel.md +777 -0
  97. package/docs-llm/Slider.md +339 -0
  98. package/docs-llm/Sliding Panels.md +340 -0
  99. package/docs-llm/Split Button.md +295 -0
  100. package/docs-llm/Static Content.md +90 -0
  101. package/docs-llm/Step Bar.md +292 -0
  102. package/docs-llm/Switch.md +268 -0
  103. package/docs-llm/Tab Bar.md +439 -0
  104. package/docs-llm/Tab Layout.md +398 -0
  105. package/docs-llm/Table.md +2642 -0
  106. package/docs-llm/Text Area.md +253 -0
  107. package/docs-llm/Text.md +339 -0
  108. package/docs-llm/Tooltip.md +325 -0
  109. package/docs-llm/Transition Open.md +406 -0
  110. package/docs-llm/Tree.md +591 -0
  111. package/docs-llm/Typography.md +125 -0
  112. package/docs-llm/Wait Spinner.md +121 -0
  113. package/docs-llm/llms.txt +101 -0
  114. package/package.json +6 -5
  115. package/types/src/Box/Box.d.ts +2 -10
  116. package/types/src/Drawer/Body.d.ts +17 -0
  117. package/types/src/Drawer/Drawer.d.ts +114 -0
  118. package/types/src/Drawer/DrawerContext.d.ts +11 -0
  119. package/types/src/Drawer/Footer.d.ts +25 -0
  120. package/types/src/Drawer/Header.d.ts +41 -0
  121. package/types/src/Drawer/docs/examples/Basic.d.ts +6 -0
  122. package/types/src/Drawer/docs/examples/ContainerPosition.d.ts +7 -0
  123. package/types/src/Drawer/docs/examples/InitialFocus.d.ts +9 -0
  124. package/types/src/Drawer/docs/examples/InlinePosition.d.ts +7 -0
  125. package/types/src/Drawer/docs/examples/PagePosition.d.ts +7 -0
  126. package/types/src/Drawer/index.d.ts +2 -0
  127. package/types/src/JSONTree/JSONTree.d.ts +12 -5
  128. package/types/src/JSONTree/renderTreeItems.d.ts +2 -1
  129. package/types/src/Menu/Item.d.ts +2 -1
  130. package/types/src/Menu/docs/examples/SelectableCheckbox.d.ts +7 -0
  131. package/types/src/Modal/Modal.d.ts +1 -2
  132. package/types/src/Multiselect/Compact.d.ts +8 -3
  133. package/types/src/Multiselect/Multiselect.d.ts +8 -3
  134. package/types/src/Multiselect/Normal.d.ts +8 -3
  135. package/types/src/Multiselect/Option.d.ts +6 -3
  136. package/types/src/Multiselect/docs/examples/Disabled.d.ts +1 -0
  137. package/types/src/Select/Option.d.ts +6 -3
  138. package/types/src/Select/Select.d.ts +8 -5
  139. package/types/src/Select/docs/examples/Dimmed.d.ts +7 -0
  140. package/types/src/SelectBase/OptionBase.d.ts +6 -3
  141. package/types/src/SelectBase/SelectBase.d.ts +8 -3
  142. package/types/src/SidePanel/SidePanel.d.ts +43 -2
  143. package/types/src/SidePanel/docs/examples/DockLayout.d.ts +17 -0
  144. package/types/src/SidePanel/docs/examples/InitialFocus.d.ts +9 -0
  145. package/types/src/TransitionOpen/TransitionOpen.d.ts +29 -4
  146. package/types/src/useKeyPress/index.d.ts +9 -2
  147. package/types/src/useOnClickOutside/index.d.ts +2 -0
  148. package/types/src/useOnClickOutside/useOnClickOutside.d.ts +4 -0
  149. package/useKeyPress.js +23 -18
  150. package/useOnClickOutside.d.ts +2 -0
  151. package/useOnClickOutside.js +79 -0
  152. package/types/src/RadioList/docs/examples/Row.d.ts +0 -6
@@ -0,0 +1,591 @@
1
+ # Tree
2
+
3
+ ## Overview
4
+
5
+
6
+ > Image: Illustration of Tree component
7
+
8
+
9
+ ## When to use this component
10
+
11
+ The `Tree` component displays hierarchical data, allowing users to expand, collapse, and select nested items efficiently.
12
+
13
+ - When you need to show nested or grouped data in a clear, expandable structure.
14
+ - When users must select or interact with items at multiple levels of a hierarchy.
15
+ - When space is limited but hierarchical context is important.
16
+
17
+ ### Additional considerations
18
+ - For large datasets, consider lazy loading or virtualization to maintain performance.
19
+ - Ensure keyboard navigation and screen reader support for accessibility.
20
+
21
+ ## When to use another component
22
+ - When you only need to show a flat list, use a `List`.
23
+ - For simple selection from a set of options, use a `ComboBox` or `Select`.
24
+ - For non-hierarchical multiple selections, use a `Multiselect`.
25
+ - If you need to display tabular data, use a `Table`.
26
+
27
+ ```mermaid
28
+ graph TD
29
+ accDescr: Decision tree that guides on when to use Tree or something else
30
+ A(Is the data hierarchical or nested?) -- Yes --- B(Do you need to expand/collapse or select items at multiple levels?)
31
+ A -- No --- C(ComboBox, Multiselect, Select, or Table)
32
+ B -- Yes --- D(Tree)
33
+ B -- No --- E(List)
34
+ ```
35
+
36
+ ### Check out
37
+ - [List][1]
38
+ - [ComboBox][2]
39
+ - [Select][3]
40
+ - [Multiselect][4]
41
+ - [Table][5]
42
+
43
+ ## Behaviors
44
+
45
+ ### Expand/Collapse
46
+
47
+ Allows users to expand or collapse branches to view nested items. Users can navigate and interact with the tree using keyboard controls.
48
+
49
+ > Image: Tree expand/collapse behavior demonstration
50
+
51
+
52
+ ### Selection
53
+
54
+ Nodes within the Tree can be selectable, enabling users to highlight or activate specific items. Keyboard focus management is integral to ensure accessibility and smooth interaction.
55
+
56
+ > Image: Tree selection behavior demonstration
57
+
58
+
59
+ ## Usage
60
+
61
+ ### Grouping and organization
62
+
63
+ Use the `Tree` component to organize items with clear parent-child relationships, such as file directories, organizational charts, or nested categories. Group related items together to improve clarity and help users understand the hierarchy.
64
+
65
+ > Image: Grouping items in Tree. The first example with heart eyes emoji shows clear organization; the second example with grimacing emoji shows cluttered grouping.
66
+
67
+
68
+ ## Content
69
+
70
+ ### Concise labels
71
+
72
+ Use concise, descriptive labels for each node, ideally 1-3 words, employing sentence-style capitalization. Labels should clearly convey the content or category represented by the node.
73
+
74
+ > Image: Tree node label examples. The first example with heart eyes emoji shows clear, brief labels; the second example with grimacing emoji shows overly long or vague labels.
75
+
76
+
77
+
78
+ [1]: ./List
79
+ [2]: ./ComboBox
80
+ [3]: ./Select
81
+ [4]: ./Multiselect
82
+ [5]: ./Table
83
+
84
+
85
+ ## Examples
86
+
87
+
88
+ ### Basic
89
+
90
+ ```typescript
91
+ import React, { useCallback, useState } from 'react';
92
+
93
+ import Tree, { TreeItemToggleExpansionHandler } from '@splunk/react-ui/Tree';
94
+
95
+
96
+ export default function Basic() {
97
+ const [expandedIdsMap, setExpandedIdsMap] = useState(
98
+ new Map([
99
+ ['two', true],
100
+ ['three', true],
101
+ ])
102
+ );
103
+
104
+ const handleToggleExpansion: TreeItemToggleExpansionHandler = useCallback(
105
+ (event, { treeItemId } = {}) => {
106
+ if (!treeItemId) {
107
+ return;
108
+ }
109
+
110
+ setExpandedIdsMap((prevMap) => {
111
+ const newMap = new Map(prevMap);
112
+ if (newMap.has(treeItemId)) {
113
+ newMap.delete(treeItemId);
114
+ } else {
115
+ newMap.set(treeItemId, true);
116
+ }
117
+
118
+ return newMap;
119
+ });
120
+ },
121
+ []
122
+ );
123
+
124
+ return (
125
+ <Tree>
126
+ <Tree.Item content="node-0" id="one" />
127
+ <Tree.Item
128
+ content="node-1"
129
+ id="two"
130
+ expanded={expandedIdsMap.has('two')}
131
+ onToggleExpansion={handleToggleExpansion}
132
+ >
133
+ <Tree.Item
134
+ content="node-1-0"
135
+ id="three"
136
+ expanded={expandedIdsMap.has('three')}
137
+ onToggleExpansion={handleToggleExpansion}
138
+ >
139
+ <Tree.Item content="node-1-0-0" id="four" />
140
+ <Tree.Item content="node-1-0-1" id="five" />
141
+ </Tree.Item>
142
+ <Tree.Item content="node-1-1" id="six" />
143
+ </Tree.Item>
144
+ <Tree.Item content="node-2" id="seven" />
145
+ </Tree>
146
+ );
147
+ }
148
+ ```
149
+
150
+
151
+
152
+ ### StyledExpansionToggleWrapper
153
+
154
+ ```typescript
155
+ import React, { useCallback, useMemo, useState } from 'react';
156
+
157
+ import styled from 'styled-components';
158
+
159
+ import ChevronDown from '@splunk/react-icons/ChevronDown';
160
+ import ChevronRight from '@splunk/react-icons/ChevronRight';
161
+ import Tree, { TreeItemPropsBase, TreeItemToggleExpansionHandler } from '@splunk/react-ui/Tree';
162
+ import { variables } from '@splunk/themes';
163
+
164
+ const StyledExpansionToggleWrapper = styled.span`
165
+ display: inline-flex;
166
+ width: 16px;
167
+ justify-content: center;
168
+ align-items: center;
169
+ padding-inline: ${variables.spacingMedium};
170
+ `;
171
+ const StyledSpan = styled.span`
172
+ display: inline-flex;
173
+ padding: ${variables.spacingXSmall} 0;
174
+ `;
175
+
176
+
177
+ const ExpansionToggle = ({
178
+ expanded,
179
+ treeItemId,
180
+ onToggleExpansion,
181
+ }: {
182
+ expanded: boolean;
183
+ treeItemId: string;
184
+ onToggleExpansion: TreeItemPropsBase['onToggleExpansion'];
185
+ }) => (
186
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
187
+ <span
188
+ onClick={(e) => {
189
+ e.preventDefault();
190
+ onToggleExpansion?.(e, { treeItemId });
191
+ }}
192
+ >
193
+ {expanded ? <ChevronDown /> : <ChevronRight />}
194
+ </span>
195
+ );
196
+
197
+ const TreeItemWithExpansion = ({
198
+ children,
199
+ content,
200
+ expanded,
201
+ id,
202
+ onToggleExpansion,
203
+ ...otherTreeItemProps
204
+ }: TreeItemPropsBase) => {
205
+ const contentWithExpansion = useMemo(() => {
206
+ const renderExpansionToggle = () => {
207
+ if (!children) {
208
+ return undefined;
209
+ }
210
+
211
+ return (
212
+ <ExpansionToggle
213
+ expanded={expanded || false}
214
+ onToggleExpansion={onToggleExpansion}
215
+ treeItemId={id}
216
+ />
217
+ );
218
+ };
219
+
220
+ return (
221
+ <StyledSpan>
222
+ <StyledExpansionToggleWrapper>
223
+ {renderExpansionToggle()}
224
+ </StyledExpansionToggleWrapper>
225
+ {content}
226
+ </StyledSpan>
227
+ );
228
+ }, [children, content, expanded, id, onToggleExpansion]);
229
+
230
+ return (
231
+ <Tree.Item
232
+ content={contentWithExpansion}
233
+ expanded={expanded}
234
+ id={id}
235
+ onToggleExpansion={onToggleExpansion}
236
+ {...otherTreeItemProps}
237
+ >
238
+ {children}
239
+ </Tree.Item>
240
+ );
241
+ };
242
+
243
+ export default function ClickableExpansion() {
244
+ const [expandedIdsMap, setExpandedIdsMap] = useState(
245
+ new Map([
246
+ ['two', true],
247
+ ['three', true],
248
+ ])
249
+ );
250
+
251
+ const handleToggleExpansion: TreeItemToggleExpansionHandler = useCallback(
252
+ (event, { treeItemId } = {}) => {
253
+ if (!treeItemId) {
254
+ return;
255
+ }
256
+
257
+ setExpandedIdsMap((prevMap) => {
258
+ const newMap = new Map(prevMap);
259
+ if (newMap.has(treeItemId)) {
260
+ newMap.delete(treeItemId);
261
+ } else {
262
+ newMap.set(treeItemId, true);
263
+ }
264
+
265
+ return newMap;
266
+ });
267
+ },
268
+ []
269
+ );
270
+
271
+ return (
272
+ <Tree data-test="tree-fixture">
273
+ <TreeItemWithExpansion content="node-0" id="one" />
274
+ <TreeItemWithExpansion
275
+ content="node-1"
276
+ id="two"
277
+ expanded={expandedIdsMap.has('two')}
278
+ onToggleExpansion={handleToggleExpansion}
279
+ >
280
+ <TreeItemWithExpansion
281
+ content="node-1-0"
282
+ id="three"
283
+ expanded={expandedIdsMap.has('three')}
284
+ onToggleExpansion={handleToggleExpansion}
285
+ >
286
+ <TreeItemWithExpansion content="node-1-0-0" id="four" />
287
+ <TreeItemWithExpansion content="node-1-0-1" id="five" />
288
+ </TreeItemWithExpansion>
289
+ <TreeItemWithExpansion content="node-1-1" id="six" />
290
+ </TreeItemWithExpansion>
291
+ <TreeItemWithExpansion content="node-2" id="seven" />
292
+ </Tree>
293
+ );
294
+ }
295
+ ```
296
+
297
+
298
+
299
+ ### ClickableExpansioWithSelection
300
+
301
+ ```typescript
302
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
303
+
304
+ import styled from 'styled-components';
305
+
306
+ import ChevronDown from '@splunk/react-icons/ChevronDown';
307
+ import ChevronRight from '@splunk/react-icons/ChevronRight';
308
+ import Checkbox from '@splunk/react-ui/Checkbox';
309
+ import Tree, {
310
+ TreeItemPropsBase,
311
+ TreeItemToggleExpansionHandler,
312
+ TreeItemToggleSelectionHandler,
313
+ } from '@splunk/react-ui/Tree';
314
+ import { variables } from '@splunk/themes';
315
+
316
+ const StyledExpansionToggleWrapper = styled.span`
317
+ display: inline-flex;
318
+ width: 16px;
319
+ justify-content: center;
320
+ align-items: center;
321
+ padding-inline: ${variables.spacingMedium};
322
+ `;
323
+ const StyledCheckbox = styled(Checkbox)`
324
+ padding-inline-end: ${variables.spacingSmall};
325
+ `;
326
+ const StyledSpan = styled.span`
327
+ align-items: center;
328
+ display: inline-flex;
329
+ min-height: 100%;
330
+ padding: ${variables.spacingXSmall} 0;
331
+ `;
332
+
333
+
334
+ const ExpansionToggle = ({
335
+ expanded,
336
+ onToggleExpansion,
337
+ treeItemId,
338
+ }: {
339
+ expanded: boolean;
340
+ onToggleExpansion: TreeItemPropsBase['onToggleExpansion'];
341
+ treeItemId: string;
342
+ }) => (
343
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
344
+ <span
345
+ onClick={(e) => {
346
+ e.preventDefault();
347
+ onToggleExpansion?.(e, { treeItemId });
348
+ }}
349
+ >
350
+ {expanded ? <ChevronDown /> : <ChevronRight />}
351
+ </span>
352
+ );
353
+
354
+ const ItemSelectionCheckbox = ({
355
+ selected,
356
+ onToggleSelection,
357
+ treeItemId,
358
+ }: {
359
+ selected?: boolean;
360
+ onToggleSelection: TreeItemPropsBase['onToggleSelection'];
361
+ treeItemId: string;
362
+ }) => (
363
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
364
+ <span
365
+ onClick={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
366
+ e.preventDefault();
367
+ onToggleSelection?.(e, { treeItemId });
368
+ }}
369
+ style={{ display: 'inline-flex', userSelect: 'none' }}
370
+ >
371
+ <StyledCheckbox checked={selected} inert />
372
+ </span>
373
+ );
374
+
375
+ const TreeItemWithExpansionAndSelection = ({
376
+ children,
377
+ expanded,
378
+ id,
379
+ label,
380
+ onToggleSelection,
381
+ onToggleExpansion,
382
+ selected,
383
+ ...otherTreeItemProps
384
+ }: Omit<TreeItemPropsBase, 'content'> & {
385
+ label: string;
386
+ selected?: boolean;
387
+ }) => {
388
+ const treeItemRef = useRef<HTMLLIElement>(null);
389
+
390
+ const content = useMemo(() => {
391
+ const renderExpansionToggle = () => {
392
+ if (!children) {
393
+ return undefined;
394
+ }
395
+
396
+ return (
397
+ <ExpansionToggle
398
+ expanded={expanded || false}
399
+ onToggleExpansion={onToggleExpansion}
400
+ treeItemId={id}
401
+ />
402
+ );
403
+ };
404
+
405
+ return (
406
+ <StyledSpan>
407
+ <StyledExpansionToggleWrapper>
408
+ {renderExpansionToggle()}
409
+ </StyledExpansionToggleWrapper>
410
+ <ItemSelectionCheckbox
411
+ selected={selected}
412
+ onToggleSelection={onToggleSelection}
413
+ treeItemId={id}
414
+ />
415
+ {label}
416
+ </StyledSpan>
417
+ );
418
+ }, [children, expanded, id, label, onToggleExpansion, onToggleSelection, selected]);
419
+
420
+ return (
421
+ <Tree.Item
422
+ aria-selected={selected ? 'true' : 'false'}
423
+ content={content}
424
+ elementRef={treeItemRef}
425
+ expanded={expanded}
426
+ id={id}
427
+ onToggleExpansion={onToggleExpansion}
428
+ onToggleSelection={onToggleSelection}
429
+ {...otherTreeItemProps}
430
+ >
431
+ {children}
432
+ </Tree.Item>
433
+ );
434
+ };
435
+
436
+ export default function ClickableExpansionWithSelection() {
437
+ const [expandedIdsMap, setExpandedIdsMap] = useState(
438
+ new Map([
439
+ ['two', true],
440
+ ['three', true],
441
+ ])
442
+ );
443
+ const [selectedIdsMap, setSelectedIdsMap] = useState(
444
+ new Map([
445
+ ['two', true],
446
+ ['three', true],
447
+ ])
448
+ );
449
+
450
+ const handleToggleExpansion: TreeItemToggleExpansionHandler = useCallback(
451
+ (event, { treeItemId } = {}) => {
452
+ if (!treeItemId) {
453
+ return;
454
+ }
455
+
456
+ setExpandedIdsMap((prevMap) => {
457
+ const newMap = new Map(prevMap);
458
+ if (newMap.has(treeItemId)) {
459
+ newMap.delete(treeItemId);
460
+ } else {
461
+ newMap.set(treeItemId, true);
462
+ }
463
+
464
+ return newMap;
465
+ });
466
+ },
467
+ []
468
+ );
469
+
470
+ const handleToggleSelected: TreeItemToggleSelectionHandler = useCallback(
471
+ (event, { treeItemId } = {}) => {
472
+ if (!treeItemId) {
473
+ return;
474
+ }
475
+
476
+ setSelectedIdsMap((prevMap) => {
477
+ const newMap = new Map(prevMap);
478
+ if (newMap.has(treeItemId)) {
479
+ newMap.delete(treeItemId);
480
+ } else {
481
+ newMap.set(treeItemId, true);
482
+ }
483
+
484
+ return newMap;
485
+ });
486
+ },
487
+ []
488
+ );
489
+
490
+ return (
491
+ <Tree aria-multiselectable="true" data-test="tree-fixture">
492
+ <TreeItemWithExpansionAndSelection
493
+ id="one"
494
+ label="node-0"
495
+ onToggleSelection={handleToggleSelected}
496
+ selected={selectedIdsMap.has('one')}
497
+ />
498
+ <TreeItemWithExpansionAndSelection
499
+ expanded={expandedIdsMap.has('two')}
500
+ id="two"
501
+ label="node-1"
502
+ onToggleExpansion={handleToggleExpansion}
503
+ onToggleSelection={handleToggleSelected}
504
+ selected={selectedIdsMap.has('two')}
505
+ >
506
+ <TreeItemWithExpansionAndSelection
507
+ id="three"
508
+ expanded={expandedIdsMap.has('three')}
509
+ label="node-1-0"
510
+ onToggleExpansion={handleToggleExpansion}
511
+ onToggleSelection={handleToggleSelected}
512
+ selected={selectedIdsMap.has('three')}
513
+ >
514
+ <TreeItemWithExpansionAndSelection
515
+ id="four"
516
+ label="node-1-0-0"
517
+ onToggleSelection={handleToggleSelected}
518
+ selected={selectedIdsMap.has('four')}
519
+ />
520
+ <TreeItemWithExpansionAndSelection
521
+ id="five"
522
+ label="node-1-0-1"
523
+ onToggleSelection={handleToggleSelected}
524
+ selected={selectedIdsMap.has('five')}
525
+ />
526
+ </TreeItemWithExpansionAndSelection>
527
+ <TreeItemWithExpansionAndSelection
528
+ id="six"
529
+ label="node-1-1"
530
+ onToggleSelection={handleToggleSelected}
531
+ selected={selectedIdsMap.has('six')}
532
+ />
533
+ </TreeItemWithExpansionAndSelection>
534
+ <TreeItemWithExpansionAndSelection
535
+ id="seven"
536
+ label="node-2"
537
+ onToggleSelection={handleToggleSelected}
538
+ selected={selectedIdsMap.has('seven')}
539
+ />
540
+ </Tree>
541
+ );
542
+ }
543
+ ```
544
+
545
+
546
+
547
+
548
+ ## API
549
+
550
+
551
+ ### Tree API
552
+
553
+ Used to present a hierarchical list.
554
+
555
+ #### Props
556
+
557
+ | Name | Type | Required | Default | Description |
558
+ |------|------|------|------|------|
559
+ | children | React.ReactNode | no | | Should contain `Tree.Item`s, can also include other elements to display in between tree items. |
560
+ | defaultIndent | boolean | no | true | Removes default indent from list styles if set to false. |
561
+ | elementRef | React.Ref<HTMLUListElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
562
+
563
+
564
+
565
+ ### Tree.Item API
566
+
567
+ #### Props
568
+
569
+ | Name | Type | Required | Default | Description |
570
+ |------|------|------|------|------|
571
+ | children | React.ReactNode | no | | Should contain `Tree.Item`s, can also include other elements to display in between tree items. |
572
+ | content | React.ReactNode | no | | Content to show on the `Tree.Item`. |
573
+ | elementRef | React.Ref<HTMLLIElement> | no | | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
574
+ | expanded | boolean | no | | Expansion state of the `Tree.Item`. |
575
+ | id | string | yes | | A unique `id` for this item and used by `Tree` to keep track of the focused item. |
576
+ | onFocus | React.FocusEventHandler<HTMLLIElement> | no | | |
577
+ | onKeyDown | React.KeyboardEventHandler<HTMLLIElement> | no | | |
578
+ | onToggleExpansion | TreeItemToggleExpansionHandler | no | | Called on expansion state change of the `Tree.Item` and should be used to maintain `expanded`. For proper keyboard accessibility this is required when a `Tree.Item` has children. |
579
+ | onToggleSelection | TreeItemToggleSelectionHandler | no | | Called on selection state change of the `Tree.Item` and can be used to maintain optional external `Tree.Item` selection state. |
580
+
581
+ #### Types
582
+
583
+ | Name | Type | Description |
584
+ |------|------|------|
585
+ | TreeItemToggleExpansionHandler | ( event: React.KeyboardEvent<HTMLLIElement> \| React.MouseEvent<HTMLSpanElement>, data?: { // Used to support clickable items inside `Tree.Item` `content` that won't have the `Tree.Item`'s id in its click event treeItemId?: string; } ) => void | |
586
+ | TreeItemToggleSelectionHandler | ( event: React.KeyboardEvent<HTMLLIElement> \| React.MouseEvent<HTMLSpanElement>, data?: { // Used to support clickable items inside `Tree.Item` `content` that won't have the `Tree.Item`'s id in its click event treeItemId?: string; } ) => void | |
587
+
588
+
589
+
590
+
591
+