@furystack/shades-common-components 14.0.0 → 15.0.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.
- package/CHANGELOG.md +51 -0
- package/esm/components/accordion/accordion-item.d.ts.map +1 -1
- package/esm/components/accordion/accordion-item.js +6 -9
- package/esm/components/accordion/accordion-item.js.map +1 -1
- package/esm/components/accordion/accordion.d.ts +7 -0
- package/esm/components/accordion/accordion.d.ts.map +1 -1
- package/esm/components/accordion/accordion.js +4 -1
- package/esm/components/accordion/accordion.js.map +1 -1
- package/esm/components/accordion/accordion.spec.js +91 -50
- package/esm/components/accordion/accordion.spec.js.map +1 -1
- package/esm/components/carousel.js +1 -1
- package/esm/components/carousel.js.map +1 -1
- package/esm/components/chip.d.ts.map +1 -1
- package/esm/components/chip.js +4 -2
- package/esm/components/chip.js.map +1 -1
- package/esm/components/chip.spec.js +42 -0
- package/esm/components/chip.spec.js.map +1 -1
- package/esm/components/command-palette/index.d.ts.map +1 -1
- package/esm/components/command-palette/index.js +14 -1
- package/esm/components/command-palette/index.js.map +1 -1
- package/esm/components/command-palette/index.spec.js +78 -33
- package/esm/components/command-palette/index.spec.js.map +1 -1
- package/esm/components/data-grid/data-grid-row.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid-row.js +18 -2
- package/esm/components/data-grid/data-grid-row.js.map +1 -1
- package/esm/components/data-grid/data-grid.d.ts +7 -0
- package/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid.js +28 -10
- package/esm/components/data-grid/data-grid.js.map +1 -1
- package/esm/components/data-grid/data-grid.spec.js +114 -34
- package/esm/components/data-grid/data-grid.spec.js.map +1 -1
- package/esm/components/data-grid/selection-cell.d.ts.map +1 -1
- package/esm/components/data-grid/selection-cell.js +1 -1
- package/esm/components/data-grid/selection-cell.js.map +1 -1
- package/esm/components/dialog.d.ts +11 -0
- package/esm/components/dialog.d.ts.map +1 -1
- package/esm/components/dialog.js +2 -2
- package/esm/components/dialog.js.map +1 -1
- package/esm/components/dialog.spec.js +54 -2
- package/esm/components/dialog.spec.js.map +1 -1
- package/esm/components/dropdown.d.ts.map +1 -1
- package/esm/components/dropdown.js +1 -1
- package/esm/components/dropdown.js.map +1 -1
- package/esm/components/dropdown.spec.js +8 -0
- package/esm/components/dropdown.spec.js.map +1 -1
- package/esm/components/image.d.ts.map +1 -1
- package/esm/components/image.js +15 -6
- package/esm/components/image.js.map +1 -1
- package/esm/components/image.spec.js +60 -0
- package/esm/components/image.spec.js.map +1 -1
- package/esm/components/inputs/checkbox.d.ts.map +1 -1
- package/esm/components/inputs/checkbox.js +1 -0
- package/esm/components/inputs/checkbox.js.map +1 -1
- package/esm/components/inputs/radio.d.ts.map +1 -1
- package/esm/components/inputs/radio.js +1 -0
- package/esm/components/inputs/radio.js.map +1 -1
- package/esm/components/inputs/slider.d.ts.map +1 -1
- package/esm/components/inputs/slider.js +1 -0
- package/esm/components/inputs/slider.js.map +1 -1
- package/esm/components/inputs/switch.d.ts.map +1 -1
- package/esm/components/inputs/switch.js +1 -0
- package/esm/components/inputs/switch.js.map +1 -1
- package/esm/components/list/list-item.d.ts.map +1 -1
- package/esm/components/list/list-item.js +21 -5
- package/esm/components/list/list-item.js.map +1 -1
- package/esm/components/list/list.d.ts +7 -0
- package/esm/components/list/list.d.ts.map +1 -1
- package/esm/components/list/list.js +28 -8
- package/esm/components/list/list.js.map +1 -1
- package/esm/components/list/list.spec.js +117 -23
- package/esm/components/list/list.spec.js.map +1 -1
- package/esm/components/markdown/markdown-display.d.ts.map +1 -1
- package/esm/components/markdown/markdown-display.js +11 -1
- package/esm/components/markdown/markdown-display.js.map +1 -1
- package/esm/components/markdown/markdown-display.spec.js +97 -0
- package/esm/components/markdown/markdown-display.spec.js.map +1 -1
- package/esm/components/markdown/markdown-editor.spec.js +87 -0
- package/esm/components/markdown/markdown-editor.spec.js.map +1 -1
- package/esm/components/menu/menu.js +1 -1
- package/esm/components/menu/menu.js.map +1 -1
- package/esm/components/modal.d.ts +10 -0
- package/esm/components/modal.d.ts.map +1 -1
- package/esm/components/modal.js +24 -4
- package/esm/components/modal.js.map +1 -1
- package/esm/components/modal.spec.js +86 -1
- package/esm/components/modal.spec.js.map +1 -1
- package/esm/components/page-layout/index.js +1 -1
- package/esm/components/page-layout/index.js.map +1 -1
- package/esm/components/page-layout/index.spec.js +14 -0
- package/esm/components/page-layout/index.spec.js.map +1 -1
- package/esm/components/rating.d.ts.map +1 -1
- package/esm/components/rating.js +28 -21
- package/esm/components/rating.js.map +1 -1
- package/esm/components/rating.spec.js +151 -4
- package/esm/components/rating.spec.js.map +1 -1
- package/esm/components/suggest/index.d.ts.map +1 -1
- package/esm/components/suggest/index.js +14 -1
- package/esm/components/suggest/index.js.map +1 -1
- package/esm/components/suggest/index.spec.js +98 -43
- package/esm/components/suggest/index.spec.js.map +1 -1
- package/esm/components/tabs.d.ts.map +1 -1
- package/esm/components/tabs.js +4 -0
- package/esm/components/tabs.js.map +1 -1
- package/esm/components/tree/tree-item.d.ts.map +1 -1
- package/esm/components/tree/tree-item.js +18 -5
- package/esm/components/tree/tree-item.js.map +1 -1
- package/esm/components/tree/tree.d.ts +7 -0
- package/esm/components/tree/tree.d.ts.map +1 -1
- package/esm/components/tree/tree.js +12 -3
- package/esm/components/tree/tree.js.map +1 -1
- package/esm/components/tree/tree.spec.js +64 -2
- package/esm/components/tree/tree.spec.js.map +1 -1
- package/esm/services/collection-service.d.ts +9 -0
- package/esm/services/collection-service.d.ts.map +1 -1
- package/esm/services/collection-service.js +33 -11
- package/esm/services/collection-service.js.map +1 -1
- package/esm/services/collection-service.spec.js +33 -24
- package/esm/services/collection-service.spec.js.map +1 -1
- package/esm/services/css-variable-theme.d.ts +7 -0
- package/esm/services/css-variable-theme.d.ts.map +1 -1
- package/esm/services/css-variable-theme.js +23 -0
- package/esm/services/css-variable-theme.js.map +1 -1
- package/esm/services/css-variable-theme.spec.js +1 -0
- package/esm/services/css-variable-theme.spec.js.map +1 -1
- package/esm/services/list-service.d.ts +9 -0
- package/esm/services/list-service.d.ts.map +1 -1
- package/esm/services/list-service.js +13 -13
- package/esm/services/list-service.js.map +1 -1
- package/esm/services/list-service.spec.js +13 -33
- package/esm/services/list-service.spec.js.map +1 -1
- package/esm/services/theme-provider-service.d.ts +3 -0
- package/esm/services/theme-provider-service.d.ts.map +1 -1
- package/esm/services/theme-provider-service.js.map +1 -1
- package/esm/services/tree-service.d.ts.map +1 -1
- package/esm/services/tree-service.js +5 -9
- package/esm/services/tree-service.js.map +1 -1
- package/esm/services/tree-service.spec.js +12 -9
- package/esm/services/tree-service.spec.js.map +1 -1
- package/esm/themes/architect-theme.d.ts +1 -0
- package/esm/themes/architect-theme.d.ts.map +1 -1
- package/esm/themes/architect-theme.js +1 -0
- package/esm/themes/architect-theme.js.map +1 -1
- package/esm/themes/auditore-theme.d.ts +1 -0
- package/esm/themes/auditore-theme.d.ts.map +1 -1
- package/esm/themes/auditore-theme.js +1 -0
- package/esm/themes/auditore-theme.js.map +1 -1
- package/esm/themes/black-mesa-theme.d.ts +1 -0
- package/esm/themes/black-mesa-theme.d.ts.map +1 -1
- package/esm/themes/black-mesa-theme.js +1 -0
- package/esm/themes/black-mesa-theme.js.map +1 -1
- package/esm/themes/chieftain-theme.d.ts +1 -0
- package/esm/themes/chieftain-theme.d.ts.map +1 -1
- package/esm/themes/chieftain-theme.js +1 -0
- package/esm/themes/chieftain-theme.js.map +1 -1
- package/esm/themes/default-dark-theme.d.ts +1 -0
- package/esm/themes/default-dark-theme.d.ts.map +1 -1
- package/esm/themes/default-dark-theme.js +1 -0
- package/esm/themes/default-dark-theme.js.map +1 -1
- package/esm/themes/default-light-theme.d.ts +1 -0
- package/esm/themes/default-light-theme.d.ts.map +1 -1
- package/esm/themes/default-light-theme.js +1 -0
- package/esm/themes/default-light-theme.js.map +1 -1
- package/esm/themes/dragonborn-theme.d.ts +1 -0
- package/esm/themes/dragonborn-theme.d.ts.map +1 -1
- package/esm/themes/dragonborn-theme.js +1 -0
- package/esm/themes/dragonborn-theme.js.map +1 -1
- package/esm/themes/hawkins-theme.d.ts +1 -0
- package/esm/themes/hawkins-theme.d.ts.map +1 -1
- package/esm/themes/hawkins-theme.js +1 -0
- package/esm/themes/hawkins-theme.js.map +1 -1
- package/esm/themes/jedi-theme.d.ts +1 -0
- package/esm/themes/jedi-theme.d.ts.map +1 -1
- package/esm/themes/jedi-theme.js +1 -0
- package/esm/themes/jedi-theme.js.map +1 -1
- package/esm/themes/neon-runner-theme.d.ts +1 -0
- package/esm/themes/neon-runner-theme.d.ts.map +1 -1
- package/esm/themes/neon-runner-theme.js +1 -0
- package/esm/themes/neon-runner-theme.js.map +1 -1
- package/esm/themes/paladin-theme.d.ts +1 -0
- package/esm/themes/paladin-theme.d.ts.map +1 -1
- package/esm/themes/paladin-theme.js +1 -0
- package/esm/themes/paladin-theme.js.map +1 -1
- package/esm/themes/plumber-theme.d.ts +1 -0
- package/esm/themes/plumber-theme.d.ts.map +1 -1
- package/esm/themes/plumber-theme.js +1 -0
- package/esm/themes/plumber-theme.js.map +1 -1
- package/esm/themes/replicant-theme.d.ts +1 -0
- package/esm/themes/replicant-theme.d.ts.map +1 -1
- package/esm/themes/replicant-theme.js +1 -0
- package/esm/themes/replicant-theme.js.map +1 -1
- package/esm/themes/sandworm-theme.d.ts +1 -0
- package/esm/themes/sandworm-theme.d.ts.map +1 -1
- package/esm/themes/sandworm-theme.js +1 -0
- package/esm/themes/sandworm-theme.js.map +1 -1
- package/esm/themes/shadow-broker-theme.d.ts +1 -0
- package/esm/themes/shadow-broker-theme.d.ts.map +1 -1
- package/esm/themes/shadow-broker-theme.js +1 -0
- package/esm/themes/shadow-broker-theme.js.map +1 -1
- package/esm/themes/sith-theme.d.ts +1 -0
- package/esm/themes/sith-theme.d.ts.map +1 -1
- package/esm/themes/sith-theme.js +1 -0
- package/esm/themes/sith-theme.js.map +1 -1
- package/esm/themes/vault-dweller-theme.d.ts +1 -0
- package/esm/themes/vault-dweller-theme.d.ts.map +1 -1
- package/esm/themes/vault-dweller-theme.js +1 -0
- package/esm/themes/vault-dweller-theme.js.map +1 -1
- package/esm/themes/wild-hunt-theme.d.ts +1 -0
- package/esm/themes/wild-hunt-theme.d.ts.map +1 -1
- package/esm/themes/wild-hunt-theme.js +1 -0
- package/esm/themes/wild-hunt-theme.js.map +1 -1
- package/esm/themes/xenomorph-theme.d.ts +1 -0
- package/esm/themes/xenomorph-theme.d.ts.map +1 -1
- package/esm/themes/xenomorph-theme.js +1 -0
- package/esm/themes/xenomorph-theme.js.map +1 -1
- package/package.json +3 -3
- package/src/components/accordion/accordion-item.tsx +9 -14
- package/src/components/accordion/accordion.spec.tsx +134 -79
- package/src/components/accordion/accordion.tsx +13 -1
- package/src/components/carousel.tsx +1 -1
- package/src/components/chip.spec.tsx +64 -0
- package/src/components/chip.tsx +4 -1
- package/src/components/command-palette/index.spec.tsx +95 -33
- package/src/components/command-palette/index.tsx +15 -3
- package/src/components/data-grid/data-grid-row.tsx +20 -2
- package/src/components/data-grid/data-grid.spec.tsx +185 -57
- package/src/components/data-grid/data-grid.tsx +38 -13
- package/src/components/data-grid/selection-cell.tsx +1 -0
- package/src/components/dialog.spec.tsx +77 -2
- package/src/components/dialog.tsx +14 -1
- package/src/components/dropdown.spec.tsx +9 -0
- package/src/components/dropdown.tsx +1 -0
- package/src/components/image.spec.tsx +82 -0
- package/src/components/image.tsx +16 -7
- package/src/components/inputs/checkbox.tsx +1 -0
- package/src/components/inputs/radio.tsx +1 -0
- package/src/components/inputs/slider.tsx +1 -0
- package/src/components/inputs/switch.tsx +1 -0
- package/src/components/list/list-item.tsx +22 -4
- package/src/components/list/list.spec.tsx +165 -32
- package/src/components/list/list.tsx +37 -10
- package/src/components/markdown/markdown-display.spec.tsx +132 -0
- package/src/components/markdown/markdown-display.tsx +12 -1
- package/src/components/markdown/markdown-editor.spec.tsx +123 -0
- package/src/components/menu/menu.tsx +1 -1
- package/src/components/modal.spec.tsx +124 -1
- package/src/components/modal.tsx +41 -3
- package/src/components/page-layout/index.spec.tsx +20 -0
- package/src/components/page-layout/index.tsx +1 -1
- package/src/components/rating.spec.tsx +199 -4
- package/src/components/rating.tsx +28 -22
- package/src/components/suggest/index.spec.tsx +147 -43
- package/src/components/suggest/index.tsx +15 -2
- package/src/components/tabs.tsx +4 -0
- package/src/components/tree/tree-item.tsx +19 -4
- package/src/components/tree/tree.spec.tsx +101 -2
- package/src/components/tree/tree.tsx +21 -3
- package/src/services/collection-service.spec.ts +33 -24
- package/src/services/collection-service.ts +35 -13
- package/src/services/css-variable-theme.spec.ts +1 -0
- package/src/services/css-variable-theme.ts +25 -0
- package/src/services/list-service.spec.ts +13 -42
- package/src/services/list-service.ts +15 -13
- package/src/services/theme-provider-service.ts +2 -0
- package/src/services/tree-service.spec.ts +12 -9
- package/src/services/tree-service.ts +5 -8
- package/src/themes/architect-theme.ts +1 -0
- package/src/themes/auditore-theme.ts +1 -0
- package/src/themes/black-mesa-theme.ts +1 -0
- package/src/themes/chieftain-theme.ts +1 -0
- package/src/themes/default-dark-theme.ts +1 -0
- package/src/themes/default-light-theme.ts +1 -0
- package/src/themes/dragonborn-theme.ts +1 -0
- package/src/themes/hawkins-theme.ts +1 -0
- package/src/themes/jedi-theme.ts +1 -0
- package/src/themes/neon-runner-theme.ts +1 -0
- package/src/themes/paladin-theme.ts +1 -0
- package/src/themes/plumber-theme.ts +1 -0
- package/src/themes/replicant-theme.ts +1 -0
- package/src/themes/sandworm-theme.ts +1 -0
- package/src/themes/shadow-broker-theme.ts +1 -0
- package/src/themes/sith-theme.ts +1 -0
- package/src/themes/vault-dweller-theme.ts +1 -0
- package/src/themes/wild-hunt-theme.ts +1 -0
- package/src/themes/xenomorph-theme.ts +1 -0
|
@@ -284,15 +284,11 @@ describe('DataGrid', () => {
|
|
|
284
284
|
expect(headers?.length).toBe(2)
|
|
285
285
|
})
|
|
286
286
|
})
|
|
287
|
-
})
|
|
288
287
|
|
|
289
|
-
|
|
290
|
-
it('should set focus on click', async () => {
|
|
288
|
+
it('should render with auto-generated data-nav-section attribute', async () => {
|
|
291
289
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
292
290
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
293
291
|
|
|
294
|
-
expect(service.hasFocus.getValue()).toBe(false)
|
|
295
|
-
|
|
296
292
|
initializeShadeRoot({
|
|
297
293
|
injector,
|
|
298
294
|
rootElement,
|
|
@@ -302,70 +298,115 @@ describe('DataGrid', () => {
|
|
|
302
298
|
collectionService={service}
|
|
303
299
|
findOptions={findOptions}
|
|
304
300
|
onFindOptionsChange={onFindOptionsChange}
|
|
305
|
-
styles={{}}
|
|
306
|
-
headerComponents={{}}
|
|
307
|
-
rowComponents={{}}
|
|
308
301
|
/>
|
|
309
302
|
),
|
|
310
303
|
})
|
|
311
304
|
|
|
312
305
|
await flushUpdates()
|
|
313
306
|
|
|
314
|
-
const
|
|
315
|
-
const
|
|
307
|
+
const wrapper = document.querySelector('.shade-grid-wrapper')
|
|
308
|
+
const navSection = wrapper?.getAttribute('data-nav-section')
|
|
309
|
+
expect(navSection).toBeTruthy()
|
|
310
|
+
expect(navSection).toMatch(/^data-grid-/)
|
|
311
|
+
})
|
|
312
|
+
})
|
|
316
313
|
|
|
317
|
-
|
|
314
|
+
it('should render with custom navSection', async () => {
|
|
315
|
+
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
316
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
318
317
|
|
|
319
|
-
|
|
318
|
+
initializeShadeRoot({
|
|
319
|
+
injector,
|
|
320
|
+
rootElement,
|
|
321
|
+
jsxElement: (
|
|
322
|
+
<DataGrid<TestEntry, 'id' | 'name'>
|
|
323
|
+
columns={['id', 'name']}
|
|
324
|
+
collectionService={service}
|
|
325
|
+
findOptions={findOptions}
|
|
326
|
+
onFindOptionsChange={onFindOptionsChange}
|
|
327
|
+
navSection="my-grid"
|
|
328
|
+
/>
|
|
329
|
+
),
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
await flushUpdates()
|
|
333
|
+
|
|
334
|
+
const wrapper = document.querySelector('.shade-grid-wrapper')
|
|
335
|
+
expect(wrapper?.getAttribute('data-nav-section')).toBe('my-grid')
|
|
320
336
|
})
|
|
321
337
|
})
|
|
338
|
+
})
|
|
322
339
|
|
|
323
|
-
|
|
340
|
+
describe('focus management', () => {
|
|
341
|
+
it('should clear hasFocus on focusout when focus leaves the grid', async () => {
|
|
324
342
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
325
343
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
344
|
+
const outsideBtn = document.createElement('button')
|
|
345
|
+
document.body.appendChild(outsideBtn)
|
|
326
346
|
|
|
327
347
|
initializeShadeRoot({
|
|
328
348
|
injector,
|
|
329
349
|
rootElement,
|
|
330
350
|
jsxElement: (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
onFindOptionsChange={onFindOptionsChange}
|
|
338
|
-
styles={{}}
|
|
339
|
-
headerComponents={{}}
|
|
340
|
-
rowComponents={{}}
|
|
341
|
-
/>
|
|
342
|
-
</>
|
|
351
|
+
<DataGrid<TestEntry, 'id' | 'name'>
|
|
352
|
+
columns={['id', 'name']}
|
|
353
|
+
collectionService={service}
|
|
354
|
+
findOptions={findOptions}
|
|
355
|
+
onFindOptionsChange={onFindOptionsChange}
|
|
356
|
+
/>
|
|
343
357
|
),
|
|
344
358
|
})
|
|
345
359
|
|
|
346
360
|
await flushUpdates()
|
|
361
|
+
await new Promise((r) => setTimeout(r, 0))
|
|
347
362
|
|
|
348
|
-
|
|
349
|
-
const wrapper = grid?.querySelector('.shade-grid-wrapper') as HTMLElement
|
|
350
|
-
wrapper?.click()
|
|
351
|
-
|
|
352
|
-
expect(service.hasFocus.getValue()).toBe(true)
|
|
363
|
+
service.hasFocus.setValue(true)
|
|
353
364
|
|
|
354
|
-
const
|
|
355
|
-
|
|
365
|
+
const wrapper = document.querySelector('.shade-grid-wrapper') as HTMLElement
|
|
366
|
+
wrapper?.dispatchEvent(new FocusEvent('focusout', { bubbles: true, relatedTarget: outsideBtn }))
|
|
356
367
|
|
|
357
368
|
expect(service.hasFocus.getValue()).toBe(false)
|
|
369
|
+
outsideBtn.remove()
|
|
358
370
|
})
|
|
359
371
|
})
|
|
360
|
-
})
|
|
361
372
|
|
|
362
|
-
|
|
363
|
-
it('should handle ArrowDown to move focus to next entry', async () => {
|
|
373
|
+
it('should clear hasFocus on focusout when focus moves outside', async () => {
|
|
364
374
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
365
375
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
376
|
+
const outsideEl = document.createElement('button')
|
|
377
|
+
outsideEl.textContent = 'Outside'
|
|
378
|
+
document.body.appendChild(outsideEl)
|
|
379
|
+
|
|
380
|
+
initializeShadeRoot({
|
|
381
|
+
injector,
|
|
382
|
+
rootElement,
|
|
383
|
+
jsxElement: (
|
|
384
|
+
<DataGrid<TestEntry, 'id' | 'name'>
|
|
385
|
+
columns={['id', 'name']}
|
|
386
|
+
collectionService={service}
|
|
387
|
+
findOptions={findOptions}
|
|
388
|
+
onFindOptionsChange={onFindOptionsChange}
|
|
389
|
+
/>
|
|
390
|
+
),
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
await flushUpdates()
|
|
394
|
+
await new Promise((r) => setTimeout(r, 0))
|
|
366
395
|
|
|
367
396
|
service.hasFocus.setValue(true)
|
|
368
|
-
|
|
397
|
+
|
|
398
|
+
const wrapper = document.querySelector('.shade-grid-wrapper') as HTMLElement
|
|
399
|
+
wrapper?.dispatchEvent(new FocusEvent('focusout', { bubbles: true, relatedTarget: outsideEl }))
|
|
400
|
+
|
|
401
|
+
expect(service.hasFocus.getValue()).toBe(false)
|
|
402
|
+
|
|
403
|
+
outsideEl.remove()
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
it('should clear hasFocus on focusout when relatedTarget is null', async () => {
|
|
408
|
+
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
409
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
369
410
|
|
|
370
411
|
initializeShadeRoot({
|
|
371
412
|
injector,
|
|
@@ -376,28 +417,30 @@ describe('DataGrid', () => {
|
|
|
376
417
|
collectionService={service}
|
|
377
418
|
findOptions={findOptions}
|
|
378
419
|
onFindOptionsChange={onFindOptionsChange}
|
|
379
|
-
styles={{}}
|
|
380
|
-
headerComponents={{}}
|
|
381
|
-
rowComponents={{}}
|
|
382
420
|
/>
|
|
383
421
|
),
|
|
384
422
|
})
|
|
385
423
|
|
|
386
424
|
await flushUpdates()
|
|
425
|
+
await new Promise((r) => setTimeout(r, 0))
|
|
387
426
|
|
|
388
|
-
|
|
389
|
-
window.dispatchEvent(keydownEvent)
|
|
427
|
+
service.hasFocus.setValue(true)
|
|
390
428
|
|
|
391
|
-
|
|
429
|
+
const wrapper = document.querySelector('.shade-grid-wrapper') as HTMLElement
|
|
430
|
+
wrapper?.dispatchEvent(new FocusEvent('focusout', { bubbles: true, relatedTarget: null }))
|
|
431
|
+
|
|
432
|
+
expect(service.hasFocus.getValue()).toBe(false)
|
|
392
433
|
})
|
|
393
434
|
})
|
|
435
|
+
})
|
|
394
436
|
|
|
395
|
-
|
|
437
|
+
describe('keyboard navigation', () => {
|
|
438
|
+
it('should move focus to next entry on ArrowDown', async () => {
|
|
396
439
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
397
440
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
398
441
|
|
|
399
442
|
service.hasFocus.setValue(true)
|
|
400
|
-
service.focusedEntry.setValue(service.data.getValue().entries[
|
|
443
|
+
service.focusedEntry.setValue(service.data.getValue().entries[0])
|
|
401
444
|
|
|
402
445
|
initializeShadeRoot({
|
|
403
446
|
injector,
|
|
@@ -417,19 +460,19 @@ describe('DataGrid', () => {
|
|
|
417
460
|
|
|
418
461
|
await flushUpdates()
|
|
419
462
|
|
|
420
|
-
const keydownEvent = new KeyboardEvent('keydown', { key: '
|
|
463
|
+
const keydownEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
|
|
421
464
|
window.dispatchEvent(keydownEvent)
|
|
422
465
|
|
|
423
|
-
expect(service.focusedEntry.getValue()).toEqual({ id:
|
|
466
|
+
expect(service.focusedEntry.getValue()).toEqual({ id: 2, name: 'Second' })
|
|
424
467
|
})
|
|
425
468
|
})
|
|
426
469
|
|
|
427
|
-
it('should
|
|
470
|
+
it('should move focus to previous entry on ArrowUp', async () => {
|
|
428
471
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
429
472
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
430
473
|
|
|
431
474
|
service.hasFocus.setValue(true)
|
|
432
|
-
service.focusedEntry.setValue(service.data.getValue().entries[
|
|
475
|
+
service.focusedEntry.setValue(service.data.getValue().entries[1])
|
|
433
476
|
|
|
434
477
|
initializeShadeRoot({
|
|
435
478
|
injector,
|
|
@@ -449,19 +492,19 @@ describe('DataGrid', () => {
|
|
|
449
492
|
|
|
450
493
|
await flushUpdates()
|
|
451
494
|
|
|
452
|
-
const keydownEvent = new KeyboardEvent('keydown', { key: '
|
|
495
|
+
const keydownEvent = new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true })
|
|
453
496
|
window.dispatchEvent(keydownEvent)
|
|
454
497
|
|
|
455
498
|
expect(service.focusedEntry.getValue()).toEqual({ id: 1, name: 'First' })
|
|
456
499
|
})
|
|
457
500
|
})
|
|
458
501
|
|
|
459
|
-
it('should handle
|
|
502
|
+
it('should handle Home to move focus to first entry', async () => {
|
|
460
503
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
461
504
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
462
505
|
|
|
463
506
|
service.hasFocus.setValue(true)
|
|
464
|
-
service.focusedEntry.setValue(service.data.getValue().entries[
|
|
507
|
+
service.focusedEntry.setValue(service.data.getValue().entries[2])
|
|
465
508
|
|
|
466
509
|
initializeShadeRoot({
|
|
467
510
|
injector,
|
|
@@ -481,18 +524,19 @@ describe('DataGrid', () => {
|
|
|
481
524
|
|
|
482
525
|
await flushUpdates()
|
|
483
526
|
|
|
484
|
-
const keydownEvent = new KeyboardEvent('keydown', { key: '
|
|
527
|
+
const keydownEvent = new KeyboardEvent('keydown', { key: 'Home', bubbles: true })
|
|
485
528
|
window.dispatchEvent(keydownEvent)
|
|
486
529
|
|
|
487
|
-
expect(service.focusedEntry.getValue()).toEqual({ id:
|
|
530
|
+
expect(service.focusedEntry.getValue()).toEqual({ id: 1, name: 'First' })
|
|
488
531
|
})
|
|
489
532
|
})
|
|
490
533
|
|
|
491
|
-
it('should handle
|
|
534
|
+
it('should handle End to move focus to last entry', async () => {
|
|
492
535
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
493
536
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
494
537
|
|
|
495
538
|
service.hasFocus.setValue(true)
|
|
539
|
+
service.focusedEntry.setValue(service.data.getValue().entries[0])
|
|
496
540
|
|
|
497
541
|
initializeShadeRoot({
|
|
498
542
|
injector,
|
|
@@ -512,10 +556,10 @@ describe('DataGrid', () => {
|
|
|
512
556
|
|
|
513
557
|
await flushUpdates()
|
|
514
558
|
|
|
515
|
-
const keydownEvent = new KeyboardEvent('keydown', { key: '
|
|
559
|
+
const keydownEvent = new KeyboardEvent('keydown', { key: 'End', bubbles: true })
|
|
516
560
|
window.dispatchEvent(keydownEvent)
|
|
517
561
|
|
|
518
|
-
expect(service.
|
|
562
|
+
expect(service.focusedEntry.getValue()).toEqual({ id: 3, name: 'Third' })
|
|
519
563
|
})
|
|
520
564
|
})
|
|
521
565
|
|
|
@@ -694,7 +738,6 @@ describe('DataGrid', () => {
|
|
|
694
738
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
695
739
|
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
696
740
|
|
|
697
|
-
service.hasFocus.setValue(false)
|
|
698
741
|
service.focusedEntry.setValue(service.data.getValue().entries[0])
|
|
699
742
|
|
|
700
743
|
initializeShadeRoot({
|
|
@@ -715,6 +758,8 @@ describe('DataGrid', () => {
|
|
|
715
758
|
|
|
716
759
|
await flushUpdates()
|
|
717
760
|
|
|
761
|
+
service.hasFocus.setValue(false)
|
|
762
|
+
|
|
718
763
|
const keydownEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
|
|
719
764
|
window.dispatchEvent(keydownEvent)
|
|
720
765
|
|
|
@@ -934,6 +979,89 @@ describe('DataGrid', () => {
|
|
|
934
979
|
})
|
|
935
980
|
})
|
|
936
981
|
|
|
982
|
+
describe('row spatial navigation attributes', () => {
|
|
983
|
+
it('should set data-spatial-nav-target on rows', async () => {
|
|
984
|
+
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
985
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
986
|
+
|
|
987
|
+
initializeShadeRoot({
|
|
988
|
+
injector,
|
|
989
|
+
rootElement,
|
|
990
|
+
jsxElement: (
|
|
991
|
+
<DataGrid<TestEntry, 'id' | 'name'>
|
|
992
|
+
columns={['id', 'name']}
|
|
993
|
+
collectionService={service}
|
|
994
|
+
findOptions={findOptions}
|
|
995
|
+
onFindOptionsChange={onFindOptionsChange}
|
|
996
|
+
/>
|
|
997
|
+
),
|
|
998
|
+
})
|
|
999
|
+
|
|
1000
|
+
await flushUpdates()
|
|
1001
|
+
|
|
1002
|
+
const rows = document.querySelectorAll('shades-data-grid-row')
|
|
1003
|
+
for (const row of rows) {
|
|
1004
|
+
expect(row.hasAttribute('data-spatial-nav-target')).toBe(true)
|
|
1005
|
+
}
|
|
1006
|
+
})
|
|
1007
|
+
})
|
|
1008
|
+
|
|
1009
|
+
it('should set tabIndex 0 on focused row and -1 on others', async () => {
|
|
1010
|
+
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
1011
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
1012
|
+
|
|
1013
|
+
service.focusedEntry.setValue(service.data.getValue().entries[1])
|
|
1014
|
+
|
|
1015
|
+
initializeShadeRoot({
|
|
1016
|
+
injector,
|
|
1017
|
+
rootElement,
|
|
1018
|
+
jsxElement: (
|
|
1019
|
+
<DataGrid<TestEntry, 'id' | 'name'>
|
|
1020
|
+
columns={['id', 'name']}
|
|
1021
|
+
collectionService={service}
|
|
1022
|
+
findOptions={findOptions}
|
|
1023
|
+
onFindOptionsChange={onFindOptionsChange}
|
|
1024
|
+
/>
|
|
1025
|
+
),
|
|
1026
|
+
})
|
|
1027
|
+
|
|
1028
|
+
await flushUpdates()
|
|
1029
|
+
|
|
1030
|
+
const rows = document.querySelectorAll<HTMLTableRowElement>('shades-data-grid-row')
|
|
1031
|
+
expect(rows[0]?.tabIndex).toBe(-1)
|
|
1032
|
+
expect(rows[1]?.tabIndex).toBe(0)
|
|
1033
|
+
expect(rows[2]?.tabIndex).toBe(-1)
|
|
1034
|
+
})
|
|
1035
|
+
})
|
|
1036
|
+
|
|
1037
|
+
it('should sync focusedEntry on row onfocus', async () => {
|
|
1038
|
+
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
1039
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
1040
|
+
|
|
1041
|
+
initializeShadeRoot({
|
|
1042
|
+
injector,
|
|
1043
|
+
rootElement,
|
|
1044
|
+
jsxElement: (
|
|
1045
|
+
<DataGrid<TestEntry, 'id' | 'name'>
|
|
1046
|
+
columns={['id', 'name']}
|
|
1047
|
+
collectionService={service}
|
|
1048
|
+
findOptions={findOptions}
|
|
1049
|
+
onFindOptionsChange={onFindOptionsChange}
|
|
1050
|
+
/>
|
|
1051
|
+
),
|
|
1052
|
+
})
|
|
1053
|
+
|
|
1054
|
+
await flushUpdates()
|
|
1055
|
+
|
|
1056
|
+
const rows = document.querySelectorAll('shades-data-grid-row')
|
|
1057
|
+
rows[2]?.dispatchEvent(new FocusEvent('focus'))
|
|
1058
|
+
|
|
1059
|
+
expect(service.focusedEntry.getValue()).toEqual({ id: 3, name: 'Third' })
|
|
1060
|
+
expect(service.hasFocus.getValue()).toBe(true)
|
|
1061
|
+
})
|
|
1062
|
+
})
|
|
1063
|
+
})
|
|
1064
|
+
|
|
937
1065
|
describe('keyboard listener cleanup', () => {
|
|
938
1066
|
it('should remove keyboard listener when component is disconnected', async () => {
|
|
939
1067
|
await withTestGrid(async ({ injector, service, findOptions, onFindOptionsChange }) => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { FindOptions } from '@furystack/core'
|
|
2
2
|
import type { ChildrenList } from '@furystack/shades'
|
|
3
3
|
import { createComponent, Shade } from '@furystack/shades'
|
|
4
|
-
import { ClickAwayService } from '../../services/click-away-service.js'
|
|
5
4
|
import type { CollectionService } from '../../services/collection-service.js'
|
|
6
5
|
import { cssVariableTheme } from '../../services/css-variable-theme.js'
|
|
7
6
|
import type { GridProps } from '../grid.js'
|
|
@@ -9,6 +8,8 @@ import { DataGridBody } from './body.js'
|
|
|
9
8
|
import { DataGridFooter } from './footer.js'
|
|
10
9
|
import { DataGridHeader } from './header.js'
|
|
11
10
|
|
|
11
|
+
let nextDataGridId = 0
|
|
12
|
+
|
|
12
13
|
export type StringFilterConfig = { type: 'string' }
|
|
13
14
|
export type NumberFilterConfig = { type: 'number' }
|
|
14
15
|
export type BooleanFilterConfig = { type: 'boolean' }
|
|
@@ -119,6 +120,14 @@ export interface DataGridProps<T, Column extends string> {
|
|
|
119
120
|
* @default dataGridItemsPerPage ([10, 20, 25, 50, 100, Infinity])
|
|
120
121
|
*/
|
|
121
122
|
paginationOptions?: number[]
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Section name for spatial navigation scoping.
|
|
126
|
+
* Sets `data-nav-section` on the grid wrapper so that SpatialNavigationService
|
|
127
|
+
* constrains arrow-key navigation within the grid.
|
|
128
|
+
* Auto-generated per instance when not provided.
|
|
129
|
+
*/
|
|
130
|
+
navSection?: string
|
|
122
131
|
}
|
|
123
132
|
|
|
124
133
|
export const DataGrid: <T, Column extends string>(
|
|
@@ -157,25 +166,43 @@ export const DataGrid: <T, Column extends string>(
|
|
|
157
166
|
borderRight: `1px solid ${cssVariableTheme.action.subtleBorder}`,
|
|
158
167
|
},
|
|
159
168
|
},
|
|
160
|
-
render: ({ props, useDisposable, useRef, useHostProps }) => {
|
|
169
|
+
render: ({ props, useDisposable, useRef, useHostProps, useState }) => {
|
|
161
170
|
const wrapperRef = useRef<HTMLDivElement>('gridWrapper')
|
|
171
|
+
const [navSectionId] = useState('navSectionId', String(nextDataGridId++))
|
|
162
172
|
|
|
163
173
|
const headerFindOptions = props.findOptions as FilterableFindOptions
|
|
164
174
|
const handleHeaderChange = props.onFindOptionsChange as (options: FilterableFindOptions) => void
|
|
165
175
|
|
|
166
176
|
useDisposable('keydown-handler', () => {
|
|
167
177
|
const listener = (ev: KeyboardEvent) => props.collectionService.handleKeyDown(ev)
|
|
168
|
-
window.addEventListener('keydown', listener)
|
|
169
|
-
return { [Symbol.dispose]: () => window.removeEventListener('keydown', listener) }
|
|
178
|
+
window.addEventListener('keydown', listener, true)
|
|
179
|
+
return { [Symbol.dispose]: () => window.removeEventListener('keydown', listener, true) }
|
|
170
180
|
})
|
|
171
181
|
|
|
172
|
-
useDisposable(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
182
|
+
useDisposable('focus-coordination', () => {
|
|
183
|
+
const handleFocusOut = (ev: FocusEvent) => {
|
|
184
|
+
const wrapper = wrapperRef.current
|
|
185
|
+
if (wrapper && (!ev.relatedTarget || !wrapper.contains(ev.relatedTarget as Node))) {
|
|
176
186
|
props.collectionService.hasFocus.setValue(false)
|
|
177
|
-
}
|
|
178
|
-
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
queueMicrotask(() => {
|
|
191
|
+
const wrapper = wrapperRef.current
|
|
192
|
+
if (wrapper) {
|
|
193
|
+
wrapper.addEventListener('focusout', handleFocusOut)
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
[Symbol.dispose]: () => {
|
|
199
|
+
const wrapper = wrapperRef.current
|
|
200
|
+
if (wrapper) {
|
|
201
|
+
wrapper.removeEventListener('focusout', handleFocusOut)
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
}
|
|
205
|
+
})
|
|
179
206
|
|
|
180
207
|
if (props.styles?.wrapper) {
|
|
181
208
|
useHostProps({ style: props.styles.wrapper as Record<string, string> })
|
|
@@ -185,9 +212,7 @@ export const DataGrid: <T, Column extends string>(
|
|
|
185
212
|
<div
|
|
186
213
|
ref={wrapperRef}
|
|
187
214
|
className="shade-grid-wrapper"
|
|
188
|
-
|
|
189
|
-
props.collectionService.hasFocus.setValue(true)
|
|
190
|
-
}}
|
|
215
|
+
data-nav-section={props.navSection ?? `data-grid-${navSectionId}`}
|
|
191
216
|
ariaMultiSelectable="true"
|
|
192
217
|
>
|
|
193
218
|
<table>
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Injector } from '@furystack/inject'
|
|
2
|
+
import { createComponent, flushUpdates, initializeShadeRoot, SpatialNavigationService } from '@furystack/shades'
|
|
3
|
+
import { usingAsync } from '@furystack/utils'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
5
|
import type { DialogProps } from './dialog.js'
|
|
4
6
|
import { ConfirmDialog, Dialog } from './dialog.js'
|
|
5
7
|
|
|
6
8
|
describe('Dialog', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
document.body.innerHTML = '<div id="root"></div>'
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
document.body.innerHTML = ''
|
|
15
|
+
})
|
|
16
|
+
|
|
7
17
|
it('should be defined', () => {
|
|
8
18
|
expect(Dialog).toBeDefined()
|
|
9
19
|
expect(typeof Dialog).toBe('function')
|
|
@@ -55,6 +65,71 @@ describe('Dialog', () => {
|
|
|
55
65
|
const el = (<Dialog isVisible={false} />) as unknown as { props: DialogProps }
|
|
56
66
|
expect(el.props.isVisible).toBe(false)
|
|
57
67
|
})
|
|
68
|
+
|
|
69
|
+
it('should forward trapFocus to the underlying Modal', async () => {
|
|
70
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
71
|
+
const spatialNav = injector.getInstance(SpatialNavigationService)
|
|
72
|
+
const pushSpy = vi.spyOn(spatialNav, 'pushFocusTrap')
|
|
73
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
74
|
+
|
|
75
|
+
initializeShadeRoot({
|
|
76
|
+
injector,
|
|
77
|
+
rootElement,
|
|
78
|
+
jsxElement: (
|
|
79
|
+
<Dialog isVisible={true} trapFocus={true} navSection="dialog-trap">
|
|
80
|
+
<div>Content</div>
|
|
81
|
+
</Dialog>
|
|
82
|
+
),
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
await flushUpdates()
|
|
86
|
+
|
|
87
|
+
expect(pushSpy).toHaveBeenCalledWith('dialog-trap')
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should forward navSection to the underlying Modal', async () => {
|
|
92
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
93
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
94
|
+
|
|
95
|
+
initializeShadeRoot({
|
|
96
|
+
injector,
|
|
97
|
+
rootElement,
|
|
98
|
+
jsxElement: (
|
|
99
|
+
<Dialog isVisible={true} navSection="my-dialog">
|
|
100
|
+
<div>Content</div>
|
|
101
|
+
</Dialog>
|
|
102
|
+
),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
await flushUpdates()
|
|
106
|
+
|
|
107
|
+
const backdrop = document.querySelector('.shade-backdrop')
|
|
108
|
+
expect(backdrop?.getAttribute('data-nav-section')).toBe('my-dialog')
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should not push focus trap when trapFocus is false', async () => {
|
|
113
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
114
|
+
const spatialNav = injector.getInstance(SpatialNavigationService)
|
|
115
|
+
const pushSpy = vi.spyOn(spatialNav, 'pushFocusTrap')
|
|
116
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
117
|
+
|
|
118
|
+
initializeShadeRoot({
|
|
119
|
+
injector,
|
|
120
|
+
rootElement,
|
|
121
|
+
jsxElement: (
|
|
122
|
+
<Dialog isVisible={true} trapFocus={false}>
|
|
123
|
+
<div>Content</div>
|
|
124
|
+
</Dialog>
|
|
125
|
+
),
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
await flushUpdates()
|
|
129
|
+
|
|
130
|
+
expect(pushSpy).not.toHaveBeenCalled()
|
|
131
|
+
})
|
|
132
|
+
})
|
|
58
133
|
})
|
|
59
134
|
|
|
60
135
|
describe('ConfirmDialog', () => {
|
|
@@ -13,6 +13,17 @@ export type DialogProps = {
|
|
|
13
13
|
actions?: JSX.Element
|
|
14
14
|
maxWidth?: string
|
|
15
15
|
fullWidth?: boolean
|
|
16
|
+
/**
|
|
17
|
+
* When true, traps spatial navigation within the dialog's bounds.
|
|
18
|
+
* Forwarded to the underlying Modal component.
|
|
19
|
+
*/
|
|
20
|
+
trapFocus?: boolean
|
|
21
|
+
/**
|
|
22
|
+
* Section name for spatial navigation scoping.
|
|
23
|
+
* Forwarded to the underlying Modal component.
|
|
24
|
+
* @default 'modal'
|
|
25
|
+
*/
|
|
26
|
+
navSection?: string
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
const showAnimation = async (el: Element | null) => {
|
|
@@ -127,7 +138,7 @@ export const Dialog = Shade<DialogProps>({
|
|
|
127
138
|
},
|
|
128
139
|
},
|
|
129
140
|
render: ({ props, children }) => {
|
|
130
|
-
const { isVisible, title, onClose, actions, maxWidth = '560px', fullWidth } = props
|
|
141
|
+
const { isVisible, title, onClose, actions, maxWidth = '560px', fullWidth, trapFocus = true, navSection } = props
|
|
131
142
|
|
|
132
143
|
const handleClose = () => {
|
|
133
144
|
onClose?.()
|
|
@@ -139,6 +150,8 @@ export const Dialog = Shade<DialogProps>({
|
|
|
139
150
|
onClose={handleClose}
|
|
140
151
|
showAnimation={showAnimation}
|
|
141
152
|
hideAnimation={hideAnimation}
|
|
153
|
+
trapFocus={trapFocus}
|
|
154
|
+
navSection={navSection}
|
|
142
155
|
backdropStyle={{
|
|
143
156
|
display: 'flex',
|
|
144
157
|
alignItems: 'center',
|
|
@@ -416,6 +416,15 @@ describe('Dropdown', () => {
|
|
|
416
416
|
})
|
|
417
417
|
})
|
|
418
418
|
|
|
419
|
+
describe('spatial navigation', () => {
|
|
420
|
+
it('should have data-spatial-nav-passthrough on the backdrop', async () => {
|
|
421
|
+
await usingAsync(await renderDropdown({}), async ({ dropdown }) => {
|
|
422
|
+
const backdrop = dropdown.querySelector('.dropdown-backdrop') as HTMLElement
|
|
423
|
+
expect(backdrop.hasAttribute('data-spatial-nav-passthrough')).toBe(true)
|
|
424
|
+
})
|
|
425
|
+
})
|
|
426
|
+
})
|
|
427
|
+
|
|
419
428
|
describe('placement', () => {
|
|
420
429
|
it('should accept bottomRight placement', async () => {
|
|
421
430
|
await usingAsync(await renderDropdown({ placement: 'bottomRight' }), async ({ dropdown }) => {
|