@furystack/shades-common-components 10.0.35 → 11.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 +66 -0
- package/esm/components/animations.spec.d.ts +2 -0
- package/esm/components/animations.spec.d.ts.map +1 -0
- package/esm/components/animations.spec.js +201 -0
- package/esm/components/animations.spec.js.map +1 -0
- package/esm/components/app-bar-link.js +21 -20
- package/esm/components/app-bar-link.js.map +1 -1
- package/esm/components/app-bar-link.spec.d.ts +2 -0
- package/esm/components/app-bar-link.spec.d.ts.map +1 -0
- package/esm/components/app-bar-link.spec.js +252 -0
- package/esm/components/app-bar-link.spec.js.map +1 -0
- package/esm/components/app-bar.js +21 -21
- package/esm/components/app-bar.js.map +1 -1
- package/esm/components/app-bar.spec.d.ts +2 -0
- package/esm/components/app-bar.spec.d.ts.map +1 -0
- package/esm/components/app-bar.spec.js +117 -0
- package/esm/components/app-bar.spec.js.map +1 -0
- package/esm/components/avatar.d.ts.map +1 -1
- package/esm/components/avatar.js +15 -19
- package/esm/components/avatar.js.map +1 -1
- package/esm/components/avatar.spec.d.ts +2 -0
- package/esm/components/avatar.spec.d.ts.map +1 -0
- package/esm/components/avatar.spec.js +114 -0
- package/esm/components/avatar.spec.js.map +1 -0
- package/esm/components/button.d.ts.map +1 -1
- package/esm/components/button.js +145 -156
- package/esm/components/button.js.map +1 -1
- package/esm/components/button.spec.d.ts +2 -0
- package/esm/components/button.spec.d.ts.map +1 -0
- package/esm/components/button.spec.js +155 -0
- package/esm/components/button.spec.js.map +1 -0
- package/esm/components/command-palette/command-palette-input.d.ts.map +1 -1
- package/esm/components/command-palette/command-palette-input.js +18 -16
- package/esm/components/command-palette/command-palette-input.js.map +1 -1
- package/esm/components/command-palette/command-palette-input.spec.d.ts +2 -0
- package/esm/components/command-palette/command-palette-input.spec.d.ts.map +1 -0
- package/esm/components/command-palette/command-palette-input.spec.js +233 -0
- package/esm/components/command-palette/command-palette-input.spec.js.map +1 -0
- package/esm/components/command-palette/command-palette-manager.spec.d.ts +2 -0
- package/esm/components/command-palette/command-palette-manager.spec.d.ts.map +1 -0
- package/esm/components/command-palette/command-palette-manager.spec.js +362 -0
- package/esm/components/command-palette/command-palette-manager.spec.js.map +1 -0
- package/esm/components/command-palette/command-palette-suggestion-list.d.ts.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.js +42 -46
- package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.spec.d.ts +2 -0
- package/esm/components/command-palette/command-palette-suggestion-list.spec.d.ts.map +1 -0
- package/esm/components/command-palette/command-palette-suggestion-list.spec.js +376 -0
- package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -0
- package/esm/components/command-palette/index.d.ts.map +1 -1
- package/esm/components/command-palette/index.js +100 -110
- package/esm/components/command-palette/index.js.map +1 -1
- package/esm/components/command-palette/index.spec.d.ts +2 -0
- package/esm/components/command-palette/index.spec.d.ts.map +1 -0
- package/esm/components/command-palette/index.spec.js +509 -0
- package/esm/components/command-palette/index.spec.js.map +1 -0
- package/esm/components/data-grid/body.js +1 -1
- package/esm/components/data-grid/body.js.map +1 -1
- package/esm/components/data-grid/body.spec.d.ts +2 -0
- package/esm/components/data-grid/body.spec.d.ts.map +1 -0
- package/esm/components/data-grid/body.spec.js +228 -0
- package/esm/components/data-grid/body.spec.js.map +1 -0
- package/esm/components/data-grid/data-grid-row.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid-row.js +49 -73
- package/esm/components/data-grid/data-grid-row.js.map +1 -1
- package/esm/components/data-grid/data-grid-row.spec.d.ts +2 -0
- package/esm/components/data-grid/data-grid-row.spec.d.ts.map +1 -0
- package/esm/components/data-grid/data-grid-row.spec.js +296 -0
- package/esm/components/data-grid/data-grid-row.spec.js.map +1 -0
- package/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid.js +35 -28
- package/esm/components/data-grid/data-grid.js.map +1 -1
- package/esm/components/data-grid/data-grid.spec.d.ts +2 -0
- package/esm/components/data-grid/data-grid.spec.d.ts.map +1 -0
- package/esm/components/data-grid/data-grid.spec.js +544 -0
- package/esm/components/data-grid/data-grid.spec.js.map +1 -0
- package/esm/components/data-grid/footer.js +21 -15
- package/esm/components/data-grid/footer.js.map +1 -1
- package/esm/components/data-grid/footer.spec.d.ts +2 -0
- package/esm/components/data-grid/footer.spec.d.ts.map +1 -0
- package/esm/components/data-grid/footer.spec.js +264 -0
- package/esm/components/data-grid/footer.spec.js.map +1 -0
- package/esm/components/data-grid/header.d.ts.map +1 -1
- package/esm/components/data-grid/header.js +55 -33
- package/esm/components/data-grid/header.js.map +1 -1
- package/esm/components/data-grid/header.spec.d.ts +2 -0
- package/esm/components/data-grid/header.spec.d.ts.map +1 -0
- package/esm/components/data-grid/header.spec.js +421 -0
- package/esm/components/data-grid/header.spec.js.map +1 -0
- package/esm/components/data-grid/selection-cell.d.ts.map +1 -1
- package/esm/components/data-grid/selection-cell.js +13 -6
- package/esm/components/data-grid/selection-cell.js.map +1 -1
- package/esm/components/data-grid/selection-cell.spec.d.ts +2 -0
- package/esm/components/data-grid/selection-cell.spec.d.ts.map +1 -0
- package/esm/components/data-grid/selection-cell.spec.js +118 -0
- package/esm/components/data-grid/selection-cell.spec.js.map +1 -0
- package/esm/components/fab.d.ts.map +1 -1
- package/esm/components/fab.js +10 -1
- package/esm/components/fab.js.map +1 -1
- package/esm/components/fab.spec.d.ts +2 -0
- package/esm/components/fab.spec.d.ts.map +1 -0
- package/esm/components/fab.spec.js +95 -0
- package/esm/components/fab.spec.js.map +1 -0
- package/esm/components/form.spec.d.ts +2 -0
- package/esm/components/form.spec.d.ts.map +1 -0
- package/esm/components/form.spec.js +314 -0
- package/esm/components/form.spec.js.map +1 -0
- package/esm/components/grid.d.ts.map +1 -1
- package/esm/components/grid.js +40 -37
- package/esm/components/grid.js.map +1 -1
- package/esm/components/grid.spec.d.ts +2 -0
- package/esm/components/grid.spec.d.ts.map +1 -0
- package/esm/components/grid.spec.js +316 -0
- package/esm/components/grid.spec.js.map +1 -0
- package/esm/components/inputs/autocomplete.spec.d.ts +2 -0
- package/esm/components/inputs/autocomplete.spec.d.ts.map +1 -0
- package/esm/components/inputs/autocomplete.spec.js +194 -0
- package/esm/components/inputs/autocomplete.spec.js.map +1 -0
- package/esm/components/inputs/input.d.ts.map +1 -1
- package/esm/components/inputs/input.js +141 -109
- package/esm/components/inputs/input.js.map +1 -1
- package/esm/components/inputs/input.spec.d.ts +2 -0
- package/esm/components/inputs/input.spec.d.ts.map +1 -0
- package/esm/components/inputs/input.spec.js +577 -0
- package/esm/components/inputs/input.spec.js.map +1 -0
- package/esm/components/inputs/text-area.d.ts.map +1 -1
- package/esm/components/inputs/text-area.js +54 -58
- package/esm/components/inputs/text-area.js.map +1 -1
- package/esm/components/inputs/text-area.spec.d.ts +2 -0
- package/esm/components/inputs/text-area.spec.d.ts.map +1 -0
- package/esm/components/inputs/text-area.spec.js +214 -0
- package/esm/components/inputs/text-area.spec.js.map +1 -0
- package/esm/components/loader.js +1 -1
- package/esm/components/loader.js.map +1 -1
- package/esm/components/loader.spec.d.ts +2 -0
- package/esm/components/loader.spec.d.ts.map +1 -0
- package/esm/components/loader.spec.js +251 -0
- package/esm/components/loader.spec.js.map +1 -0
- package/esm/components/modal.d.ts.map +1 -1
- package/esm/components/modal.js +11 -9
- package/esm/components/modal.js.map +1 -1
- package/esm/components/modal.spec.d.ts +2 -0
- package/esm/components/modal.spec.d.ts.map +1 -0
- package/esm/components/modal.spec.js +227 -0
- package/esm/components/modal.spec.js.map +1 -0
- package/esm/components/noty-list.d.ts.map +1 -1
- package/esm/components/noty-list.js +39 -40
- package/esm/components/noty-list.js.map +1 -1
- package/esm/components/noty-list.spec.d.ts +2 -0
- package/esm/components/noty-list.spec.d.ts.map +1 -0
- package/esm/components/noty-list.spec.js +486 -0
- package/esm/components/noty-list.spec.js.map +1 -0
- package/esm/components/paper.d.ts.map +1 -1
- package/esm/components/paper.js +15 -12
- package/esm/components/paper.js.map +1 -1
- package/esm/components/paper.spec.d.ts +2 -0
- package/esm/components/paper.spec.d.ts.map +1 -0
- package/esm/components/paper.spec.js +63 -0
- package/esm/components/paper.spec.js.map +1 -0
- package/esm/components/skeleton.js +1 -1
- package/esm/components/skeleton.js.map +1 -1
- package/esm/components/skeleton.spec.d.ts +2 -0
- package/esm/components/skeleton.spec.d.ts.map +1 -0
- package/esm/components/skeleton.spec.js +159 -0
- package/esm/components/skeleton.spec.js.map +1 -0
- package/esm/components/styles.spec.d.ts +2 -0
- package/esm/components/styles.spec.d.ts.map +1 -0
- package/esm/components/styles.spec.js +56 -0
- package/esm/components/styles.spec.js.map +1 -0
- package/esm/components/suggest/index.d.ts.map +1 -1
- package/esm/components/suggest/index.js +74 -83
- package/esm/components/suggest/index.js.map +1 -1
- package/esm/components/suggest/index.spec.d.ts +2 -0
- package/esm/components/suggest/index.spec.d.ts.map +1 -0
- package/esm/components/suggest/index.spec.js +515 -0
- package/esm/components/suggest/index.spec.js.map +1 -0
- package/esm/components/suggest/suggest-input.d.ts.map +1 -1
- package/esm/components/suggest/suggest-input.js +16 -17
- package/esm/components/suggest/suggest-input.js.map +1 -1
- package/esm/components/suggest/suggest-input.spec.d.ts +2 -0
- package/esm/components/suggest/suggest-input.spec.d.ts.map +1 -0
- package/esm/components/suggest/suggest-input.spec.js +138 -0
- package/esm/components/suggest/suggest-input.spec.js.map +1 -0
- package/esm/components/suggest/suggest-manager.spec.d.ts +2 -0
- package/esm/components/suggest/suggest-manager.spec.d.ts.map +1 -0
- package/esm/components/suggest/suggest-manager.spec.js +308 -0
- package/esm/components/suggest/suggest-manager.spec.js.map +1 -0
- package/esm/components/suggest/suggestion-list.d.ts.map +1 -1
- package/esm/components/suggest/suggestion-list.js +43 -48
- package/esm/components/suggest/suggestion-list.js.map +1 -1
- package/esm/components/suggest/suggestion-list.spec.d.ts +2 -0
- package/esm/components/suggest/suggestion-list.spec.d.ts.map +1 -0
- package/esm/components/suggest/suggestion-list.spec.js +252 -0
- package/esm/components/suggest/suggestion-list.spec.js.map +1 -0
- package/esm/components/tabs.d.ts.map +1 -1
- package/esm/components/tabs.js +32 -18
- package/esm/components/tabs.js.map +1 -1
- package/esm/components/tabs.spec.d.ts +2 -0
- package/esm/components/tabs.spec.d.ts.map +1 -0
- package/esm/components/tabs.spec.js +187 -0
- package/esm/components/tabs.spec.js.map +1 -0
- package/esm/components/wizard/index.d.ts.map +1 -1
- package/esm/components/wizard/index.js +10 -7
- package/esm/components/wizard/index.js.map +1 -1
- package/esm/components/wizard/index.spec.d.ts +2 -0
- package/esm/components/wizard/index.spec.d.ts.map +1 -0
- package/esm/components/wizard/index.spec.js +171 -0
- package/esm/components/wizard/index.spec.js.map +1 -0
- package/esm/services/collection-service.spec.js +391 -2
- package/esm/services/collection-service.spec.js.map +1 -1
- package/esm/services/css-variable-theme.d.ts.map +1 -1
- package/esm/services/css-variable-theme.js +21 -1
- package/esm/services/css-variable-theme.js.map +1 -1
- package/esm/services/css-variable-theme.spec.d.ts +2 -0
- package/esm/services/css-variable-theme.spec.d.ts.map +1 -0
- package/esm/services/css-variable-theme.spec.js +169 -0
- package/esm/services/css-variable-theme.spec.js.map +1 -0
- package/esm/services/default-palette.d.ts +4 -0
- package/esm/services/default-palette.d.ts.map +1 -1
- package/esm/services/default-palette.js +22 -0
- package/esm/services/default-palette.js.map +1 -1
- package/esm/services/theme-provider-service.d.ts +59 -1
- package/esm/services/theme-provider-service.d.ts.map +1 -1
- package/esm/services/theme-provider-service.js.map +1 -1
- package/esm/services/theme-provider-service.spec.d.ts +2 -0
- package/esm/services/theme-provider-service.spec.d.ts.map +1 -0
- package/esm/services/theme-provider-service.spec.js +166 -0
- package/esm/services/theme-provider-service.spec.js.map +1 -0
- package/package.json +2 -2
- package/src/components/animations.spec.ts +299 -0
- package/src/components/app-bar-link.spec.tsx +341 -0
- package/src/components/app-bar-link.tsx +21 -21
- package/src/components/app-bar.spec.tsx +142 -0
- package/src/components/app-bar.tsx +22 -22
- package/src/components/avatar.spec.tsx +146 -0
- package/src/components/avatar.tsx +17 -20
- package/src/components/button.spec.tsx +193 -0
- package/src/components/button.tsx +162 -197
- package/src/components/command-palette/command-palette-input.spec.tsx +320 -0
- package/src/components/command-palette/command-palette-input.tsx +19 -22
- package/src/components/command-palette/command-palette-manager.spec.ts +470 -0
- package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +499 -0
- package/src/components/command-palette/command-palette-suggestion-list.tsx +42 -46
- package/src/components/command-palette/index.spec.tsx +684 -0
- package/src/components/command-palette/index.tsx +107 -136
- package/src/components/data-grid/body.spec.tsx +340 -0
- package/src/components/data-grid/body.tsx +1 -1
- package/src/components/data-grid/data-grid-row.spec.tsx +382 -0
- package/src/components/data-grid/data-grid-row.tsx +50 -82
- package/src/components/data-grid/data-grid.spec.tsx +939 -0
- package/src/components/data-grid/data-grid.tsx +38 -35
- package/src/components/data-grid/footer.spec.tsx +344 -0
- package/src/components/data-grid/footer.tsx +19 -19
- package/src/components/data-grid/header.spec.tsx +563 -0
- package/src/components/data-grid/header.tsx +53 -44
- package/src/components/data-grid/selection-cell.spec.tsx +150 -0
- package/src/components/data-grid/selection-cell.tsx +12 -6
- package/src/components/fab.spec.tsx +108 -0
- package/src/components/fab.tsx +10 -1
- package/src/components/form.spec.tsx +481 -0
- package/src/components/grid.spec.tsx +334 -0
- package/src/components/grid.tsx +57 -63
- package/src/components/inputs/autocomplete.spec.tsx +258 -0
- package/src/components/inputs/input.spec.tsx +808 -0
- package/src/components/inputs/input.tsx +153 -139
- package/src/components/inputs/text-area.spec.tsx +285 -0
- package/src/components/inputs/text-area.tsx +53 -79
- package/src/components/loader.spec.tsx +346 -0
- package/src/components/loader.tsx +1 -1
- package/src/components/modal.spec.tsx +304 -0
- package/src/components/modal.tsx +11 -9
- package/src/components/noty-list.spec.tsx +631 -0
- package/src/components/noty-list.tsx +39 -50
- package/src/components/paper.spec.tsx +72 -0
- package/src/components/paper.tsx +15 -13
- package/src/components/skeleton.spec.tsx +219 -0
- package/src/components/skeleton.tsx +1 -1
- package/src/components/styles.spec.ts +70 -0
- package/src/components/suggest/index.spec.tsx +861 -0
- package/src/components/suggest/index.tsx +74 -101
- package/src/components/suggest/suggest-input.spec.tsx +181 -0
- package/src/components/suggest/suggest-input.tsx +16 -24
- package/src/components/suggest/suggest-manager.spec.ts +409 -0
- package/src/components/suggest/suggestion-list.spec.tsx +334 -0
- package/src/components/suggest/suggestion-list.tsx +43 -48
- package/src/components/tabs.spec.tsx +236 -0
- package/src/components/tabs.tsx +33 -21
- package/src/components/wizard/index.spec.tsx +224 -0
- package/src/components/wizard/index.tsx +10 -9
- package/src/services/collection-service.spec.ts +492 -3
- package/src/services/css-variable-theme.spec.ts +204 -0
- package/src/services/css-variable-theme.ts +21 -1
- package/src/services/default-palette.ts +22 -0
- package/src/services/theme-provider-service.spec.ts +195 -0
- package/src/services/theme-provider-service.ts +60 -2
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { Injector } from '@furystack/inject'
|
|
2
|
+
import { createComponent, initializeShadeRoot, LocationService } from '@furystack/shades'
|
|
3
|
+
import { sleepAsync, usingAsync } from '@furystack/utils'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import { AppBarLink } from './app-bar-link.js'
|
|
6
|
+
|
|
7
|
+
describe('AppBarLink component', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
document.body.innerHTML = '<div id="root"></div>'
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
document.body.innerHTML = ''
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('rendering', () => {
|
|
17
|
+
it('should render the shade-app-bar-link custom element', async () => {
|
|
18
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
19
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
20
|
+
|
|
21
|
+
initializeShadeRoot({
|
|
22
|
+
injector,
|
|
23
|
+
rootElement,
|
|
24
|
+
jsxElement: <AppBarLink href="/test">Link</AppBarLink>,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
await sleepAsync(50)
|
|
28
|
+
|
|
29
|
+
const appBarLink = document.querySelector('shade-app-bar-link')
|
|
30
|
+
expect(appBarLink).not.toBeNull()
|
|
31
|
+
expect(appBarLink?.tagName.toLowerCase()).toBe('shade-app-bar-link')
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should render children through RouteLink', async () => {
|
|
36
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
37
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
38
|
+
|
|
39
|
+
initializeShadeRoot({
|
|
40
|
+
injector,
|
|
41
|
+
rootElement,
|
|
42
|
+
jsxElement: <AppBarLink href="/test">Test Link Text</AppBarLink>,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
await sleepAsync(50)
|
|
46
|
+
|
|
47
|
+
expect(document.body.innerHTML).toContain('Test Link Text')
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should render RouteLink with correct href', async () => {
|
|
52
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
53
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
54
|
+
|
|
55
|
+
initializeShadeRoot({
|
|
56
|
+
injector,
|
|
57
|
+
rootElement,
|
|
58
|
+
jsxElement: <AppBarLink href="/my-route">Link</AppBarLink>,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
await sleepAsync(50)
|
|
62
|
+
|
|
63
|
+
expect(document.body.innerHTML).toContain('href="/my-route"')
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('route matching', () => {
|
|
69
|
+
it('should have active class when current URL matches href', async () => {
|
|
70
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
71
|
+
history.pushState(null, '', '/dashboard')
|
|
72
|
+
|
|
73
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
74
|
+
|
|
75
|
+
initializeShadeRoot({
|
|
76
|
+
injector,
|
|
77
|
+
rootElement,
|
|
78
|
+
jsxElement: <AppBarLink href="/dashboard">Dashboard</AppBarLink>,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
await sleepAsync(50)
|
|
82
|
+
|
|
83
|
+
const appBarLink = document.querySelector('shade-app-bar-link')
|
|
84
|
+
expect(appBarLink?.classList.contains('active')).toBe(true)
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('should not have active class when current URL does not match href', async () => {
|
|
89
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
90
|
+
history.pushState(null, '', '/other-page')
|
|
91
|
+
|
|
92
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
93
|
+
|
|
94
|
+
initializeShadeRoot({
|
|
95
|
+
injector,
|
|
96
|
+
rootElement,
|
|
97
|
+
jsxElement: <AppBarLink href="/dashboard">Dashboard</AppBarLink>,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
await sleepAsync(50)
|
|
101
|
+
|
|
102
|
+
const appBarLink = document.querySelector('shade-app-bar-link')
|
|
103
|
+
expect(appBarLink?.classList.contains('active')).toBe(false)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('should update active class when location changes', async () => {
|
|
108
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
109
|
+
history.pushState(null, '', '/home')
|
|
110
|
+
|
|
111
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
112
|
+
|
|
113
|
+
initializeShadeRoot({
|
|
114
|
+
injector,
|
|
115
|
+
rootElement,
|
|
116
|
+
jsxElement: <AppBarLink href="/dashboard">Dashboard</AppBarLink>,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
await sleepAsync(50)
|
|
120
|
+
|
|
121
|
+
const appBarLink = document.querySelector('shade-app-bar-link')
|
|
122
|
+
expect(appBarLink?.classList.contains('active')).toBe(false)
|
|
123
|
+
|
|
124
|
+
// Navigate to matching route
|
|
125
|
+
history.pushState(null, '', '/dashboard')
|
|
126
|
+
injector.getInstance(LocationService).updateState()
|
|
127
|
+
|
|
128
|
+
await sleepAsync(50)
|
|
129
|
+
|
|
130
|
+
expect(appBarLink?.classList.contains('active')).toBe(true)
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('should remove active class when navigating away', async () => {
|
|
135
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
136
|
+
history.pushState(null, '', '/dashboard')
|
|
137
|
+
|
|
138
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
139
|
+
|
|
140
|
+
initializeShadeRoot({
|
|
141
|
+
injector,
|
|
142
|
+
rootElement,
|
|
143
|
+
jsxElement: <AppBarLink href="/dashboard">Dashboard</AppBarLink>,
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
await sleepAsync(50)
|
|
147
|
+
|
|
148
|
+
const appBarLink = document.querySelector('shade-app-bar-link')
|
|
149
|
+
expect(appBarLink?.classList.contains('active')).toBe(true)
|
|
150
|
+
|
|
151
|
+
// Navigate away
|
|
152
|
+
history.pushState(null, '', '/other')
|
|
153
|
+
injector.getInstance(LocationService).updateState()
|
|
154
|
+
|
|
155
|
+
await sleepAsync(50)
|
|
156
|
+
|
|
157
|
+
expect(appBarLink?.classList.contains('active')).toBe(false)
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('should match routes with parameters', async () => {
|
|
162
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
163
|
+
history.pushState(null, '', '/users/123')
|
|
164
|
+
|
|
165
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
166
|
+
|
|
167
|
+
initializeShadeRoot({
|
|
168
|
+
injector,
|
|
169
|
+
rootElement,
|
|
170
|
+
jsxElement: <AppBarLink href="/users/:id">Users</AppBarLink>,
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
await sleepAsync(50)
|
|
174
|
+
|
|
175
|
+
const appBarLink = document.querySelector('shade-app-bar-link')
|
|
176
|
+
expect(appBarLink?.classList.contains('active')).toBe(true)
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('should support routingOptions with end: false for prefix matching', async () => {
|
|
181
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
182
|
+
history.pushState(null, '', '/admin/settings/security')
|
|
183
|
+
|
|
184
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
185
|
+
|
|
186
|
+
initializeShadeRoot({
|
|
187
|
+
injector,
|
|
188
|
+
rootElement,
|
|
189
|
+
jsxElement: (
|
|
190
|
+
<AppBarLink href="/admin" routingOptions={{ end: false }}>
|
|
191
|
+
Admin
|
|
192
|
+
</AppBarLink>
|
|
193
|
+
),
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
await sleepAsync(50)
|
|
197
|
+
|
|
198
|
+
const appBarLink = document.querySelector('shade-app-bar-link')
|
|
199
|
+
expect(appBarLink?.classList.contains('active')).toBe(true)
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('should not match prefix by default (end: true)', async () => {
|
|
204
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
205
|
+
history.pushState(null, '', '/admin/settings')
|
|
206
|
+
|
|
207
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
208
|
+
|
|
209
|
+
initializeShadeRoot({
|
|
210
|
+
injector,
|
|
211
|
+
rootElement,
|
|
212
|
+
jsxElement: <AppBarLink href="/admin">Admin</AppBarLink>,
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
await sleepAsync(50)
|
|
216
|
+
|
|
217
|
+
const appBarLink = document.querySelector('shade-app-bar-link')
|
|
218
|
+
expect(appBarLink?.classList.contains('active')).toBe(false)
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
describe('multiple links', () => {
|
|
224
|
+
it('should only activate the matching link', async () => {
|
|
225
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
226
|
+
history.pushState(null, '', '/settings')
|
|
227
|
+
|
|
228
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
229
|
+
|
|
230
|
+
initializeShadeRoot({
|
|
231
|
+
injector,
|
|
232
|
+
rootElement,
|
|
233
|
+
jsxElement: (
|
|
234
|
+
<div>
|
|
235
|
+
<AppBarLink href="/home">Home</AppBarLink>
|
|
236
|
+
<AppBarLink href="/settings">Settings</AppBarLink>
|
|
237
|
+
<AppBarLink href="/about">About</AppBarLink>
|
|
238
|
+
</div>
|
|
239
|
+
),
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
await sleepAsync(50)
|
|
243
|
+
|
|
244
|
+
const links = document.querySelectorAll('shade-app-bar-link')
|
|
245
|
+
expect(links[0]?.classList.contains('active')).toBe(false)
|
|
246
|
+
expect(links[1]?.classList.contains('active')).toBe(true)
|
|
247
|
+
expect(links[2]?.classList.contains('active')).toBe(false)
|
|
248
|
+
})
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('should update all links when location changes', async () => {
|
|
252
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
253
|
+
history.pushState(null, '', '/home')
|
|
254
|
+
|
|
255
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
256
|
+
|
|
257
|
+
initializeShadeRoot({
|
|
258
|
+
injector,
|
|
259
|
+
rootElement,
|
|
260
|
+
jsxElement: (
|
|
261
|
+
<div>
|
|
262
|
+
<AppBarLink href="/home">Home</AppBarLink>
|
|
263
|
+
<AppBarLink href="/settings">Settings</AppBarLink>
|
|
264
|
+
</div>
|
|
265
|
+
),
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
await sleepAsync(50)
|
|
269
|
+
|
|
270
|
+
const links = document.querySelectorAll('shade-app-bar-link')
|
|
271
|
+
expect(links[0]?.classList.contains('active')).toBe(true)
|
|
272
|
+
expect(links[1]?.classList.contains('active')).toBe(false)
|
|
273
|
+
|
|
274
|
+
// Navigate to settings
|
|
275
|
+
history.pushState(null, '', '/settings')
|
|
276
|
+
injector.getInstance(LocationService).updateState()
|
|
277
|
+
|
|
278
|
+
await sleepAsync(50)
|
|
279
|
+
|
|
280
|
+
expect(links[0]?.classList.contains('active')).toBe(false)
|
|
281
|
+
expect(links[1]?.classList.contains('active')).toBe(true)
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
describe('styling', () => {
|
|
287
|
+
it('should have flex display', async () => {
|
|
288
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
289
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
290
|
+
|
|
291
|
+
initializeShadeRoot({
|
|
292
|
+
injector,
|
|
293
|
+
rootElement,
|
|
294
|
+
jsxElement: <AppBarLink href="/test">Link</AppBarLink>,
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
await sleepAsync(50)
|
|
298
|
+
|
|
299
|
+
const appBarLink = document.querySelector('shade-app-bar-link') as HTMLElement
|
|
300
|
+
const computedStyle = window.getComputedStyle(appBarLink)
|
|
301
|
+
expect(computedStyle.display).toBe('flex')
|
|
302
|
+
})
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('should have pointer cursor', async () => {
|
|
306
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
307
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
308
|
+
|
|
309
|
+
initializeShadeRoot({
|
|
310
|
+
injector,
|
|
311
|
+
rootElement,
|
|
312
|
+
jsxElement: <AppBarLink href="/test">Link</AppBarLink>,
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
await sleepAsync(50)
|
|
316
|
+
|
|
317
|
+
const appBarLink = document.querySelector('shade-app-bar-link') as HTMLElement
|
|
318
|
+
const computedStyle = window.getComputedStyle(appBarLink)
|
|
319
|
+
expect(computedStyle.cursor).toBe('pointer')
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('should have transition for animations', async () => {
|
|
324
|
+
await usingAsync(new Injector(), async (injector) => {
|
|
325
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
326
|
+
|
|
327
|
+
initializeShadeRoot({
|
|
328
|
+
injector,
|
|
329
|
+
rootElement,
|
|
330
|
+
jsxElement: <AppBarLink href="/test">Link</AppBarLink>,
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
await sleepAsync(50)
|
|
334
|
+
|
|
335
|
+
const appBarLink = document.querySelector('shade-app-bar-link') as HTMLElement
|
|
336
|
+
const computedStyle = window.getComputedStyle(appBarLink)
|
|
337
|
+
expect(computedStyle.transition).toContain('color')
|
|
338
|
+
})
|
|
339
|
+
})
|
|
340
|
+
})
|
|
341
|
+
})
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
import type { RouteLinkProps } from '@furystack/shades'
|
|
2
|
-
import {
|
|
2
|
+
import { createComponent, LocationService, RouteLink, Shade } from '@furystack/shades'
|
|
3
3
|
import { match, type MatchOptions } from 'path-to-regexp'
|
|
4
|
-
import {
|
|
4
|
+
import { cssVariableTheme } from '../services/css-variable-theme.js'
|
|
5
5
|
|
|
6
6
|
export const AppBarLink = Shade<RouteLinkProps & { routingOptions?: MatchOptions }>({
|
|
7
7
|
shadowDomName: 'shade-app-bar-link',
|
|
8
|
+
css: {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
height: '100%',
|
|
11
|
+
textDecoration: 'none',
|
|
12
|
+
alignItems: 'center',
|
|
13
|
+
padding: '0 8px',
|
|
14
|
+
transition: 'color .2s ease-in-out, transform .2s ease-in-out',
|
|
15
|
+
cursor: 'pointer',
|
|
16
|
+
color: cssVariableTheme.text.secondary,
|
|
17
|
+
opacity: '0.8',
|
|
18
|
+
transform: 'scale(0.9)',
|
|
19
|
+
'&.active': {
|
|
20
|
+
color: cssVariableTheme.text.primary,
|
|
21
|
+
opacity: '1',
|
|
22
|
+
transform: 'scale(1)',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
8
25
|
render: ({ children, props, useObservable, injector, element }) => {
|
|
9
|
-
const getAnchorStyle = (currentUrl: string) => {
|
|
10
|
-
const isActive = !!match(props.href as string, props.routingOptions)(currentUrl)
|
|
11
|
-
const themeProviderService = injector.getInstance(ThemeProviderService)
|
|
12
|
-
const { theme } = themeProviderService
|
|
13
|
-
return {
|
|
14
|
-
display: 'flex',
|
|
15
|
-
height: '100%',
|
|
16
|
-
textDecoration: 'none',
|
|
17
|
-
alignItems: 'center',
|
|
18
|
-
padding: '0 8px',
|
|
19
|
-
transition: 'color .2s ease-in-out, transform .2s ease-in-out',
|
|
20
|
-
color: isActive ? theme.text.primary : theme.text.secondary,
|
|
21
|
-
opacity: isActive ? '1' : '0.8',
|
|
22
|
-
transform: isActive ? 'scale(1)' : 'scale(0.9)',
|
|
23
|
-
cursor: 'pointer',
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
26
|
const [currentUrl] = useObservable('locationChange', injector.getInstance(LocationService).onLocationPathChanged)
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
const isActive = !!match(props.href as string, props.routingOptions)(currentUrl)
|
|
29
|
+
element.classList.toggle('active', isActive)
|
|
30
30
|
|
|
31
31
|
return <RouteLink {...props}>{children}</RouteLink>
|
|
32
32
|
},
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Injector } from '@furystack/inject'
|
|
2
|
+
import { createComponent, initializeShadeRoot } from '@furystack/shades'
|
|
3
|
+
import { sleepAsync } from '@furystack/utils'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import { AppBar } from './app-bar.js'
|
|
6
|
+
|
|
7
|
+
describe('AppBar component', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
document.body.innerHTML = '<div id="root"></div>'
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
document.body.innerHTML = ''
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const renderAppBar = async (children: JSX.Element) => {
|
|
17
|
+
const injector = new Injector()
|
|
18
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
19
|
+
|
|
20
|
+
initializeShadeRoot({
|
|
21
|
+
injector,
|
|
22
|
+
rootElement,
|
|
23
|
+
jsxElement: <AppBar>{children}</AppBar>,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
await sleepAsync(50)
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
injector,
|
|
30
|
+
appBar: document.querySelector('shade-app-bar') as HTMLElement,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('rendering', () => {
|
|
35
|
+
it('should render the shade-app-bar custom element', async () => {
|
|
36
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
37
|
+
expect(appBar).not.toBeNull()
|
|
38
|
+
expect(appBar.tagName.toLowerCase()).toBe('shade-app-bar')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should render children in shadow DOM', async () => {
|
|
42
|
+
const { appBar } = await renderAppBar(<span id="child-content">Test Content</span>)
|
|
43
|
+
// Children are rendered inside shadow DOM - verify via the element itself
|
|
44
|
+
expect(appBar).not.toBeNull()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should render multiple children', async () => {
|
|
48
|
+
const injector = new Injector()
|
|
49
|
+
const rootElement = document.getElementById('root') as HTMLDivElement
|
|
50
|
+
|
|
51
|
+
initializeShadeRoot({
|
|
52
|
+
injector,
|
|
53
|
+
rootElement,
|
|
54
|
+
jsxElement: (
|
|
55
|
+
<AppBar>
|
|
56
|
+
<span id="logo">Logo</span>
|
|
57
|
+
<nav id="navigation">Nav</nav>
|
|
58
|
+
<button id="action">Action</button>
|
|
59
|
+
</AppBar>
|
|
60
|
+
),
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
await sleepAsync(50)
|
|
64
|
+
|
|
65
|
+
const appBar = document.querySelector('shade-app-bar')
|
|
66
|
+
expect(appBar).not.toBeNull()
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('positioning', () => {
|
|
71
|
+
it('should have fixed positioning', async () => {
|
|
72
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
73
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
74
|
+
expect(computedStyle.position).toBe('fixed')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should have z-index of 1', async () => {
|
|
78
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
79
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
80
|
+
expect(computedStyle.zIndex).toBe('1')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should have full width', async () => {
|
|
84
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
85
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
86
|
+
expect(computedStyle.width).toBe('100%')
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('fade-in animation', () => {
|
|
91
|
+
it('should add visible class after construction', async () => {
|
|
92
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
93
|
+
expect(appBar.classList.contains('visible')).toBe(true)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should have opacity 1 when visible class is applied', async () => {
|
|
97
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
98
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
99
|
+
expect(computedStyle.opacity).toBe('1')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should have transition styles for animation', async () => {
|
|
103
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
104
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
105
|
+
expect(computedStyle.transition).toContain('opacity')
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('layout', () => {
|
|
110
|
+
it('should have flex display', async () => {
|
|
111
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
112
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
113
|
+
expect(computedStyle.display).toBe('flex')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('should align items center', async () => {
|
|
117
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
118
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
119
|
+
expect(computedStyle.alignItems).toBe('center')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('should justify content to flex-start', async () => {
|
|
123
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
124
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
125
|
+
expect(computedStyle.justifyContent).toBe('flex-start')
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('styling', () => {
|
|
130
|
+
it('should have box shadow', async () => {
|
|
131
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
132
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
133
|
+
expect(computedStyle.boxShadow).not.toBe('none')
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should have semi-transparent background', async () => {
|
|
137
|
+
const { appBar } = await renderAppBar(<span>Content</span>)
|
|
138
|
+
const computedStyle = window.getComputedStyle(appBar)
|
|
139
|
+
expect(computedStyle.background).toContain('rgba')
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
})
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { Shade, createComponent
|
|
2
|
-
import {
|
|
1
|
+
import { Shade, createComponent } from '@furystack/shades'
|
|
2
|
+
import { cssVariableTheme } from '../services/css-variable-theme.js'
|
|
3
3
|
|
|
4
4
|
export const AppBar = Shade({
|
|
5
5
|
shadowDomName: 'shade-app-bar',
|
|
6
|
+
css: {
|
|
7
|
+
width: '100%',
|
|
8
|
+
background: 'rgba(128,128,128,0.2)',
|
|
9
|
+
backdropFilter: 'blur(15px)',
|
|
10
|
+
display: 'flex',
|
|
11
|
+
justifyContent: 'flex-start',
|
|
12
|
+
alignItems: 'center',
|
|
13
|
+
boxShadow: '0 0 12px rgba(0,0,0,0.6)',
|
|
14
|
+
transition:
|
|
15
|
+
'opacity .35s cubic-bezier(0.550, 0.085, 0.680, 0.530), padding .2s cubic-bezier(0.550, 0.085, 0.680, 0.530)',
|
|
16
|
+
opacity: '0',
|
|
17
|
+
position: 'fixed',
|
|
18
|
+
zIndex: '1',
|
|
19
|
+
color: cssVariableTheme.text.secondary,
|
|
20
|
+
'&.visible': {
|
|
21
|
+
opacity: '1',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
6
24
|
constructed: ({ element }) => {
|
|
7
25
|
requestAnimationFrame(() => {
|
|
8
|
-
element.
|
|
26
|
+
element.classList.add('visible')
|
|
9
27
|
})
|
|
10
28
|
},
|
|
11
|
-
render: ({ children
|
|
12
|
-
const { theme } = injector.getInstance(ThemeProviderService)
|
|
13
|
-
attachProps(element, {
|
|
14
|
-
style: {
|
|
15
|
-
width: '100%',
|
|
16
|
-
background: 'rgba(128,128,128,0.2)',
|
|
17
|
-
backdropFilter: 'blur(15px)',
|
|
18
|
-
display: 'flex',
|
|
19
|
-
justifyContent: 'flex-start',
|
|
20
|
-
alignItems: 'center',
|
|
21
|
-
boxShadow: '0 0 12px rgba(0,0,0,0.6)',
|
|
22
|
-
transition:
|
|
23
|
-
'opacity .35s cubic-bezier(0.550, 0.085, 0.680, 0.530), padding .2s cubic-bezier(0.550, 0.085, 0.680, 0.530)',
|
|
24
|
-
opacity: '0',
|
|
25
|
-
position: 'fixed',
|
|
26
|
-
zIndex: '1',
|
|
27
|
-
color: theme.text.secondary,
|
|
28
|
-
},
|
|
29
|
-
})
|
|
29
|
+
render: ({ children }) => {
|
|
30
30
|
return <>{children}</>
|
|
31
31
|
},
|
|
32
32
|
})
|