@instructure/ui-tree-browser 11.6.0 → 11.6.1-snapshot-129

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 (168) hide show
  1. package/CHANGELOG.md +33 -288
  2. package/es/TreeBrowser/{TreeButton → v1/TreeButton}/index.js +2 -2
  3. package/es/TreeBrowser/{TreeCollection → v1/TreeCollection}/index.js +1 -1
  4. package/es/TreeBrowser/{TreeNode → v1/TreeNode}/index.js +2 -2
  5. package/es/TreeBrowser/{index.js → v1/index.js} +1 -1
  6. package/es/TreeBrowser/v2/TreeBrowserContext.js +39 -0
  7. package/es/TreeBrowser/v2/TreeButton/index.js +202 -0
  8. package/es/TreeBrowser/v2/TreeButton/props.js +26 -0
  9. package/es/TreeBrowser/v2/TreeButton/styles.js +306 -0
  10. package/es/TreeBrowser/v2/TreeCollection/index.js +367 -0
  11. package/es/TreeBrowser/v2/TreeCollection/props.js +26 -0
  12. package/es/TreeBrowser/v2/TreeCollection/styles.js +143 -0
  13. package/es/TreeBrowser/v2/TreeNode/index.js +156 -0
  14. package/es/{index.js → TreeBrowser/v2/TreeNode/props.js} +3 -4
  15. package/es/TreeBrowser/v2/index.js +347 -0
  16. package/es/TreeBrowser/v2/props.js +35 -0
  17. package/es/TreeBrowser/v2/styles.js +71 -0
  18. package/es/exports/a.js +27 -0
  19. package/es/exports/b.js +27 -0
  20. package/lib/TreeBrowser/{TreeButton → v1/TreeButton}/index.js +3 -3
  21. package/lib/TreeBrowser/{TreeCollection → v1/TreeCollection}/index.js +1 -1
  22. package/lib/TreeBrowser/{TreeNode → v1/TreeNode}/index.js +3 -3
  23. package/lib/TreeBrowser/{index.js → v1/index.js} +3 -3
  24. package/lib/TreeBrowser/v2/TreeBrowserContext.js +44 -0
  25. package/lib/TreeBrowser/v2/TreeButton/index.js +208 -0
  26. package/lib/TreeBrowser/v2/TreeButton/props.js +31 -0
  27. package/lib/TreeBrowser/v2/TreeButton/styles.js +312 -0
  28. package/lib/TreeBrowser/v2/TreeCollection/index.js +372 -0
  29. package/lib/TreeBrowser/v2/TreeCollection/props.js +31 -0
  30. package/lib/TreeBrowser/v2/TreeCollection/styles.js +149 -0
  31. package/lib/TreeBrowser/v2/TreeNode/index.js +162 -0
  32. package/lib/TreeBrowser/v2/TreeNode/props.js +31 -0
  33. package/lib/TreeBrowser/v2/index.js +353 -0
  34. package/lib/TreeBrowser/v2/props.js +40 -0
  35. package/lib/TreeBrowser/v2/styles.js +77 -0
  36. package/lib/{index.js → exports/a.js} +5 -5
  37. package/lib/exports/b.js +33 -0
  38. package/package.json +40 -18
  39. package/src/TreeBrowser/{TreeButton → v1/TreeButton}/index.tsx +2 -2
  40. package/src/TreeBrowser/{TreeCollection → v1/TreeCollection}/index.tsx +1 -1
  41. package/src/TreeBrowser/{TreeNode → v1/TreeNode}/index.tsx +2 -2
  42. package/src/TreeBrowser/{index.tsx → v1/index.tsx} +1 -1
  43. package/src/TreeBrowser/v2/README.md +712 -0
  44. package/src/TreeBrowser/v2/TreeBrowserContext.ts +53 -0
  45. package/src/TreeBrowser/v2/TreeButton/index.tsx +210 -0
  46. package/src/TreeBrowser/v2/TreeButton/props.ts +95 -0
  47. package/src/TreeBrowser/v2/TreeButton/styles.ts +331 -0
  48. package/src/TreeBrowser/v2/TreeCollection/index.tsx +416 -0
  49. package/src/TreeBrowser/v2/TreeCollection/props.ts +88 -0
  50. package/src/TreeBrowser/v2/TreeCollection/styles.ts +157 -0
  51. package/src/TreeBrowser/v2/TreeNode/index.tsx +158 -0
  52. package/src/TreeBrowser/v2/TreeNode/props.ts +84 -0
  53. package/src/TreeBrowser/v2/index.tsx +438 -0
  54. package/src/TreeBrowser/v2/props.ts +245 -0
  55. package/src/TreeBrowser/v2/styles.ts +79 -0
  56. package/src/exports/a.ts +32 -0
  57. package/src/exports/b.ts +32 -0
  58. package/tsconfig.build.tsbuildinfo +1 -1
  59. package/types/TreeBrowser/v1/TreeBrowserContext.d.ts.map +1 -0
  60. package/types/TreeBrowser/v1/TreeButton/index.d.ts.map +1 -0
  61. package/types/TreeBrowser/v1/TreeButton/props.d.ts.map +1 -0
  62. package/types/TreeBrowser/v1/TreeButton/styles.d.ts.map +1 -0
  63. package/types/TreeBrowser/v1/TreeButton/theme.d.ts.map +1 -0
  64. package/types/TreeBrowser/{TreeCollection → v1/TreeCollection}/index.d.ts +1 -1
  65. package/types/TreeBrowser/v1/TreeCollection/index.d.ts.map +1 -0
  66. package/types/TreeBrowser/v1/TreeCollection/props.d.ts.map +1 -0
  67. package/types/TreeBrowser/v1/TreeCollection/styles.d.ts.map +1 -0
  68. package/types/TreeBrowser/v1/TreeCollection/theme.d.ts.map +1 -0
  69. package/types/TreeBrowser/v1/TreeNode/index.d.ts.map +1 -0
  70. package/types/TreeBrowser/v1/TreeNode/props.d.ts.map +1 -0
  71. package/types/TreeBrowser/v1/index.d.ts.map +1 -0
  72. package/types/TreeBrowser/v1/props.d.ts.map +1 -0
  73. package/types/TreeBrowser/v1/styles.d.ts.map +1 -0
  74. package/types/TreeBrowser/v1/theme.d.ts.map +1 -0
  75. package/types/TreeBrowser/v2/TreeBrowserContext.d.ts +24 -0
  76. package/types/TreeBrowser/v2/TreeBrowserContext.d.ts.map +1 -0
  77. package/types/TreeBrowser/v2/TreeButton/index.d.ts +44 -0
  78. package/types/TreeBrowser/v2/TreeButton/index.d.ts.map +1 -0
  79. package/types/TreeBrowser/v2/TreeButton/props.d.ts +37 -0
  80. package/types/TreeBrowser/v2/TreeButton/props.d.ts.map +1 -0
  81. package/types/TreeBrowser/v2/TreeButton/styles.d.ts +19 -0
  82. package/types/TreeBrowser/v2/TreeButton/styles.d.ts.map +1 -0
  83. package/types/TreeBrowser/v2/TreeCollection/index.d.ts +66 -0
  84. package/types/TreeBrowser/v2/TreeCollection/index.d.ts.map +1 -0
  85. package/types/TreeBrowser/v2/TreeCollection/props.d.ts +24 -0
  86. package/types/TreeBrowser/v2/TreeCollection/props.d.ts.map +1 -0
  87. package/types/TreeBrowser/v2/TreeCollection/styles.d.ts +18 -0
  88. package/types/TreeBrowser/v2/TreeCollection/styles.d.ts.map +1 -0
  89. package/types/TreeBrowser/v2/TreeNode/index.d.ts +41 -0
  90. package/types/TreeBrowser/v2/TreeNode/index.d.ts.map +1 -0
  91. package/types/TreeBrowser/v2/TreeNode/props.d.ts +25 -0
  92. package/types/TreeBrowser/v2/TreeNode/props.d.ts.map +1 -0
  93. package/types/TreeBrowser/v2/index.d.ts +70 -0
  94. package/types/TreeBrowser/v2/index.d.ts.map +1 -0
  95. package/types/TreeBrowser/v2/props.d.ts +163 -0
  96. package/types/TreeBrowser/v2/props.d.ts.map +1 -0
  97. package/types/TreeBrowser/v2/styles.d.ts +16 -0
  98. package/types/TreeBrowser/v2/styles.d.ts.map +1 -0
  99. package/types/exports/a.d.ts +9 -0
  100. package/types/exports/a.d.ts.map +1 -0
  101. package/types/exports/b.d.ts +9 -0
  102. package/types/exports/b.d.ts.map +1 -0
  103. package/src/index.ts +0 -32
  104. package/types/TreeBrowser/TreeBrowserContext.d.ts.map +0 -1
  105. package/types/TreeBrowser/TreeButton/index.d.ts.map +0 -1
  106. package/types/TreeBrowser/TreeButton/props.d.ts.map +0 -1
  107. package/types/TreeBrowser/TreeButton/styles.d.ts.map +0 -1
  108. package/types/TreeBrowser/TreeButton/theme.d.ts.map +0 -1
  109. package/types/TreeBrowser/TreeCollection/index.d.ts.map +0 -1
  110. package/types/TreeBrowser/TreeCollection/props.d.ts.map +0 -1
  111. package/types/TreeBrowser/TreeCollection/styles.d.ts.map +0 -1
  112. package/types/TreeBrowser/TreeCollection/theme.d.ts.map +0 -1
  113. package/types/TreeBrowser/TreeNode/index.d.ts.map +0 -1
  114. package/types/TreeBrowser/TreeNode/props.d.ts.map +0 -1
  115. package/types/TreeBrowser/index.d.ts.map +0 -1
  116. package/types/TreeBrowser/props.d.ts.map +0 -1
  117. package/types/TreeBrowser/styles.d.ts.map +0 -1
  118. package/types/TreeBrowser/theme.d.ts.map +0 -1
  119. package/types/index.d.ts +0 -9
  120. package/types/index.d.ts.map +0 -1
  121. /package/es/TreeBrowser/{TreeBrowserContext.js → v1/TreeBrowserContext.js} +0 -0
  122. /package/es/TreeBrowser/{TreeButton → v1/TreeButton}/props.js +0 -0
  123. /package/es/TreeBrowser/{TreeButton → v1/TreeButton}/styles.js +0 -0
  124. /package/es/TreeBrowser/{TreeButton → v1/TreeButton}/theme.js +0 -0
  125. /package/es/TreeBrowser/{TreeCollection → v1/TreeCollection}/props.js +0 -0
  126. /package/es/TreeBrowser/{TreeCollection → v1/TreeCollection}/styles.js +0 -0
  127. /package/es/TreeBrowser/{TreeCollection → v1/TreeCollection}/theme.js +0 -0
  128. /package/es/TreeBrowser/{TreeNode → v1/TreeNode}/props.js +0 -0
  129. /package/es/TreeBrowser/{props.js → v1/props.js} +0 -0
  130. /package/es/TreeBrowser/{styles.js → v1/styles.js} +0 -0
  131. /package/es/TreeBrowser/{theme.js → v1/theme.js} +0 -0
  132. /package/lib/TreeBrowser/{TreeBrowserContext.js → v1/TreeBrowserContext.js} +0 -0
  133. /package/lib/TreeBrowser/{TreeButton → v1/TreeButton}/props.js +0 -0
  134. /package/lib/TreeBrowser/{TreeButton → v1/TreeButton}/styles.js +0 -0
  135. /package/lib/TreeBrowser/{TreeButton → v1/TreeButton}/theme.js +0 -0
  136. /package/lib/TreeBrowser/{TreeCollection → v1/TreeCollection}/props.js +0 -0
  137. /package/lib/TreeBrowser/{TreeCollection → v1/TreeCollection}/styles.js +0 -0
  138. /package/lib/TreeBrowser/{TreeCollection → v1/TreeCollection}/theme.js +0 -0
  139. /package/lib/TreeBrowser/{TreeNode → v1/TreeNode}/props.js +0 -0
  140. /package/lib/TreeBrowser/{props.js → v1/props.js} +0 -0
  141. /package/lib/TreeBrowser/{styles.js → v1/styles.js} +0 -0
  142. /package/lib/TreeBrowser/{theme.js → v1/theme.js} +0 -0
  143. /package/src/TreeBrowser/{README.md → v1/README.md} +0 -0
  144. /package/src/TreeBrowser/{TreeBrowserContext.ts → v1/TreeBrowserContext.ts} +0 -0
  145. /package/src/TreeBrowser/{TreeButton → v1/TreeButton}/props.ts +0 -0
  146. /package/src/TreeBrowser/{TreeButton → v1/TreeButton}/styles.ts +0 -0
  147. /package/src/TreeBrowser/{TreeButton → v1/TreeButton}/theme.ts +0 -0
  148. /package/src/TreeBrowser/{TreeCollection → v1/TreeCollection}/props.ts +0 -0
  149. /package/src/TreeBrowser/{TreeCollection → v1/TreeCollection}/styles.ts +0 -0
  150. /package/src/TreeBrowser/{TreeCollection → v1/TreeCollection}/theme.ts +0 -0
  151. /package/src/TreeBrowser/{TreeNode → v1/TreeNode}/props.ts +0 -0
  152. /package/src/TreeBrowser/{props.ts → v1/props.ts} +0 -0
  153. /package/src/TreeBrowser/{styles.ts → v1/styles.ts} +0 -0
  154. /package/src/TreeBrowser/{theme.ts → v1/theme.ts} +0 -0
  155. /package/types/TreeBrowser/{TreeBrowserContext.d.ts → v1/TreeBrowserContext.d.ts} +0 -0
  156. /package/types/TreeBrowser/{TreeButton → v1/TreeButton}/index.d.ts +0 -0
  157. /package/types/TreeBrowser/{TreeButton → v1/TreeButton}/props.d.ts +0 -0
  158. /package/types/TreeBrowser/{TreeButton → v1/TreeButton}/styles.d.ts +0 -0
  159. /package/types/TreeBrowser/{TreeButton → v1/TreeButton}/theme.d.ts +0 -0
  160. /package/types/TreeBrowser/{TreeCollection → v1/TreeCollection}/props.d.ts +0 -0
  161. /package/types/TreeBrowser/{TreeCollection → v1/TreeCollection}/styles.d.ts +0 -0
  162. /package/types/TreeBrowser/{TreeCollection → v1/TreeCollection}/theme.d.ts +0 -0
  163. /package/types/TreeBrowser/{TreeNode → v1/TreeNode}/index.d.ts +0 -0
  164. /package/types/TreeBrowser/{TreeNode → v1/TreeNode}/props.d.ts +0 -0
  165. /package/types/TreeBrowser/{index.d.ts → v1/index.d.ts} +0 -0
  166. /package/types/TreeBrowser/{props.d.ts → v1/props.d.ts} +0 -0
  167. /package/types/TreeBrowser/{styles.d.ts → v1/styles.d.ts} +0 -0
  168. /package/types/TreeBrowser/{theme.d.ts → v1/theme.d.ts} +0 -0
