@furystack/shades-common-components 14.0.0 → 15.0.1
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 +71 -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/suggest/suggest-manager.js +2 -2
- package/esm/components/suggest/suggest-manager.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 +7 -7
- 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/suggest/suggest-manager.ts +2 -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
|
@@ -52,6 +52,34 @@ describe('Accordion', () => {
|
|
|
52
52
|
await flushUpdates()
|
|
53
53
|
expect(accordion.hasAttribute('data-variant')).toBe(false)
|
|
54
54
|
})
|
|
55
|
+
|
|
56
|
+
describe('spatial navigation', () => {
|
|
57
|
+
it('should set data-nav-section with auto-generated id', async () => {
|
|
58
|
+
const el = (
|
|
59
|
+
<div>
|
|
60
|
+
<Accordion />
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
const accordion = el.firstElementChild as JSX.Element
|
|
64
|
+
accordion.updateComponent()
|
|
65
|
+
await flushUpdates()
|
|
66
|
+
const navSection = accordion.getAttribute('data-nav-section')
|
|
67
|
+
expect(navSection).toBeTruthy()
|
|
68
|
+
expect(navSection).toMatch(/^accordion-\d+$/)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('should use custom navSection when provided', async () => {
|
|
72
|
+
const el = (
|
|
73
|
+
<div>
|
|
74
|
+
<Accordion navSection="my-accordion" />
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
const accordion = el.firstElementChild as JSX.Element
|
|
78
|
+
accordion.updateComponent()
|
|
79
|
+
await flushUpdates()
|
|
80
|
+
expect(accordion.getAttribute('data-nav-section')).toBe('my-accordion')
|
|
81
|
+
})
|
|
82
|
+
})
|
|
55
83
|
})
|
|
56
84
|
|
|
57
85
|
describe('AccordionItem', () => {
|
|
@@ -239,7 +267,7 @@ describe('AccordionItem', () => {
|
|
|
239
267
|
expect(content.style.height).not.toBe('0px')
|
|
240
268
|
})
|
|
241
269
|
|
|
242
|
-
it('should
|
|
270
|
+
it('should render the header as a <button> element', async () => {
|
|
243
271
|
const el = (
|
|
244
272
|
<div>
|
|
245
273
|
<AccordionItem title="Test" />
|
|
@@ -248,11 +276,12 @@ describe('AccordionItem', () => {
|
|
|
248
276
|
const item = el.firstElementChild as JSX.Element
|
|
249
277
|
item.updateComponent()
|
|
250
278
|
await flushUpdates()
|
|
251
|
-
const header = item.querySelector('.accordion-header') as
|
|
252
|
-
expect(header.
|
|
279
|
+
const header = item.querySelector('.accordion-header') as HTMLButtonElement
|
|
280
|
+
expect(header.tagName).toBe('BUTTON')
|
|
281
|
+
expect(header.type).toBe('button')
|
|
253
282
|
})
|
|
254
283
|
|
|
255
|
-
it('should
|
|
284
|
+
it('should not disable the header button when not disabled', async () => {
|
|
256
285
|
const el = (
|
|
257
286
|
<div>
|
|
258
287
|
<AccordionItem title="Test" />
|
|
@@ -261,11 +290,11 @@ describe('AccordionItem', () => {
|
|
|
261
290
|
const item = el.firstElementChild as JSX.Element
|
|
262
291
|
item.updateComponent()
|
|
263
292
|
await flushUpdates()
|
|
264
|
-
const header = item.querySelector('.accordion-header') as
|
|
265
|
-
expect(header.
|
|
293
|
+
const header = item.querySelector('.accordion-header') as HTMLButtonElement
|
|
294
|
+
expect(header.disabled).toBe(false)
|
|
266
295
|
})
|
|
267
296
|
|
|
268
|
-
it('should
|
|
297
|
+
it('should disable the header button when disabled', async () => {
|
|
269
298
|
const el = (
|
|
270
299
|
<div>
|
|
271
300
|
<AccordionItem title="Test" disabled />
|
|
@@ -274,8 +303,8 @@ describe('AccordionItem', () => {
|
|
|
274
303
|
const item = el.firstElementChild as JSX.Element
|
|
275
304
|
item.updateComponent()
|
|
276
305
|
await flushUpdates()
|
|
277
|
-
const header = item.querySelector('.accordion-header') as
|
|
278
|
-
expect(header.
|
|
306
|
+
const header = item.querySelector('.accordion-header') as HTMLButtonElement
|
|
307
|
+
expect(header.disabled).toBe(true)
|
|
279
308
|
})
|
|
280
309
|
|
|
281
310
|
it('should toggle data-expanded on header click', async () => {
|
|
@@ -299,7 +328,6 @@ describe('AccordionItem', () => {
|
|
|
299
328
|
header.click()
|
|
300
329
|
await flushUpdates()
|
|
301
330
|
|
|
302
|
-
// The attribute is set synchronously before the animation awaits
|
|
303
331
|
expect(item.hasAttribute('data-expanded')).toBe(true)
|
|
304
332
|
})
|
|
305
333
|
|
|
@@ -324,75 +352,6 @@ describe('AccordionItem', () => {
|
|
|
324
352
|
header.click()
|
|
325
353
|
await flushUpdates()
|
|
326
354
|
|
|
327
|
-
// The attribute is removed synchronously before the animation awaits
|
|
328
|
-
expect(item.hasAttribute('data-expanded')).toBe(false)
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
it('should toggle on Enter keydown', async () => {
|
|
332
|
-
const mockAnimation = { finished: Promise.resolve() }
|
|
333
|
-
Element.prototype.animate = vi.fn().mockReturnValue(mockAnimation)
|
|
334
|
-
|
|
335
|
-
const el = (
|
|
336
|
-
<div>
|
|
337
|
-
<AccordionItem title="Keyboard Toggle">
|
|
338
|
-
<p>Content</p>
|
|
339
|
-
</AccordionItem>
|
|
340
|
-
</div>
|
|
341
|
-
)
|
|
342
|
-
const item = el.firstElementChild as JSX.Element
|
|
343
|
-
item.updateComponent()
|
|
344
|
-
await flushUpdates()
|
|
345
|
-
|
|
346
|
-
expect(item.hasAttribute('data-expanded')).toBe(false)
|
|
347
|
-
|
|
348
|
-
const header = item.querySelector('.accordion-header') as HTMLElement
|
|
349
|
-
header.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
|
|
350
|
-
await flushUpdates()
|
|
351
|
-
|
|
352
|
-
expect(item.hasAttribute('data-expanded')).toBe(true)
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
it('should toggle on Space keydown', async () => {
|
|
356
|
-
const mockAnimation = { finished: Promise.resolve() }
|
|
357
|
-
Element.prototype.animate = vi.fn().mockReturnValue(mockAnimation)
|
|
358
|
-
|
|
359
|
-
const el = (
|
|
360
|
-
<div>
|
|
361
|
-
<AccordionItem title="Space Toggle">
|
|
362
|
-
<p>Content</p>
|
|
363
|
-
</AccordionItem>
|
|
364
|
-
</div>
|
|
365
|
-
)
|
|
366
|
-
const item = el.firstElementChild as JSX.Element
|
|
367
|
-
item.updateComponent()
|
|
368
|
-
await flushUpdates()
|
|
369
|
-
|
|
370
|
-
expect(item.hasAttribute('data-expanded')).toBe(false)
|
|
371
|
-
|
|
372
|
-
const header = item.querySelector('.accordion-header') as HTMLElement
|
|
373
|
-
header.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true }))
|
|
374
|
-
await flushUpdates()
|
|
375
|
-
|
|
376
|
-
expect(item.hasAttribute('data-expanded')).toBe(true)
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
it('should not toggle on unrelated keydown', async () => {
|
|
380
|
-
const el = (
|
|
381
|
-
<div>
|
|
382
|
-
<AccordionItem title="No Toggle">
|
|
383
|
-
<p>Content</p>
|
|
384
|
-
</AccordionItem>
|
|
385
|
-
</div>
|
|
386
|
-
)
|
|
387
|
-
const item = el.firstElementChild as JSX.Element
|
|
388
|
-
item.updateComponent()
|
|
389
|
-
await flushUpdates()
|
|
390
|
-
|
|
391
|
-
expect(item.hasAttribute('data-expanded')).toBe(false)
|
|
392
|
-
|
|
393
|
-
const header = item.querySelector('.accordion-header') as HTMLElement
|
|
394
|
-
header.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab', bubbles: true }))
|
|
395
|
-
|
|
396
355
|
expect(item.hasAttribute('data-expanded')).toBe(false)
|
|
397
356
|
})
|
|
398
357
|
|
|
@@ -415,4 +374,100 @@ describe('AccordionItem', () => {
|
|
|
415
374
|
|
|
416
375
|
expect(item.hasAttribute('data-expanded')).toBe(false)
|
|
417
376
|
})
|
|
377
|
+
|
|
378
|
+
describe('spatial navigation integration', () => {
|
|
379
|
+
it('should set inert on content when collapsed', async () => {
|
|
380
|
+
const el = (
|
|
381
|
+
<div>
|
|
382
|
+
<AccordionItem title="Collapsed">
|
|
383
|
+
<p>Content</p>
|
|
384
|
+
</AccordionItem>
|
|
385
|
+
</div>
|
|
386
|
+
)
|
|
387
|
+
const item = el.firstElementChild as JSX.Element
|
|
388
|
+
item.updateComponent()
|
|
389
|
+
await flushUpdates()
|
|
390
|
+
const content = item.querySelector('.accordion-content') as HTMLElement
|
|
391
|
+
expect(content.inert).toBe(true)
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
it('should not set inert on content when expanded', async () => {
|
|
395
|
+
const el = (
|
|
396
|
+
<div>
|
|
397
|
+
<AccordionItem title="Expanded" defaultExpanded>
|
|
398
|
+
<p>Content</p>
|
|
399
|
+
</AccordionItem>
|
|
400
|
+
</div>
|
|
401
|
+
)
|
|
402
|
+
const item = el.firstElementChild as JSX.Element
|
|
403
|
+
item.updateComponent()
|
|
404
|
+
await flushUpdates()
|
|
405
|
+
const content = item.querySelector('.accordion-content') as HTMLElement
|
|
406
|
+
expect(content.inert).toBe(false)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('should set inert on content after collapsing', async () => {
|
|
410
|
+
const mockAnimation = { finished: Promise.resolve() }
|
|
411
|
+
Element.prototype.animate = vi.fn().mockReturnValue(mockAnimation)
|
|
412
|
+
|
|
413
|
+
const el = (
|
|
414
|
+
<div>
|
|
415
|
+
<AccordionItem title="Toggleable" defaultExpanded>
|
|
416
|
+
<p>Content</p>
|
|
417
|
+
</AccordionItem>
|
|
418
|
+
</div>
|
|
419
|
+
)
|
|
420
|
+
const item = el.firstElementChild as JSX.Element
|
|
421
|
+
item.updateComponent()
|
|
422
|
+
await flushUpdates()
|
|
423
|
+
|
|
424
|
+
const content = item.querySelector('.accordion-content') as HTMLElement
|
|
425
|
+
expect(content.inert).toBe(false)
|
|
426
|
+
|
|
427
|
+
const header = item.querySelector('.accordion-header') as HTMLElement
|
|
428
|
+
header.click()
|
|
429
|
+
await flushUpdates()
|
|
430
|
+
|
|
431
|
+
expect(content.inert).toBe(true)
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
it('should remove inert on content after expanding', async () => {
|
|
435
|
+
const mockAnimation = { finished: Promise.resolve() }
|
|
436
|
+
Element.prototype.animate = vi.fn().mockReturnValue(mockAnimation)
|
|
437
|
+
|
|
438
|
+
const el = (
|
|
439
|
+
<div>
|
|
440
|
+
<AccordionItem title="Toggleable">
|
|
441
|
+
<p>Content</p>
|
|
442
|
+
</AccordionItem>
|
|
443
|
+
</div>
|
|
444
|
+
)
|
|
445
|
+
const item = el.firstElementChild as JSX.Element
|
|
446
|
+
item.updateComponent()
|
|
447
|
+
await flushUpdates()
|
|
448
|
+
|
|
449
|
+
const content = item.querySelector('.accordion-content') as HTMLElement
|
|
450
|
+
expect(content.inert).toBe(true)
|
|
451
|
+
|
|
452
|
+
const header = item.querySelector('.accordion-header') as HTMLElement
|
|
453
|
+
header.click()
|
|
454
|
+
await flushUpdates()
|
|
455
|
+
|
|
456
|
+
expect(content.inert).toBe(false)
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
it('should exclude disabled header from spatial navigation via native disabled attribute', async () => {
|
|
460
|
+
const el = (
|
|
461
|
+
<div>
|
|
462
|
+
<AccordionItem title="Disabled" disabled />
|
|
463
|
+
</div>
|
|
464
|
+
)
|
|
465
|
+
const item = el.firstElementChild as JSX.Element
|
|
466
|
+
item.updateComponent()
|
|
467
|
+
await flushUpdates()
|
|
468
|
+
const header = item.querySelector('.accordion-header') as HTMLButtonElement
|
|
469
|
+
expect(header.disabled).toBe(true)
|
|
470
|
+
expect(header.matches('button:not([disabled])')).toBe(false)
|
|
471
|
+
})
|
|
472
|
+
})
|
|
418
473
|
})
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { Shade, createComponent } from '@furystack/shades'
|
|
2
2
|
import { cssVariableTheme } from '../../services/css-variable-theme.js'
|
|
3
3
|
|
|
4
|
+
let nextAccordionId = 0
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Props for the Accordion container component.
|
|
6
8
|
*/
|
|
7
9
|
export type AccordionProps = {
|
|
8
10
|
/** Visual variant of the accordion container */
|
|
9
11
|
variant?: 'outlined' | 'elevation'
|
|
12
|
+
/**
|
|
13
|
+
* Section name for spatial navigation scoping.
|
|
14
|
+
* Sets `data-nav-section` on the accordion so that SpatialNavigationService
|
|
15
|
+
* constrains arrow-key navigation within the accordion group.
|
|
16
|
+
* Auto-generated per instance when not provided.
|
|
17
|
+
*/
|
|
18
|
+
navSection?: string
|
|
10
19
|
}
|
|
11
20
|
|
|
12
21
|
/**
|
|
@@ -41,9 +50,12 @@ export const Accordion = Shade<AccordionProps>({
|
|
|
41
50
|
background: cssVariableTheme.background.paper,
|
|
42
51
|
},
|
|
43
52
|
},
|
|
44
|
-
render: ({ props, useHostProps, children }) => {
|
|
53
|
+
render: ({ props, useHostProps, children, useState }) => {
|
|
54
|
+
const [navSectionId] = useState('navSectionId', String(nextAccordionId++))
|
|
55
|
+
|
|
45
56
|
useHostProps({
|
|
46
57
|
'data-variant': props.variant || undefined,
|
|
58
|
+
'data-nav-section': props.navSection ?? `accordion-${navSectionId}`,
|
|
47
59
|
})
|
|
48
60
|
|
|
49
61
|
return <>{children}</>
|
|
@@ -200,7 +200,7 @@ export const Carousel = Shade<CarouselProps>({
|
|
|
200
200
|
'data-vertical': vertical ? '' : undefined,
|
|
201
201
|
role: 'region',
|
|
202
202
|
'aria-roledescription': 'carousel',
|
|
203
|
-
|
|
203
|
+
tabIndex: 0,
|
|
204
204
|
...(style ? { style: style as Record<string, string> } : {}),
|
|
205
205
|
})
|
|
206
206
|
|
|
@@ -140,6 +140,70 @@ describe('Chip', () => {
|
|
|
140
140
|
expect(chip.hasAttribute('data-clickable')).toBe(true)
|
|
141
141
|
})
|
|
142
142
|
|
|
143
|
+
it('should be focusable when clickable', async () => {
|
|
144
|
+
const el = (
|
|
145
|
+
<div>
|
|
146
|
+
<Chip clickable>Clickable</Chip>
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
const chip = el.firstElementChild as JSX.Element
|
|
150
|
+
chip.updateComponent()
|
|
151
|
+
await flushUpdates()
|
|
152
|
+
expect(chip.getAttribute('tabindex')).toBe('0')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('should be focusable when onclick handler is provided', async () => {
|
|
156
|
+
const el = (
|
|
157
|
+
<div>
|
|
158
|
+
<Chip onclick={() => {}}>Click Handler</Chip>
|
|
159
|
+
</div>
|
|
160
|
+
)
|
|
161
|
+
const chip = el.firstElementChild as JSX.Element
|
|
162
|
+
chip.updateComponent()
|
|
163
|
+
await flushUpdates()
|
|
164
|
+
expect(chip.getAttribute('tabindex')).toBe('0')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('should not be focusable when clickable but disabled', async () => {
|
|
168
|
+
const el = (
|
|
169
|
+
<div>
|
|
170
|
+
<Chip clickable disabled>
|
|
171
|
+
Disabled Clickable
|
|
172
|
+
</Chip>
|
|
173
|
+
</div>
|
|
174
|
+
)
|
|
175
|
+
const chip = el.firstElementChild as JSX.Element
|
|
176
|
+
chip.updateComponent()
|
|
177
|
+
await flushUpdates()
|
|
178
|
+
expect(chip.hasAttribute('tabindex')).toBe(false)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should not be focusable when not clickable', async () => {
|
|
182
|
+
const el = (
|
|
183
|
+
<div>
|
|
184
|
+
<Chip>Not Clickable</Chip>
|
|
185
|
+
</div>
|
|
186
|
+
)
|
|
187
|
+
const chip = el.firstElementChild as JSX.Element
|
|
188
|
+
chip.updateComponent()
|
|
189
|
+
await flushUpdates()
|
|
190
|
+
expect(chip.hasAttribute('tabindex')).toBe(false)
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('should have a focusable delete button', async () => {
|
|
194
|
+
const onDelete = vi.fn()
|
|
195
|
+
const el = (
|
|
196
|
+
<div>
|
|
197
|
+
<Chip onDelete={onDelete}>Deletable</Chip>
|
|
198
|
+
</div>
|
|
199
|
+
)
|
|
200
|
+
const chip = el.firstElementChild as JSX.Element
|
|
201
|
+
chip.updateComponent()
|
|
202
|
+
await flushUpdates()
|
|
203
|
+
const deleteBtn = chip.querySelector('.chip-delete') as HTMLElement
|
|
204
|
+
expect(deleteBtn.getAttribute('tabindex')).toBe('0')
|
|
205
|
+
})
|
|
206
|
+
|
|
143
207
|
it('should set CSS custom properties for palette color', async () => {
|
|
144
208
|
const el = (
|
|
145
209
|
<div>
|
package/src/components/chip.tsx
CHANGED
|
@@ -152,11 +152,13 @@ export const Chip = Shade<ChipProps>({
|
|
|
152
152
|
const { variant, color, size, disabled, clickable, onDelete, style, ...rest } = props
|
|
153
153
|
|
|
154
154
|
const colors = color ? paletteFullColors[color] : defaultColors
|
|
155
|
+
const isClickable = clickable || rest.onclick
|
|
155
156
|
useHostProps({
|
|
156
157
|
'data-variant': variant || undefined,
|
|
157
158
|
'data-size': size === 'small' ? 'small' : undefined,
|
|
158
159
|
'data-disabled': disabled ? '' : undefined,
|
|
159
|
-
'data-clickable':
|
|
160
|
+
'data-clickable': isClickable ? '' : undefined,
|
|
161
|
+
tabIndex: isClickable && !disabled ? 0 : undefined,
|
|
160
162
|
style: {
|
|
161
163
|
'--chip-color-main': colors.main,
|
|
162
164
|
'--chip-color-main-contrast': colors.mainContrast,
|
|
@@ -174,6 +176,7 @@ export const Chip = Shade<ChipProps>({
|
|
|
174
176
|
<span
|
|
175
177
|
className="chip-delete"
|
|
176
178
|
role="button"
|
|
179
|
+
tabIndex={0}
|
|
177
180
|
onclick={(ev: MouseEvent) => {
|
|
178
181
|
ev.stopPropagation()
|
|
179
182
|
onDelete(ev)
|
|
@@ -121,12 +121,16 @@ describe('CommandPalette', () => {
|
|
|
121
121
|
})
|
|
122
122
|
|
|
123
123
|
describe('keyboard navigation', () => {
|
|
124
|
-
const
|
|
125
|
-
const event = new KeyboardEvent('
|
|
124
|
+
const triggerKeydown = (input: HTMLInputElement, key: string) => {
|
|
125
|
+
const event = new KeyboardEvent('keydown', { key, bubbles: true })
|
|
126
126
|
Object.defineProperty(event, 'target', { value: input, writable: false })
|
|
127
127
|
input.dispatchEvent(event)
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
const triggerInput = (input: HTMLInputElement) => {
|
|
131
|
+
input.dispatchEvent(new Event('input', { bubbles: true }))
|
|
132
|
+
}
|
|
133
|
+
|
|
130
134
|
const getSuggestionItems = (commandPalette: HTMLElement) => {
|
|
131
135
|
const suggestionList = commandPalette.querySelector('shade-command-palette-suggestion-list') as HTMLElement
|
|
132
136
|
return suggestionList?.querySelectorAll('.suggestion-item') || []
|
|
@@ -155,13 +159,13 @@ describe('CommandPalette', () => {
|
|
|
155
159
|
|
|
156
160
|
// Open and trigger suggestions
|
|
157
161
|
input.value = 'test'
|
|
158
|
-
|
|
162
|
+
triggerInput(input)
|
|
159
163
|
|
|
160
164
|
await vi.advanceTimersByTimeAsync(300)
|
|
161
165
|
await flushUpdates()
|
|
162
166
|
|
|
163
167
|
// Press ArrowDown
|
|
164
|
-
|
|
168
|
+
triggerKeydown(input, 'ArrowDown')
|
|
165
169
|
await flushUpdates()
|
|
166
170
|
|
|
167
171
|
const suggestionItems = getSuggestionItems(commandPalette)
|
|
@@ -192,15 +196,15 @@ describe('CommandPalette', () => {
|
|
|
192
196
|
|
|
193
197
|
// Open and trigger suggestions
|
|
194
198
|
input.value = 'test'
|
|
195
|
-
|
|
199
|
+
triggerInput(input)
|
|
196
200
|
|
|
197
201
|
await vi.advanceTimersByTimeAsync(300)
|
|
198
202
|
await flushUpdates()
|
|
199
203
|
|
|
200
204
|
// Navigate down then up
|
|
201
|
-
|
|
205
|
+
triggerKeydown(input, 'ArrowDown')
|
|
202
206
|
await flushUpdates()
|
|
203
|
-
|
|
207
|
+
triggerKeydown(input, 'ArrowUp')
|
|
204
208
|
await flushUpdates()
|
|
205
209
|
|
|
206
210
|
const suggestionItems = getSuggestionItems(commandPalette)
|
|
@@ -226,15 +230,15 @@ describe('CommandPalette', () => {
|
|
|
226
230
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
227
231
|
|
|
228
232
|
input.value = 'test'
|
|
229
|
-
|
|
233
|
+
triggerInput(input)
|
|
230
234
|
|
|
231
235
|
await vi.advanceTimersByTimeAsync(300)
|
|
232
236
|
await flushUpdates()
|
|
233
237
|
|
|
234
238
|
// Press ArrowDown multiple times
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
239
|
+
triggerKeydown(input, 'ArrowDown')
|
|
240
|
+
triggerKeydown(input, 'ArrowDown')
|
|
241
|
+
triggerKeydown(input, 'ArrowDown')
|
|
238
242
|
await flushUpdates()
|
|
239
243
|
|
|
240
244
|
const suggestionItems = getSuggestionItems(commandPalette)
|
|
@@ -260,13 +264,13 @@ describe('CommandPalette', () => {
|
|
|
260
264
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
261
265
|
|
|
262
266
|
input.value = 'test'
|
|
263
|
-
|
|
267
|
+
triggerInput(input)
|
|
264
268
|
|
|
265
269
|
await vi.advanceTimersByTimeAsync(300)
|
|
266
270
|
await flushUpdates()
|
|
267
271
|
|
|
268
272
|
// Press ArrowUp when already at first item
|
|
269
|
-
|
|
273
|
+
triggerKeydown(input, 'ArrowUp')
|
|
270
274
|
await flushUpdates()
|
|
271
275
|
|
|
272
276
|
const suggestionItems = getSuggestionItems(commandPalette)
|
|
@@ -293,13 +297,13 @@ describe('CommandPalette', () => {
|
|
|
293
297
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
294
298
|
|
|
295
299
|
input.value = 'test'
|
|
296
|
-
|
|
300
|
+
triggerInput(input)
|
|
297
301
|
|
|
298
302
|
await vi.advanceTimersByTimeAsync(300)
|
|
299
303
|
await flushUpdates()
|
|
300
304
|
|
|
301
305
|
// Press Enter
|
|
302
|
-
|
|
306
|
+
triggerKeydown(input, 'Enter')
|
|
303
307
|
await flushUpdates()
|
|
304
308
|
|
|
305
309
|
expect(onSelected).toHaveBeenCalledTimes(1)
|
|
@@ -309,12 +313,16 @@ describe('CommandPalette', () => {
|
|
|
309
313
|
})
|
|
310
314
|
|
|
311
315
|
describe('selection', () => {
|
|
312
|
-
const
|
|
313
|
-
const event = new KeyboardEvent('
|
|
316
|
+
const triggerKeydown = (input: HTMLInputElement, key: string) => {
|
|
317
|
+
const event = new KeyboardEvent('keydown', { key, bubbles: true })
|
|
314
318
|
Object.defineProperty(event, 'target', { value: input, writable: false })
|
|
315
319
|
input.dispatchEvent(event)
|
|
316
320
|
}
|
|
317
321
|
|
|
322
|
+
const triggerInput = (input: HTMLInputElement) => {
|
|
323
|
+
input.dispatchEvent(new Event('input', { bubbles: true }))
|
|
324
|
+
}
|
|
325
|
+
|
|
318
326
|
const getSuggestionItems = (commandPalette: HTMLElement) => {
|
|
319
327
|
const suggestionList = commandPalette.querySelector('shade-command-palette-suggestion-list') as HTMLElement
|
|
320
328
|
return suggestionList?.querySelectorAll('.suggestion-item') || []
|
|
@@ -345,7 +353,7 @@ describe('CommandPalette', () => {
|
|
|
345
353
|
|
|
346
354
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
347
355
|
input.value = 'test'
|
|
348
|
-
|
|
356
|
+
triggerInput(input)
|
|
349
357
|
|
|
350
358
|
await vi.advanceTimersByTimeAsync(300)
|
|
351
359
|
await flushUpdates()
|
|
@@ -386,12 +394,13 @@ describe('CommandPalette', () => {
|
|
|
386
394
|
|
|
387
395
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
388
396
|
input.value = 'test'
|
|
389
|
-
|
|
397
|
+
triggerInput(input)
|
|
390
398
|
|
|
399
|
+
await vi.advanceTimersByTimeAsync(300)
|
|
391
400
|
await flushUpdates()
|
|
392
401
|
|
|
393
402
|
// Select via Enter
|
|
394
|
-
|
|
403
|
+
triggerKeydown(input, 'Enter')
|
|
395
404
|
await flushUpdates()
|
|
396
405
|
|
|
397
406
|
expect(commandPalette.hasAttribute('data-opened')).toBe(false)
|
|
@@ -400,10 +409,8 @@ describe('CommandPalette', () => {
|
|
|
400
409
|
})
|
|
401
410
|
|
|
402
411
|
describe('command providers', () => {
|
|
403
|
-
const
|
|
404
|
-
|
|
405
|
-
Object.defineProperty(event, 'target', { value: input, writable: false })
|
|
406
|
-
input.dispatchEvent(event)
|
|
412
|
+
const triggerInput = (input: HTMLInputElement) => {
|
|
413
|
+
input.dispatchEvent(new Event('input', { bubbles: true }))
|
|
407
414
|
}
|
|
408
415
|
|
|
409
416
|
const getSuggestionItems = (commandPalette: HTMLElement) => {
|
|
@@ -430,7 +437,7 @@ describe('CommandPalette', () => {
|
|
|
430
437
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
431
438
|
|
|
432
439
|
input.value = 'search'
|
|
433
|
-
|
|
440
|
+
triggerInput(input)
|
|
434
441
|
|
|
435
442
|
await vi.advanceTimersByTimeAsync(300)
|
|
436
443
|
await flushUpdates()
|
|
@@ -459,7 +466,7 @@ describe('CommandPalette', () => {
|
|
|
459
466
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
460
467
|
|
|
461
468
|
input.value = 'search'
|
|
462
|
-
|
|
469
|
+
triggerInput(input)
|
|
463
470
|
|
|
464
471
|
await vi.advanceTimersByTimeAsync(300)
|
|
465
472
|
await flushUpdates()
|
|
@@ -493,7 +500,7 @@ describe('CommandPalette', () => {
|
|
|
493
500
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
494
501
|
|
|
495
502
|
input.value = 'search'
|
|
496
|
-
|
|
503
|
+
triggerInput(input)
|
|
497
504
|
|
|
498
505
|
await vi.advanceTimersByTimeAsync(300)
|
|
499
506
|
await flushUpdates()
|
|
@@ -585,9 +592,7 @@ describe('CommandPalette', () => {
|
|
|
585
592
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
586
593
|
|
|
587
594
|
input.value = 'test'
|
|
588
|
-
|
|
589
|
-
Object.defineProperty(event, 'target', { value: input, writable: false })
|
|
590
|
-
input.dispatchEvent(event)
|
|
595
|
+
input.dispatchEvent(new Event('input', { bubbles: true }))
|
|
591
596
|
|
|
592
597
|
await vi.advanceTimersByTimeAsync(260)
|
|
593
598
|
await flushUpdates()
|
|
@@ -602,6 +607,65 @@ describe('CommandPalette', () => {
|
|
|
602
607
|
})
|
|
603
608
|
})
|
|
604
609
|
|
|
610
|
+
describe('spatial navigation attributes', () => {
|
|
611
|
+
it('should have data-spatial-nav-target on the host element', async () => {
|
|
612
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
613
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
614
|
+
|
|
615
|
+
initializeShadeRoot({
|
|
616
|
+
injector,
|
|
617
|
+
rootElement,
|
|
618
|
+
jsxElement: <CommandPalette commandProviders={[]} defaultPrefix=">" />,
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
await flushUpdates()
|
|
622
|
+
|
|
623
|
+
const commandPalette = document.querySelector('shade-command-palette') as HTMLElement
|
|
624
|
+
expect(commandPalette.hasAttribute('data-spatial-nav-target')).toBe(true)
|
|
625
|
+
})
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
it('should have tabIndex of -1 on the host element', async () => {
|
|
629
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
630
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
631
|
+
|
|
632
|
+
initializeShadeRoot({
|
|
633
|
+
injector,
|
|
634
|
+
rootElement,
|
|
635
|
+
jsxElement: <CommandPalette commandProviders={[]} defaultPrefix=">" />,
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
await flushUpdates()
|
|
639
|
+
|
|
640
|
+
const commandPalette = document.querySelector('shade-command-palette') as HTMLElement
|
|
641
|
+
expect(commandPalette.tabIndex).toBe(-1)
|
|
642
|
+
})
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
it('should delegate focus to the inner input when the host is focused', async () => {
|
|
646
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
647
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
648
|
+
|
|
649
|
+
initializeShadeRoot({
|
|
650
|
+
injector,
|
|
651
|
+
rootElement,
|
|
652
|
+
jsxElement: <CommandPalette commandProviders={[]} defaultPrefix=">" />,
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
await flushUpdates()
|
|
656
|
+
|
|
657
|
+
const commandPalette = document.querySelector('shade-command-palette') as HTMLElement
|
|
658
|
+
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
659
|
+
|
|
660
|
+
commandPalette.dispatchEvent(new FocusEvent('focus', { bubbles: false }))
|
|
661
|
+
|
|
662
|
+
await flushUpdates()
|
|
663
|
+
|
|
664
|
+
expect(document.activeElement).toBe(input)
|
|
665
|
+
})
|
|
666
|
+
})
|
|
667
|
+
})
|
|
668
|
+
|
|
605
669
|
describe('click away', () => {
|
|
606
670
|
it('should close when clicking outside the component', async () => {
|
|
607
671
|
await usingAsync(new Injector(), async (injector) => {
|
|
@@ -681,9 +745,7 @@ describe('CommandPalette', () => {
|
|
|
681
745
|
|
|
682
746
|
const input = commandPalette.querySelector('input') as HTMLInputElement
|
|
683
747
|
input.value = 'test'
|
|
684
|
-
|
|
685
|
-
Object.defineProperty(event, 'target', { value: input, writable: false })
|
|
686
|
-
input.dispatchEvent(event)
|
|
748
|
+
input.dispatchEvent(new Event('input', { bubbles: true }))
|
|
687
749
|
|
|
688
750
|
await flushUpdates()
|
|
689
751
|
|