@instructure/ui-tree-browser 10.16.1 → 10.16.3
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.
- package/CHANGELOG.md +16 -0
- package/es/TreeBrowser/TreeButton/__new-tests__/TreeButton.test.js +165 -0
- package/es/TreeBrowser/TreeCollection/__new-tests__/TreeCollection.test.js +454 -0
- package/es/TreeBrowser/{TreeBrowserLocator.js → TreeNode/__new-tests__/TreeNode.test.js} +31 -14
- package/es/TreeBrowser/__new-tests__/TreeBrowser.test.js +525 -0
- package/lib/TreeBrowser/TreeButton/__new-tests__/TreeButton.test.js +166 -0
- package/lib/TreeBrowser/TreeCollection/__new-tests__/TreeCollection.test.js +457 -0
- package/lib/TreeBrowser/TreeNode/__new-tests__/TreeNode.test.js +56 -0
- package/lib/TreeBrowser/__new-tests__/TreeBrowser.test.js +527 -0
- package/package.json +17 -14
- package/src/TreeBrowser/TreeButton/__new-tests__/TreeButton.test.tsx +162 -0
- package/src/TreeBrowser/TreeCollection/__new-tests__/TreeCollection.test.tsx +423 -0
- package/src/TreeBrowser/{TreeBrowserLocator.ts → TreeNode/__new-tests__/TreeNode.test.tsx} +30 -13
- package/src/TreeBrowser/__new-tests__/TreeBrowser.test.tsx +575 -0
- package/tsconfig.build.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/TreeBrowser/TreeButton/__new-tests__/TreeButton.test.d.ts +2 -0
- package/types/TreeBrowser/TreeButton/__new-tests__/TreeButton.test.d.ts.map +1 -0
- package/types/TreeBrowser/TreeCollection/__new-tests__/TreeCollection.test.d.ts +2 -0
- package/types/TreeBrowser/TreeCollection/__new-tests__/TreeCollection.test.d.ts.map +1 -0
- package/types/TreeBrowser/TreeNode/__new-tests__/TreeNode.test.d.ts +2 -0
- package/types/TreeBrowser/TreeNode/__new-tests__/TreeNode.test.d.ts.map +1 -0
- package/types/TreeBrowser/__new-tests__/TreeBrowser.test.d.ts +2 -0
- package/types/TreeBrowser/__new-tests__/TreeBrowser.test.d.ts.map +1 -0
- package/es/TreeBrowser/locator.js +0 -26
- package/lib/TreeBrowser/TreeBrowserLocator.js +0 -44
- package/lib/TreeBrowser/locator.js +0 -37
- package/src/TreeBrowser/locator.ts +0 -27
- package/types/TreeBrowser/TreeBrowserLocator.d.ts +0 -1065
- package/types/TreeBrowser/TreeBrowserLocator.d.ts.map +0 -1
- package/types/TreeBrowser/locator.d.ts +0 -4
- package/types/TreeBrowser/locator.d.ts.map +0 -1
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The MIT License (MIT)
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2015 - present Instructure, Inc.
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { render, screen, waitFor } from '@testing-library/react'
|
|
26
|
+
import userEvent from '@testing-library/user-event'
|
|
27
|
+
import { vi } from 'vitest'
|
|
28
|
+
import type { MockInstance } from 'vitest'
|
|
29
|
+
import { runAxeCheck } from '@instructure/ui-axe-check'
|
|
30
|
+
|
|
31
|
+
import '@testing-library/jest-dom'
|
|
32
|
+
import { TreeBrowser } from '../index'
|
|
33
|
+
import { TreeNode } from '../TreeNode'
|
|
34
|
+
|
|
35
|
+
const COLLECTIONS_DATA = {
|
|
36
|
+
2: { id: 2, name: 'Root Directory', collections: [3, 4], items: [1] },
|
|
37
|
+
3: { id: 3, name: 'Sub Root 1', collections: [5] },
|
|
38
|
+
4: { id: 4, name: 'Sub Root 2' },
|
|
39
|
+
5: { id: 5, name: 'Nested Sub Collection' }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const COLLECTIONS_DATA_WITH_ZERO = {
|
|
43
|
+
0: { id: 0, name: 'Root Directory', collections: [3, 4], items: [1] },
|
|
44
|
+
3: { id: 3, name: 'Sub Root 1', collections: [5] },
|
|
45
|
+
4: { id: 4, name: 'Sub Root 2' },
|
|
46
|
+
5: { id: 5, name: 'Nested Sub Collection' }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const COLLECTIONS_DATA_WITH_STRING_IDS = {
|
|
50
|
+
'2': { id: '2', name: 'Root Directory', collections: ['3', '4'], items: [1] },
|
|
51
|
+
'3': { id: '3', name: 'Sub Root 1', collections: ['5'] },
|
|
52
|
+
'4': { id: '4', name: 'Sub Root 2' },
|
|
53
|
+
'5': { id: '5', name: 'Nested Sub Collection' }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const ITEMS_DATA = {
|
|
57
|
+
1: { id: 1, name: 'Item 1' }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe('<TreeBrowser />', () => {
|
|
61
|
+
let consoleWarningMock: ReturnType<typeof vi.spyOn>
|
|
62
|
+
let consoleErrorMock: ReturnType<typeof vi.spyOn>
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
// Mocking console to prevent test output pollution and expect for messages
|
|
66
|
+
consoleWarningMock = vi
|
|
67
|
+
.spyOn(console, 'warn')
|
|
68
|
+
.mockImplementation(() => {}) as MockInstance
|
|
69
|
+
|
|
70
|
+
consoleErrorMock = vi
|
|
71
|
+
.spyOn(console, 'error')
|
|
72
|
+
.mockImplementation(() => {}) as MockInstance
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
consoleWarningMock.mockRestore()
|
|
77
|
+
consoleErrorMock.mockRestore()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should render a tree', async () => {
|
|
81
|
+
const { container } = render(
|
|
82
|
+
<TreeBrowser
|
|
83
|
+
collections={COLLECTIONS_DATA}
|
|
84
|
+
items={ITEMS_DATA}
|
|
85
|
+
rootId={2}
|
|
86
|
+
/>
|
|
87
|
+
)
|
|
88
|
+
const tree = container.querySelector('[class$="-treeBrowser"]')
|
|
89
|
+
|
|
90
|
+
expect(tree).toBeInTheDocument()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should render subcollections', async () => {
|
|
94
|
+
render(
|
|
95
|
+
<TreeBrowser
|
|
96
|
+
collections={COLLECTIONS_DATA}
|
|
97
|
+
items={ITEMS_DATA}
|
|
98
|
+
rootId={2}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
const items = screen.getAllByRole('treeitem')
|
|
102
|
+
|
|
103
|
+
expect(items.length).toEqual(1)
|
|
104
|
+
|
|
105
|
+
await userEvent.click(items[0])
|
|
106
|
+
|
|
107
|
+
await waitFor(() => {
|
|
108
|
+
const itemsAfterClick = screen.getAllByRole('treeitem')
|
|
109
|
+
expect(itemsAfterClick.length).toEqual(4)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should render all collections at top level if showRootCollection is true and rootId is undefined', async () => {
|
|
114
|
+
render(
|
|
115
|
+
<TreeBrowser
|
|
116
|
+
collections={COLLECTIONS_DATA}
|
|
117
|
+
items={ITEMS_DATA}
|
|
118
|
+
rootId={undefined}
|
|
119
|
+
/>
|
|
120
|
+
)
|
|
121
|
+
const items = screen.getAllByRole('treeitem')
|
|
122
|
+
|
|
123
|
+
expect(items.length).toEqual(4)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
describe('expanded', () => {
|
|
127
|
+
it('should not expand collections or items without defaultExpanded prop', async () => {
|
|
128
|
+
render(
|
|
129
|
+
<TreeBrowser
|
|
130
|
+
collections={COLLECTIONS_DATA}
|
|
131
|
+
items={ITEMS_DATA}
|
|
132
|
+
rootId={2}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
const items = screen.getAllByRole('treeitem')
|
|
136
|
+
|
|
137
|
+
expect(items.length).toEqual(1)
|
|
138
|
+
expect(items[0]).toHaveTextContent('Root Directory')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should accept an array of default expanded collections', async () => {
|
|
142
|
+
render(
|
|
143
|
+
<TreeBrowser
|
|
144
|
+
collections={COLLECTIONS_DATA}
|
|
145
|
+
items={ITEMS_DATA}
|
|
146
|
+
rootId={2}
|
|
147
|
+
defaultExpanded={[2, 3]}
|
|
148
|
+
/>
|
|
149
|
+
)
|
|
150
|
+
const items = screen.getAllByRole('treeitem')
|
|
151
|
+
const subRoot2 = screen.getByLabelText('Sub Root 2')
|
|
152
|
+
const nestedSub = screen.getByLabelText('Nested Sub Collection')
|
|
153
|
+
|
|
154
|
+
expect(items.length).toEqual(5)
|
|
155
|
+
|
|
156
|
+
expect(subRoot2).toHaveAttribute('aria-label', 'Sub Root 2')
|
|
157
|
+
expect(subRoot2).toHaveTextContent('Sub Root 2')
|
|
158
|
+
|
|
159
|
+
expect(nestedSub).toHaveAttribute('aria-label', 'Nested Sub Collection')
|
|
160
|
+
expect(nestedSub).toHaveTextContent('Nested Sub Collection')
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
describe('selected', () => {
|
|
165
|
+
it('should not show the selection if selectionType is none', async () => {
|
|
166
|
+
render(
|
|
167
|
+
<TreeBrowser
|
|
168
|
+
collections={COLLECTIONS_DATA}
|
|
169
|
+
items={ITEMS_DATA}
|
|
170
|
+
rootId={2}
|
|
171
|
+
/>
|
|
172
|
+
)
|
|
173
|
+
const item = screen.getByRole('treeitem')
|
|
174
|
+
|
|
175
|
+
await userEvent.click(item)
|
|
176
|
+
|
|
177
|
+
await waitFor(() => {
|
|
178
|
+
expect(item).not.toHaveAttribute('aria-selected')
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should show the selection indicator on last clicked collection or item', async () => {
|
|
183
|
+
render(
|
|
184
|
+
<TreeBrowser
|
|
185
|
+
collections={COLLECTIONS_DATA}
|
|
186
|
+
items={ITEMS_DATA}
|
|
187
|
+
rootId={2}
|
|
188
|
+
selectionType="single"
|
|
189
|
+
/>
|
|
190
|
+
)
|
|
191
|
+
const item = screen.getByLabelText('Root Directory')
|
|
192
|
+
|
|
193
|
+
await userEvent.click(item)
|
|
194
|
+
|
|
195
|
+
await waitFor(() => {
|
|
196
|
+
expect(item).toHaveAttribute('aria-selected')
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
const nestedItem = screen.getByLabelText('Item 1')
|
|
200
|
+
|
|
201
|
+
await userEvent.click(nestedItem)
|
|
202
|
+
|
|
203
|
+
await waitFor(() => {
|
|
204
|
+
expect(nestedItem).toHaveAttribute('aria-selected')
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('collections', () => {
|
|
210
|
+
it('should render collections with string-keyed ids', async () => {
|
|
211
|
+
render(
|
|
212
|
+
<TreeBrowser
|
|
213
|
+
collections={COLLECTIONS_DATA_WITH_STRING_IDS}
|
|
214
|
+
items={ITEMS_DATA}
|
|
215
|
+
rootId={'2'}
|
|
216
|
+
showRootCollection={true}
|
|
217
|
+
/>
|
|
218
|
+
)
|
|
219
|
+
const item = screen.getByLabelText('Root Directory')
|
|
220
|
+
|
|
221
|
+
expect(item).toBeInTheDocument()
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('should not show the first keyed collection if showRootCollection is false', async () => {
|
|
225
|
+
render(
|
|
226
|
+
<TreeBrowser
|
|
227
|
+
collections={COLLECTIONS_DATA}
|
|
228
|
+
items={ITEMS_DATA}
|
|
229
|
+
rootId={2}
|
|
230
|
+
showRootCollection={false}
|
|
231
|
+
/>
|
|
232
|
+
)
|
|
233
|
+
const items = screen.getAllByRole('treeitem')
|
|
234
|
+
|
|
235
|
+
expect(items.length).toEqual(3)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it('should render first keyed collection if showRootCollection is true and rootId specified', async () => {
|
|
239
|
+
render(
|
|
240
|
+
<TreeBrowser
|
|
241
|
+
collections={COLLECTIONS_DATA}
|
|
242
|
+
items={ITEMS_DATA}
|
|
243
|
+
rootId={2}
|
|
244
|
+
/>
|
|
245
|
+
)
|
|
246
|
+
const item = screen.getByLabelText('Root Directory')
|
|
247
|
+
|
|
248
|
+
expect(item).toBeInTheDocument()
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('should not show the first keyed collection if showRootCollection is false and rootId is 0', async () => {
|
|
252
|
+
render(
|
|
253
|
+
<TreeBrowser
|
|
254
|
+
collections={COLLECTIONS_DATA_WITH_ZERO}
|
|
255
|
+
items={ITEMS_DATA}
|
|
256
|
+
rootId={0}
|
|
257
|
+
showRootCollection={false}
|
|
258
|
+
/>
|
|
259
|
+
)
|
|
260
|
+
const items = screen.getAllByRole('treeitem')
|
|
261
|
+
|
|
262
|
+
expect(items.length).toEqual(3)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('should render a folder icon by default', async () => {
|
|
266
|
+
const { container } = render(
|
|
267
|
+
<TreeBrowser
|
|
268
|
+
collections={COLLECTIONS_DATA}
|
|
269
|
+
items={ITEMS_DATA}
|
|
270
|
+
rootId={2}
|
|
271
|
+
/>
|
|
272
|
+
)
|
|
273
|
+
const iconFolder = container.querySelectorAll('svg[name="IconFolder"]')
|
|
274
|
+
|
|
275
|
+
expect(iconFolder.length).toEqual(1)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('should render a custom icon', async () => {
|
|
279
|
+
const IconCustom = (
|
|
280
|
+
<svg height="100" width="100" data-testid="icon-custom">
|
|
281
|
+
<title data-testid="icon-custom-title">Custom icon</title>
|
|
282
|
+
<circle cx="50" cy="50" r="40" />
|
|
283
|
+
</svg>
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
render(
|
|
287
|
+
<TreeBrowser
|
|
288
|
+
collections={COLLECTIONS_DATA}
|
|
289
|
+
items={ITEMS_DATA}
|
|
290
|
+
rootId={2}
|
|
291
|
+
collectionIcon={() => IconCustom}
|
|
292
|
+
/>
|
|
293
|
+
)
|
|
294
|
+
const iconCustom = screen.getByTestId('icon-custom')
|
|
295
|
+
const title = screen.getByTestId('icon-custom-title')
|
|
296
|
+
|
|
297
|
+
expect(iconCustom).toBeInTheDocument()
|
|
298
|
+
expect(title).toBeInTheDocument()
|
|
299
|
+
expect(title).toHaveTextContent('Custom icon')
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('should render without icon if set to null', async () => {
|
|
303
|
+
const { container } = render(
|
|
304
|
+
<TreeBrowser
|
|
305
|
+
collections={COLLECTIONS_DATA}
|
|
306
|
+
items={ITEMS_DATA}
|
|
307
|
+
rootId={2}
|
|
308
|
+
collectionIcon={null}
|
|
309
|
+
/>
|
|
310
|
+
)
|
|
311
|
+
const icon = container.querySelector('svg')
|
|
312
|
+
|
|
313
|
+
expect(icon).not.toBeInTheDocument()
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it('should call onCollectionToggle when expanding and collapsing with mouse', async () => {
|
|
317
|
+
const onCollectionToggle = vi.fn()
|
|
318
|
+
|
|
319
|
+
render(
|
|
320
|
+
<TreeBrowser
|
|
321
|
+
collections={COLLECTIONS_DATA}
|
|
322
|
+
items={ITEMS_DATA}
|
|
323
|
+
rootId={2}
|
|
324
|
+
onCollectionToggle={onCollectionToggle}
|
|
325
|
+
/>
|
|
326
|
+
)
|
|
327
|
+
const item = screen.getByRole('treeitem')
|
|
328
|
+
|
|
329
|
+
await userEvent.click(item)
|
|
330
|
+
|
|
331
|
+
await waitFor(() => {
|
|
332
|
+
expect(onCollectionToggle).toHaveBeenCalled()
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
it('should call onCollectionClick on button activation (space/enter or click)', async () => {
|
|
337
|
+
const onCollectionClick = vi.fn()
|
|
338
|
+
|
|
339
|
+
render(
|
|
340
|
+
<TreeBrowser
|
|
341
|
+
collections={COLLECTIONS_DATA}
|
|
342
|
+
items={ITEMS_DATA}
|
|
343
|
+
rootId={2}
|
|
344
|
+
onCollectionClick={onCollectionClick}
|
|
345
|
+
/>
|
|
346
|
+
)
|
|
347
|
+
const item = screen.getByLabelText('Root Directory')
|
|
348
|
+
|
|
349
|
+
await userEvent.click(item)
|
|
350
|
+
await userEvent.type(item, '{space}')
|
|
351
|
+
await userEvent.type(item, '{enter}')
|
|
352
|
+
|
|
353
|
+
await waitFor(() => {
|
|
354
|
+
expect(onCollectionClick).toHaveBeenCalledTimes(3)
|
|
355
|
+
})
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
it('should render before, after nodes of the provided collection', async () => {
|
|
359
|
+
const { container } = render(
|
|
360
|
+
<TreeBrowser
|
|
361
|
+
collections={{
|
|
362
|
+
2: {
|
|
363
|
+
id: 2,
|
|
364
|
+
name: 'Root Directory',
|
|
365
|
+
collections: [],
|
|
366
|
+
items: [],
|
|
367
|
+
renderBeforeItems: (
|
|
368
|
+
<TreeNode>
|
|
369
|
+
<input id="input-before" />
|
|
370
|
+
</TreeNode>
|
|
371
|
+
),
|
|
372
|
+
renderAfterItems: (
|
|
373
|
+
<TreeNode>
|
|
374
|
+
<input id="input-after" />
|
|
375
|
+
</TreeNode>
|
|
376
|
+
)
|
|
377
|
+
}
|
|
378
|
+
}}
|
|
379
|
+
items={{}}
|
|
380
|
+
expanded={[2]}
|
|
381
|
+
rootId={2}
|
|
382
|
+
/>
|
|
383
|
+
)
|
|
384
|
+
const contentBefore = container.querySelector('#input-before')
|
|
385
|
+
const contentAfter = container.querySelector('#input-after')
|
|
386
|
+
|
|
387
|
+
expect(contentBefore).toBeInTheDocument()
|
|
388
|
+
expect(contentAfter).toBeInTheDocument()
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
describe('items', () => {
|
|
393
|
+
it('should render a document icon by default', async () => {
|
|
394
|
+
const { container } = render(
|
|
395
|
+
<TreeBrowser
|
|
396
|
+
collections={COLLECTIONS_DATA}
|
|
397
|
+
items={ITEMS_DATA}
|
|
398
|
+
rootId={2}
|
|
399
|
+
defaultExpanded={[2]}
|
|
400
|
+
/>
|
|
401
|
+
)
|
|
402
|
+
const iconDocument = container.querySelectorAll(
|
|
403
|
+
'svg[name="IconDocument"]'
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
expect(iconDocument.length).toEqual(1)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('should render a custom icon', async () => {
|
|
410
|
+
const IconCustom = (
|
|
411
|
+
<svg height="100" width="100" data-testid="icon-custom">
|
|
412
|
+
<title data-testid="icon-custom-title">Custom icon</title>
|
|
413
|
+
<circle cx="50" cy="50" r="40" />
|
|
414
|
+
</svg>
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
render(
|
|
418
|
+
<TreeBrowser
|
|
419
|
+
collections={COLLECTIONS_DATA}
|
|
420
|
+
items={ITEMS_DATA}
|
|
421
|
+
rootId={2}
|
|
422
|
+
defaultExpanded={[2]}
|
|
423
|
+
itemIcon={() => IconCustom}
|
|
424
|
+
/>
|
|
425
|
+
)
|
|
426
|
+
const iconCustom = screen.getByTestId('icon-custom')
|
|
427
|
+
const title = screen.getByTestId('icon-custom-title')
|
|
428
|
+
|
|
429
|
+
expect(iconCustom).toBeInTheDocument()
|
|
430
|
+
expect(title).toBeInTheDocument()
|
|
431
|
+
expect(title).toHaveTextContent('Custom icon')
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
it('should render without icon if set to null', async () => {
|
|
435
|
+
const { container } = render(
|
|
436
|
+
<TreeBrowser
|
|
437
|
+
collections={COLLECTIONS_DATA}
|
|
438
|
+
items={ITEMS_DATA}
|
|
439
|
+
rootId={2}
|
|
440
|
+
/>
|
|
441
|
+
)
|
|
442
|
+
const iconDocument = container.querySelector('svg[name="IconDocument"]')
|
|
443
|
+
|
|
444
|
+
expect(iconDocument).not.toBeInTheDocument()
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
describe('for a11y', () => {
|
|
449
|
+
it('should meet a11y standards', async () => {
|
|
450
|
+
const { container } = render(
|
|
451
|
+
<TreeBrowser
|
|
452
|
+
collections={COLLECTIONS_DATA}
|
|
453
|
+
items={ITEMS_DATA}
|
|
454
|
+
rootId={2}
|
|
455
|
+
/>
|
|
456
|
+
)
|
|
457
|
+
const axeCheck = await runAxeCheck(container)
|
|
458
|
+
expect(axeCheck).toBe(true)
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
it('should accept a treeLabel prop', async () => {
|
|
462
|
+
render(
|
|
463
|
+
<TreeBrowser
|
|
464
|
+
collections={COLLECTIONS_DATA}
|
|
465
|
+
items={ITEMS_DATA}
|
|
466
|
+
rootId={2}
|
|
467
|
+
treeLabel="Test treeLabel"
|
|
468
|
+
/>
|
|
469
|
+
)
|
|
470
|
+
const tree = screen.getByLabelText('Test treeLabel')
|
|
471
|
+
expect(tree).toBeInTheDocument()
|
|
472
|
+
})
|
|
473
|
+
|
|
474
|
+
it('should toggle aria-expanded', async () => {
|
|
475
|
+
render(
|
|
476
|
+
<TreeBrowser
|
|
477
|
+
collections={COLLECTIONS_DATA}
|
|
478
|
+
items={ITEMS_DATA}
|
|
479
|
+
rootId={2}
|
|
480
|
+
/>
|
|
481
|
+
)
|
|
482
|
+
const item = screen.getByRole('treeitem')
|
|
483
|
+
|
|
484
|
+
expect(item).toHaveAttribute('aria-expanded', 'false')
|
|
485
|
+
|
|
486
|
+
await userEvent.click(item)
|
|
487
|
+
|
|
488
|
+
await waitFor(() => {
|
|
489
|
+
expect(item).toHaveAttribute('aria-expanded', 'true')
|
|
490
|
+
})
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
it('should use aria-selected when selectionType is not none', async () => {
|
|
494
|
+
render(
|
|
495
|
+
<TreeBrowser
|
|
496
|
+
collections={COLLECTIONS_DATA}
|
|
497
|
+
items={ITEMS_DATA}
|
|
498
|
+
rootId={2}
|
|
499
|
+
selectionType="single"
|
|
500
|
+
/>
|
|
501
|
+
)
|
|
502
|
+
const item = screen.getByRole('treeitem')
|
|
503
|
+
expect(item).not.toHaveAttribute('aria-selected')
|
|
504
|
+
|
|
505
|
+
await userEvent.click(item)
|
|
506
|
+
|
|
507
|
+
await waitFor(() => {
|
|
508
|
+
expect(item).toHaveAttribute('aria-selected', 'true')
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
const nestedItem = screen.getByLabelText('Sub Root 1')
|
|
512
|
+
expect(nestedItem).toHaveAttribute('aria-selected', 'false')
|
|
513
|
+
})
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
describe('sorting', () => {
|
|
517
|
+
it("should present collections and items in alphabetical order, in spite of the order of 'collections' and 'items' arrays", async () => {
|
|
518
|
+
render(
|
|
519
|
+
<TreeBrowser
|
|
520
|
+
collections={{
|
|
521
|
+
1: {
|
|
522
|
+
id: 1,
|
|
523
|
+
name: 'Assignments',
|
|
524
|
+
collections: [5, 3, 2, 4],
|
|
525
|
+
items: [3, 5, 2, 1, 4]
|
|
526
|
+
},
|
|
527
|
+
2: {
|
|
528
|
+
id: 2,
|
|
529
|
+
name: 'English Assignments',
|
|
530
|
+
collections: [],
|
|
531
|
+
items: []
|
|
532
|
+
},
|
|
533
|
+
3: { id: 3, name: 'Math Assignments', collections: [], items: [] },
|
|
534
|
+
4: {
|
|
535
|
+
id: 4,
|
|
536
|
+
name: 'Reading Assignments',
|
|
537
|
+
collections: [],
|
|
538
|
+
items: []
|
|
539
|
+
},
|
|
540
|
+
5: { id: 5, name: 'Advanced Math Assignments', items: [] }
|
|
541
|
+
}}
|
|
542
|
+
items={{
|
|
543
|
+
1: { id: 1, name: 'Addition Worksheet' },
|
|
544
|
+
2: { id: 2, name: 'Subtraction Worksheet' },
|
|
545
|
+
3: { id: 3, name: 'General Questions' },
|
|
546
|
+
4: { id: 4, name: 'Vogon Poetry' },
|
|
547
|
+
5: { id: 5, name: 'Bistromath' }
|
|
548
|
+
}}
|
|
549
|
+
rootId={1}
|
|
550
|
+
defaultExpanded={[1]}
|
|
551
|
+
sortOrder={(a, b) => {
|
|
552
|
+
return a.name.localeCompare(b.name)
|
|
553
|
+
}}
|
|
554
|
+
/>
|
|
555
|
+
)
|
|
556
|
+
const items = screen.getAllByRole('treeitem')
|
|
557
|
+
|
|
558
|
+
const arr = items.map((item) => item.textContent)
|
|
559
|
+
expect(arr.slice(1, 5)).toStrictEqual([
|
|
560
|
+
'Advanced Math Assignments',
|
|
561
|
+
'English Assignments',
|
|
562
|
+
'Math Assignments',
|
|
563
|
+
'Reading Assignments'
|
|
564
|
+
])
|
|
565
|
+
|
|
566
|
+
expect(arr.slice(5)).toStrictEqual([
|
|
567
|
+
'Addition Worksheet',
|
|
568
|
+
'Bistromath',
|
|
569
|
+
'General Questions',
|
|
570
|
+
'Subtraction Worksheet',
|
|
571
|
+
'Vogon Poetry'
|
|
572
|
+
])
|
|
573
|
+
})
|
|
574
|
+
})
|
|
575
|
+
})
|
package/tsconfig.build.json
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
},
|
|
8
8
|
"include": ["src"],
|
|
9
9
|
"references": [
|
|
10
|
+
{ "path": "../ui-axe-check/tsconfig.build.json" },
|
|
10
11
|
{ "path": "../ui-babel-preset/tsconfig.build.json" },
|
|
11
12
|
{ "path": "../ui-color-utils/tsconfig.build.json" },
|
|
12
|
-
{ "path": "../ui-test-locator/tsconfig.build.json" },
|
|
13
13
|
{ "path": "../ui-test-utils/tsconfig.build.json" },
|
|
14
14
|
{ "path": "../ui-themes/tsconfig.build.json" },
|
|
15
15
|
{ "path": "../emotion/tsconfig.build.json" },
|