@@ -0,0 +1,712 @@
1
+ ---
2
+ describes: TreeBrowser
3
+ ---
4
+
5
+ The `<TreeBrowser/>` component provides a keyboard accessible tree structure. The component expects
6
+ to receive a normalized data structure, examples can be seen at https://github.com/paularmstrong/normalizr.
7
+
8
+ ### Size
9
+
10
+ ```js
11
+ ---
12
+ type: example
13
+ ---
14
+ const Example = () => {
15
+ const [size, setSize] = useState('medium')
16
+ const sizes = ['small', 'medium', 'large']
17
+
18
+ const handleSizeSelect = (e, size) => {
19
+ setSize(size)
20
+ }
21
+
22
+ return (
23
+ <>
24
+ <View display="block" margin="none none medium">
25
+ <RadioInputGroup
26
+ name="treeBrowserSize"
27
+ defaultValue="medium"
28
+ description={
29
+ <ScreenReaderContent>
30
+ TreeBrowser size selector
31
+ </ScreenReaderContent>
32
+ }
33
+ variant="toggle"
34
+ onChange={handleSizeSelect}
35
+ >
36
+ {sizes.map((size) => (
37
+ <RadioInput key={size} label={size} value={size} />
38
+ ))}
39
+ </RadioInputGroup>
40
+ </View>
41
+
42
+ <TreeBrowser
43
+ size={size}
44
+ collections={{
45
+ 1: {
46
+ id: 1,
47
+ name: 'Assignments',
48
+ collections: [2, 3],
49
+ items: [3],
50
+ descriptor: 'Class Assignments'
51
+ },
52
+ 2: {
53
+ id: 2,
54
+ name: 'English Assignments',
55
+ collections: [4],
56
+ items: []
57
+ },
58
+ 3: {
59
+ id: 3,
60
+ name: 'Math Assignments',
61
+ collections: [5],
62
+ items: [1, 2]
63
+ },
64
+ 4: {
65
+ id: 4,
66
+ name: 'Reading Assignments',
67
+ collections: [],
68
+ items: [4]
69
+ },
70
+ 5: { id: 5, name: 'Advanced Math Assignments', items: [5] }
71
+ }}
72
+ items={{
73
+ 1: { id: 1, name: 'Addition Worksheet' },
74
+ 2: { id: 2, name: 'Subtraction Worksheet' },
75
+ 3: { id: 3, name: 'General Questions' },
76
+ 4: { id: 4, name: 'Vogon Poetry' },
77
+ 5: {
78
+ id: 5,
79
+ name: 'Bistromath',
80
+ descriptor: 'Explain the Bistromathic Drive'
81
+ }
82
+ }}
83
+ defaultExpanded={[1, 3]}
84
+ rootId={1}
85
+ />
86
+ </>
87
+ )
88
+ }
89
+
90
+ render(<Example />)
91
+ ```
92
+
93
+ ### Managing State
94
+
95
+ `<TreeBrowser />` can be fully controlled. The following example uses the `onCollectionToggle` callback function to set the state. It then uses the `expanded` prop to configure which collections are open or closed.
96
+
97
+ ```js
98
+ ---
99
+ type: example
100
+ ---
101
+ const Example = () => {
102
+ const [expanded, setExpanded] = useState([2])
103
+
104
+ const handleCollectionClick = (id, collection) => {
105
+ console.log(collection.id)
106
+ }
107
+
108
+ const handleCollectionToggle = (collection) => {
109
+ setExpanded((prevExpanded) => {
110
+ const newExpanded = [...prevExpanded]
111
+ const index = newExpanded.indexOf(collection.id)
112
+
113
+ if (!collection.expanded) {
114
+ newExpanded.splice(index, 1)
115
+ } else if (index < 0) {
116
+ newExpanded.push(collection.id)
117
+ }
118
+
119
+ return newExpanded
120
+ })
121
+ }
122
+
123
+ return (
124
+ <TreeBrowser
125
+ variant="indent"
126
+ selectionType="single"
127
+ collections={{
128
+ 1: { id: 1, name: 'Grade 1', collections: [2, 3, 6] },
129
+ 2: {
130
+ id: 2,
131
+ name: 'Math Outcomes',
132
+ collections: [4],
133
+ items: [3, 4],
134
+ descriptor: '1 Group | 2 Outcomes'
135
+ },
136
+ 3: {
137
+ id: 3,
138
+ name: 'Reading Outcome',
139
+ collections: [5],
140
+ items: [1, 2],
141
+ descriptor: '1 Group | 2 Outcomes'
142
+ },
143
+ 4: {
144
+ id: 4,
145
+ name: 'Advanced Math',
146
+ items: [6],
147
+ descriptor: '1 Outcome'
148
+ },
149
+ 5: {
150
+ id: 5,
151
+ name: 'Advanced Reading',
152
+ items: [5],
153
+ descriptor: '1 Group | 2 Outcomes'
154
+ },
155
+ 6: {
156
+ id: 6,
157
+ name: 'Advanced Outcomes',
158
+ items: [5, 6],
159
+ descriptor: '2 Outcomes'
160
+ }
161
+ }}
162
+ items={{
163
+ 1: { id: 1, name: 'Can read' },
164
+ 2: { id: 2, name: 'Can write' },
165
+ 3: { id: 3, name: 'Can add' },
166
+ 4: { id: 4, name: 'Can subtract' },
167
+ 5: { id: 5, name: 'Can read Shakespeare' },
168
+ 6: { id: 6, name: 'Can do quantum physics' }
169
+ }}
170
+ showRootCollection={false}
171
+ rootId={1}
172
+ expanded={expanded}
173
+ onCollectionToggle={handleCollectionToggle}
174
+ onCollectionClick={handleCollectionClick}
175
+ />
176
+ )
177
+ }
178
+
179
+ render(<Example />)
180
+ ```
181
+
182
+ ### Customizing Icons
183
+
184
+ All of the `<TreeBrowser>` icons are customizable.
185
+ The following example sets custom icons for the expanded and collapsed state of the collections via `collectionIcon` and `collectionIconExpanded` and custom item icons via `itemIcon`.
186
+
187
+ ```js
188
+ ---
189
+ type: example
190
+ ---
191
+ <TreeBrowser
192
+ collections={{
193
+ 1: {
194
+ id: 1,
195
+ name: "Grades",
196
+ collections: [],
197
+ items: [1,2,3]
198
+ },
199
+ }}
200
+ items={{
201
+ 1: { id: 1, name: "Sarah" },
202
+ 2: { id: 2, name: "Jenny" },
203
+ 3: { id: 3, name: "Juan" }
204
+ }}
205
+ defaultExpanded={[1]}
206
+
207
+ collectionIcon = {<BookCheckInstUIIcon />}
208
+ collectionIconExpanded = {XInstUIIcon}
209
+ itemIcon={() => <UserInstUIIcon />}
210
+
211
+ rootId={1}
212
+ size="large"
213
+ />
214
+ ```
215
+
216
+ #### Different icons for each item
217
+
218
+ One way do this is to use `getItemProps`. This function is called with the props for each item and returns new props you specify. These props are then passed to the item when it is rendered. In the following example, we override the `itemIcon` prop depending on the item name.
219
+
220
+ ```js
221
+ ---
222
+ type: example
223
+ ---
224
+ <TreeBrowser
225
+ collections={{
226
+ 1: {
227
+ id: 1,
228
+ name: "Saved",
229
+ collections: [],
230
+ items: [1,2,3]
231
+ },
232
+ }}
233
+ items={{
234
+ 1: { id: 1, name: "Modules" },
235
+ 2: { id: 2, name: "Videos" },
236
+ 3: { id: 3, name: "Students" }
237
+ }}
238
+ defaultExpanded={[1]}
239
+ rootId={1}
240
+ size="large"
241
+ getItemProps={({ name, ...props }) => {
242
+ let itemIcon = <UserInstUIIcon />
243
+
244
+ if (name === 'Modules') {
245
+ itemIcon = <BoxesInstUIIcon />
246
+ }
247
+
248
+ if (name === 'Videos') {
249
+ itemIcon = <PlaySquareInstUIIcon />
250
+ }
251
+
252
+ return {
253
+ ...props, // Be sure to pass the rest of the props along
254
+ itemIcon,
255
+ name
256
+ }
257
+ }}
258
+ />
259
+ ```
260
+
261
+ Another way to do it is to specify the `thumbnail` property in the `items` collection. This also overrides `itemIcon`.
262
+
263
+ ```js
264
+ ---
265
+ type: example
266
+ ---
267
+ <TreeBrowser
268
+ collections={{
269
+ 1: {
270
+ id: 1,
271
+ name: "Pandas",
272
+ collections: [],
273
+ items: [1,2,3]
274
+ },
275
+ }}
276
+ items={{
277
+ 1: { id: 1, name: "Bao Bao", thumbnail: avatarSquare },
278
+ 2: { id: 2, name: "Bei Bei" },
279
+ 3: { id: 3, name: "Mei Xiang", thumbnail: avatarPortrait }
280
+ }}
281
+ defaultExpanded={[1]}
282
+ itemIcon={<UserInstUIIcon />}
283
+ rootId={1}
284
+ size="large"
285
+ />
286
+ ```
287
+
288
+ ### Rendering custom content in nodes
289
+
290
+ Using the `renderContent` property allows you to render custom content in the area where the text and descriptor are.
291
+ To meet a11y standards make sure to have the right contrast ratio on hovered and selected states; you can use the
292
+ `selected` and `focused` attributes in the given `props`.
293
+
294
+ ```js
295
+ ---
296
+ type: example
297
+ ---
298
+ <TreeBrowser
299
+ collections={{
300
+ 1: {
301
+ id: 1,
302
+ name: "Pandas",
303
+ collections: [],
304
+ items: [1, 2, 3]
305
+ },
306
+ }}
307
+ items={{
308
+ 1: { id: 1, name: "Bao Bao" },
309
+ 2: { id: 2, name: "Bei Bei" },
310
+ 3: { id: 3, name: "Mei Xiang" }
311
+ }}
312
+ defaultExpanded={[1]}
313
+ itemIcon={<UserInstUIIcon />}
314
+ rootId={1}
315
+ size="large"
316
+ renderContent={(props)=> {
317
+ if (props.level > 1) {
318
+ return <div style={{ display: 'flex', alignItems: 'flex-end', padding: '0.6rem 0 0.6rem 1rem', color: 'darkorange' }}>
319
+ <span>{props.name}</span>
320
+ <Tag text="done" size="small" margin="0 xx-small 0 xx-small"/>
321
+ <Tag text="class A" size="small"/>
322
+ </div>
323
+ }
324
+ return <div style={{padding: '0.6rem 0 0.6rem 1rem', color: 'darkorange'}}>{props.name}</div>
325
+ }}
326
+ />
327
+ ```
328
+
329
+ ### Rendering custom items before and after nodes
330
+
331
+ An example of a `<TreeBrowser />` with a custom item after each collection.
332
+
333
+ When rendering custom interactive content (such as input fields), use the `hoverable` prop on `<TreeBrowser.Node />` to control the default hover behavior. Set it to `false` to disable hover effects while custom interactive elements are shown.
334
+
335
+ This example uses controlled selection (the `selection` prop with the `onSelectionChange` callback) to manage the selected state. Clicking the custom node clears the current selection by calling `setSelection('')`, ensuring a clean and predictable user experience.
336
+
337
+ ```js
338
+ ---
339
+ type: example
340
+ ---
341
+ const Example = () => {
342
+ const [expanded, setExpanded] = useState(true)
343
+ const [hoveredLine, setHoveredLine] = useState(null)
344
+ const [selection, setSelection] = useState('')
345
+ const nodeRef = useRef(null)
346
+
347
+ const handleExpandToggle = useCallback((e, newExpandedState) => {
348
+ e.stopPropagation()
349
+ setExpanded(newExpandedState)
350
+ setSelection('')
351
+ nodeRef.current?.focus()
352
+ }, [])
353
+
354
+ const handleKeyPress = useCallback(
355
+ (e, newExpandedState) => {
356
+ if (e.code === 'Space' || e.code === 'Enter') {
357
+ e.preventDefault()
358
+ handleExpandToggle(e, newExpandedState)
359
+ }
360
+ },
361
+ [handleExpandToggle]
362
+ )
363
+
364
+ const renderInput = () => {
365
+ if (expanded) {
366
+ return (
367
+ <View
368
+ as="div"
369
+ padding="xx-small"
370
+ onFocus={(e) => e.stopPropagation()}
371
+ onClick={(e) => {
372
+ e.stopPropagation()
373
+ setSelection('')
374
+ }}
375
+ onMouseEnter={() => setHoveredLine('renderAfter')}
376
+ onMouseLeave={() => setHoveredLine(null)}
377
+ >
378
+ <TextInput
379
+ placeholder="Enter new group name"
380
+ display="inline-block"
381
+ width="12rem"
382
+ renderLabel=""
383
+ onKeyDown={(e) => e.stopPropagation()}
384
+ onFocus={() => setSelection('')}
385
+ />
386
+ <IconButton
387
+ screenReaderLabel="Cancel"
388
+ onClick={(e) => handleExpandToggle(e, false)}
389
+ onKeyDown={(e) => handleKeyPress(e, false)}
390
+ margin="0 0 0 small"
391
+ >
392
+ <XInstUIIcon />
393
+ </IconButton>
394
+ <IconButton
395
+ screenReaderLabel="Add new group"
396
+ onClick={(e) => handleExpandToggle(e, false)}
397
+ onKeyDown={(e) => handleKeyPress(e, false)}
398
+ margin="0 0 0 small"
399
+ >
400
+ <CheckInstUIIcon />
401
+ </IconButton>
402
+ </View>
403
+ )
404
+ }
405
+
406
+ return <View as="div">Create New Group</View>
407
+ }
408
+
409
+ const renderNode = () => (
410
+ <TreeBrowser.Node
411
+ containerRef={(el) => (nodeRef.current = el)}
412
+ onClick={(e) => handleExpandToggle(e, !expanded)}
413
+ onKeyDown={(e) => handleKeyPress(e, !expanded)}
414
+ itemIcon={expanded ? '' : <PlusInstUIIcon />}
415
+ size="large"
416
+ hoverable={!expanded}
417
+ >
418
+ {renderInput()}
419
+ </TreeBrowser.Node>
420
+ )
421
+
422
+ return (
423
+ <TreeBrowser
424
+ selectionType="single"
425
+ selection={selection}
426
+ onSelectionChange={(newSelection) => setSelection(newSelection)}
427
+ size="large"
428
+ defaultExpanded={[1, 2]}
429
+ collections={{
430
+ 1: {
431
+ id: 1,
432
+ name: 'Grade 1',
433
+ collections: [2]
434
+ },
435
+ 2: {
436
+ id: 2,
437
+ name: 'Math Outcomes',
438
+ collections: [],
439
+ items: [1, 2],
440
+ descriptor: '1 Group | 2 Outcomes',
441
+ renderAfterItems: renderNode()
442
+ }
443
+ }}
444
+ items={{
445
+ 1: { id: 1, name: 'Can add' },
446
+ 2: { id: 2, name: 'Can subtract' }
447
+ }}
448
+ showRootCollection={true}
449
+ rootId={1}
450
+ />
451
+ )
452
+ }
453
+
454
+ render(<Example />)
455
+ ```
456
+
457
+ ### Change the order of appearance of items and collections
458
+
459
+ By default, the order of collections and items depend on the order of `collections` and `items` array. We can override it by providing a `sortOrder` comparison function.
460
+
461
+ ---
462
+
463
+ **NOTE**
464
+
465
+ This works with all collections and items of the TreeBrowser.
466
+
467
+ ---
468
+
469
+ ```js
470
+ ---
471
+ type: example
472
+ ---
473
+ const Example = () => {
474
+ const [size, setSize] = useState('medium')
475
+ const [sorted, setSorted] = useState(false)
476
+
477
+ const toggleSort = () => {
478
+ setSorted(!sorted)
479
+ }
480
+
481
+ return (
482
+ <>
483
+ <View display="block" margin="none none medium">
484
+ <FormFieldGroup description="Turn on/off sorting">
485
+ <Checkbox checked={sorted} label="Sort" onChange={toggleSort} />
486
+ </FormFieldGroup>
487
+ </View>
488
+
489
+ <TreeBrowser
490
+ size={size}
491
+ collections={{
492
+ 1: {
493
+ id: 1,
494
+ name: 'Assignments',
495
+ collections: [3, 2],
496
+ items: [3],
497
+ descriptor: 'Class Assignments'
498
+ },
499
+ 2: {
500
+ id: 2,
501
+ name: 'English Assignments',
502
+ collections: [4],
503
+ items: []
504
+ },
505
+ 3: {
506
+ id: 3,
507
+ name: 'Math Assignments',
508
+ collections: [5],
509
+ items: [2, 1]
510
+ },
511
+ 4: {
512
+ id: 4,
513
+ name: 'Reading Assignments',
514
+ collections: [],
515
+ items: [4]
516
+ },
517
+ 5: { id: 5, name: 'Advanced Math Assignments', items: [5] }
518
+ }}
519
+ items={{
520
+ 1: { id: 1, name: 'Addition Worksheet' },
521
+ 2: { id: 2, name: 'Subtraction Worksheet' },
522
+ 3: { id: 3, name: 'General Questions' },
523
+ 4: { id: 4, name: 'Vogon Poetry' },
524
+ 5: {
525
+ id: 5,
526
+ name: 'Bistromath',
527
+ descriptor: 'Explain the Bistromathic Drive'
528
+ }
529
+ }}
530
+ defaultExpanded={[1, 3]}
531
+ rootId={1}
532
+ sortOrder={sorted ? (a, b) => a.name.localeCompare(b.name) : () => 0}
533
+ />
534
+ </>
535
+ )
536
+ }
537
+
538
+ render(<Example />)
539
+ ```
540
+
541
+ There is another way to sort the children of one collection. By adding the `compareFunc` as the comparison function to the collection's properties. This will be effective only within the collection's scope. For more convenience, we support a prop called `type` to specify whether the collection's children is either an item or a subcollection (this is only make sense in `compareFunc`)
542
+
543
+ ```js
544
+ ---
545
+ type: example
546
+ ---
547
+ const Example = () => {
548
+ const [size, setSize] = useState('medium')
549
+
550
+ return (
551
+ <TreeBrowser
552
+ size={size}
553
+ collections={{
554
+ 1: {
555
+ id: 1,
556
+ name: 'Assignments',
557
+ collections: [3, 2],
558
+ items: [3],
559
+ descriptor: 'Class Assignments',
560
+ // Sort the direct children of "Assignment" by their name in alphabetical order
561
+ compareFunc: (a, b) => a.name.localeCompare(b.name)
562
+ },
563
+ 2: {
564
+ id: 2,
565
+ name: 'English Assignments',
566
+ collections: [4],
567
+ items: []
568
+ },
569
+ 3: {
570
+ id: 3,
571
+ name: 'Math Assignments',
572
+ collections: [5],
573
+ items: [2, 1],
574
+ // The items appear before subcollections
575
+ compareFunc: (a, b) => {
576
+ if (a.type === 'item' && b.type === 'collection') {
577
+ return -1
578
+ }
579
+ if (a.type === 'collection' && b.type === 'item') {
580
+ return 1
581
+ }
582
+ return 0
583
+ }
584
+ },
585
+ 4: {
586
+ id: 4,
587
+ name: 'Reading Assignments',
588
+ collections: [],
589
+ items: [4]
590
+ },
591
+ 5: { id: 5, name: 'Advanced Math Assignments', items: [5] }
592
+ }}
593
+ items={{
594
+ 1: { id: 1, name: 'Addition Worksheet' },
595
+ 2: { id: 2, name: 'Subtraction Worksheet' },
596
+ 3: { id: 3, name: 'General Questions' },
597
+ 4: { id: 4, name: 'Vogon Poetry' },
598
+ 5: {
599
+ id: 5,
600
+ name: 'Bistromath',
601
+ descriptor: 'Explain the Bistromathic Drive'
602
+ }
603
+ }}
604
+ defaultExpanded={[1, 3]}
605
+ rootId={1}
606
+ />
607
+ )
608
+ }
609
+
610
+ render(<Example />)
611
+ ```
612
+
613
+ ### showRootCollection
614
+
615
+ The `showRootCollection` prop sets whether the root collection (specified in `rootId` prop) is displayed or to begin with its immediate sub-collections and items instead. It defaults to `true`.
616
+
617
+ ```js
618
+ ---
619
+ type: example
620
+ ---
621
+ const Example = () => {
622
+ const [showRootCollection, setShowRootCollection] = useState(true)
623
+
624
+ const handleSwitch = () => {
625
+ setShowRootCollection(!showRootCollection)
626
+ }
627
+
628
+ const renderNode = () => {
629
+ return (
630
+ <TreeBrowser.Node itemIcon={<PlusInstUIIcon />}>More</TreeBrowser.Node>
631
+ )
632
+ }
633
+
634
+ return (
635
+ <>
636
+ <View display="block" margin="none none medium">
637
+ <Checkbox
638
+ label="showRootCollection"
639
+ variant="toggle"
640
+ size="medium"
641
+ checked={showRootCollection}
642
+ onChange={handleSwitch}
643
+ />
644
+ </View>
645
+
646
+ <TreeBrowser
647
+ collections={{
648
+ 1: {
649
+ id: 1,
650
+ name: 'Assignments',
651
+ collections: [2, 3],
652
+ items: [3, 5],
653
+ descriptor: 'Class Assignments',
654
+ renderAfterItems: renderNode()
655
+ },
656
+ 2: {
657
+ id: 2,
658
+ name: 'English Assignments',
659
+ collections: [4],
660
+ items: []
661
+ },
662
+ 3: {
663
+ id: 3,
664
+ name: 'Math Assignments',
665
+ collections: [5],
666
+ items: [1, 2]
667
+ },
668
+ 4: {
669
+ id: 4,
670
+ name: 'Reading Assignments',
671
+ collections: [],
672
+ items: [4]
673
+ },
674
+ 5: { id: 5, name: 'Advanced Math Assignments', items: [5] }
675
+ }}
676
+ items={{
677
+ 1: { id: 1, name: 'Addition Worksheet' },
678
+ 2: { id: 2, name: 'Subtraction Worksheet' },
679
+ 3: { id: 3, name: 'General Questions' },
680
+ 4: { id: 4, name: 'Vogon Poetry' },
681
+ 5: {
682
+ id: 5,
683
+ name: 'Bistromath',
684
+ descriptor: 'Explain the Bistromathic Drive'
685
+ }
686
+ }}
687
+ defaultExpanded={[1, 3]}
688
+ rootId={1}
689
+ showRootCollection={showRootCollection}
690
+ />
691
+ </>
692
+ )
693
+ }
694
+
695
+ render(<Example />)
696
+ ```
697
+
698
+ ### Guidelines
699
+
700
+ ```js
701
+ ---
702
+ type: embed
703
+ ---
704
+ <Guidelines>
705
+ <Figure recommendation="yes" title="Do">
706
+ <Figure.Item>Use for browsing hierarchical content</Figure.Item>
707
+ </Figure>
708
+ <Figure recommendation="no" title="Don't">
709
+ <Figure.Item>Use for site navigation</Figure.Item>
710
+ </Figure>
711
+ </Guidelines>
712
+ ```