@skewedaspect/sleekspace-ui 0.8.1 → 0.9.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/dist/components/Dropdown/SkDropdown.vue.d.ts +9 -1
- package/dist/components/Dropdown/types.d.ts +2 -1
- package/dist/components/NavBar/SkNavBar.vue.d.ts +9 -1
- package/dist/components/NavBar/context.d.ts +2 -0
- package/dist/components/NavBar/types.d.ts +5 -1
- package/dist/components/Page/SkPage.vue.d.ts +9 -0
- package/dist/components/ScrollArea/SkScrollArea.vue.d.ts +105 -4
- package/dist/composables/useCustomColors.d.ts +18 -56
- package/{src → dist}/global.d.ts +6 -2
- package/dist/sleekspace-ui.css +4253 -1251
- package/dist/sleekspace-ui.es.js +204 -109
- package/dist/sleekspace-ui.umd.js +204 -109
- package/dist/static/classes.d.ts +18 -0
- package/dist/static/components/alert.d.ts +12 -0
- package/dist/static/components/avatar.d.ts +9 -0
- package/dist/static/components/breadcrumbs.d.ts +6 -0
- package/dist/static/components/button.d.ts +13 -0
- package/dist/static/components/card.d.ts +5 -0
- package/dist/static/components/checkbox.d.ts +10 -0
- package/dist/static/components/colorPicker.d.ts +8 -0
- package/dist/static/components/divider.d.ts +8 -0
- package/dist/static/components/dropdown.d.ts +8 -0
- package/dist/static/components/field.d.ts +15 -0
- package/dist/static/components/group.d.ts +5 -0
- package/dist/static/components/input.d.ts +14 -0
- package/dist/static/components/navBar.d.ts +16 -0
- package/dist/static/components/numberInput.d.ts +15 -0
- package/dist/static/components/page.d.ts +9 -0
- package/dist/static/components/pagination.d.ts +5 -0
- package/dist/static/components/panel.d.ts +11 -0
- package/dist/static/components/progress.d.ts +9 -0
- package/dist/static/components/radio.d.ts +11 -0
- package/dist/static/components/select.d.ts +10 -0
- package/dist/static/components/sidebar.d.ts +9 -0
- package/dist/static/components/skeleton.d.ts +11 -0
- package/dist/static/components/slider.d.ts +12 -0
- package/dist/static/components/spinner.d.ts +12 -0
- package/dist/static/components/switchInput.d.ts +10 -0
- package/dist/static/components/table.d.ts +12 -0
- package/dist/static/components/tag.d.ts +8 -0
- package/dist/static/components/tagsInput.d.ts +7 -0
- package/dist/static/components/textarea.d.ts +12 -0
- package/dist/static/components/toolbar.d.ts +12 -0
- package/dist/static/components/tooltip.d.ts +7 -0
- package/dist/static/escape.d.ts +2 -0
- package/dist/static/index.cjs.js +1 -0
- package/dist/static/index.d.ts +68 -0
- package/dist/static/index.es.js +732 -0
- package/dist/static/render.d.ts +12 -0
- package/dist/static/specs.d.ts +2 -0
- package/dist/static/types.d.ts +43 -0
- package/dist/tokens.css +322 -0
- package/dist/types/index.d.ts +36 -0
- package/docs/guides/installation.md +8 -2
- package/docs/guides/pure-css/_meta.yaml +8 -0
- package/docs/guides/pure-css/class-api.md +1070 -0
- package/docs/guides/pure-css/custom-elements.md +574 -0
- package/docs/guides/pure-css/index.md +86 -0
- package/docs/guides/pure-css/limitations.md +152 -0
- package/docs/guides/pure-css/static-helpers.md +1203 -0
- package/llms-full.txt +3736 -261
- package/package.json +16 -5
- package/src/components/Card/SkCard.vue +1 -0
- package/src/components/ContextMenu/SkContextMenuRadioGroup.vue +4 -1
- package/src/components/Dropdown/SkDropdown.vue +20 -3
- package/src/components/Dropdown/SkDropdownRadioGroup.vue +4 -1
- package/src/components/Dropdown/types.ts +2 -1
- package/src/components/NavBar/SkNavBar.vue +14 -4
- package/src/components/NavBar/context.ts +4 -2
- package/src/components/NavBar/types.ts +6 -1
- package/src/components/Page/SkPage.vue +11 -0
- package/src/components/Panel/SkPanel.vue +2 -1
- package/src/components/ScrollArea/SkScrollArea.vue +78 -5
- package/src/components/TreeView/SkTreeView.vue +7 -2
- package/src/composables/useCustomColors.ts +86 -77
- package/src/composables/usePortalContext.test.ts +0 -2
- package/src/shims.d.ts +10 -0
- package/src/static/__tests__/parity.test.ts +717 -0
- package/src/static/__tests__/parityHarness.test.ts +98 -0
- package/src/static/__tests__/parityHarness.ts +260 -0
- package/src/static/classes.test.ts +82 -0
- package/src/static/classes.ts +111 -0
- package/src/static/components/__tests__/helpers.test.ts +837 -0
- package/src/static/components/alert.ts +117 -0
- package/src/static/components/avatar.ts +86 -0
- package/src/static/components/breadcrumbs.ts +28 -0
- package/src/static/components/button.ts +75 -0
- package/src/static/components/card.ts +27 -0
- package/src/static/components/checkbox.ts +48 -0
- package/src/static/components/colorPicker.ts +45 -0
- package/src/static/components/divider.ts +39 -0
- package/src/static/components/dropdown.ts +36 -0
- package/src/static/components/field.ts +86 -0
- package/src/static/components/group.ts +27 -0
- package/src/static/components/input.ts +55 -0
- package/src/static/components/navBar.ts +94 -0
- package/src/static/components/numberInput.ts +64 -0
- package/src/static/components/page.ts +31 -0
- package/src/static/components/pagination.ts +27 -0
- package/src/static/components/panel.ts +33 -0
- package/src/static/components/progress.ts +31 -0
- package/src/static/components/radio.ts +53 -0
- package/src/static/components/select.ts +51 -0
- package/src/static/components/sidebar.ts +85 -0
- package/src/static/components/skeleton.ts +66 -0
- package/src/static/components/slider.ts +50 -0
- package/src/static/components/spinner.ts +94 -0
- package/src/static/components/switchInput.ts +49 -0
- package/src/static/components/table.ts +88 -0
- package/src/static/components/tag.ts +76 -0
- package/src/static/components/tagsInput.ts +35 -0
- package/src/static/components/textarea.ts +53 -0
- package/src/static/components/toolbar.ts +74 -0
- package/src/static/components/tooltip.ts +29 -0
- package/src/static/escape.test.ts +53 -0
- package/src/static/escape.ts +28 -0
- package/src/static/generated/defaults.ts +378 -0
- package/src/static/generated/propTypes.ts +425 -0
- package/src/static/index.ts +116 -0
- package/src/static/render.test.ts +83 -0
- package/src/static/render.ts +76 -0
- package/src/static/specs.test.ts +58 -0
- package/src/static/specs.ts +230 -0
- package/src/static/types.ts +176 -0
- package/src/styles/__tests__/testHelpers.ts +97 -0
- package/src/styles/base/_custom-elements.scss +51 -0
- package/src/styles/base/_index.scss +4 -0
- package/src/styles/components/__tests__/componentSelectors.test.ts +2575 -0
- package/src/styles/components/_alert.scss +82 -39
- package/src/styles/components/_avatar.scss +102 -47
- package/src/styles/components/_breadcrumbs.scss +39 -37
- package/src/styles/components/_button.scss +58 -5
- package/src/styles/components/_card.scss +64 -2
- package/src/styles/components/_checkbox.scss +35 -5
- package/src/styles/components/_color-picker.scss +48 -13
- package/src/styles/components/_divider.scss +86 -52
- package/src/styles/components/_dropdown.scss +214 -0
- package/src/styles/components/_field.scss +76 -23
- package/src/styles/components/_group.scss +190 -79
- package/src/styles/components/_index.scss +1 -0
- package/src/styles/components/_input.scss +81 -5
- package/src/styles/components/_menu.scss +1 -1
- package/src/styles/components/_navbar.scss +76 -45
- package/src/styles/components/_number-input.scss +88 -83
- package/src/styles/components/_page.scss +82 -23
- package/src/styles/components/_pagination.scss +240 -212
- package/src/styles/components/_panel.scss +268 -122
- package/src/styles/components/_progress.scss +120 -70
- package/src/styles/components/_radio.scss +35 -5
- package/src/styles/components/_scroll-area.scss +50 -22
- package/src/styles/components/_select.scss +40 -9
- package/src/styles/components/_sidebar.scss +59 -34
- package/src/styles/components/_skeleton.scss +111 -65
- package/src/styles/components/_slider.scss +34 -10
- package/src/styles/components/_spinner.scss +107 -56
- package/src/styles/components/_switch.scss +36 -5
- package/src/styles/components/_table.scss +150 -166
- package/src/styles/components/_tag.scss +244 -154
- package/src/styles/components/_tags-input.scss +46 -12
- package/src/styles/components/_textarea.scss +36 -5
- package/src/styles/components/_toolbar.scss +85 -31
- package/src/styles/components/_tooltip.scss +172 -3
- package/src/styles/mixins/_cut-border.scss +18 -4
- package/src/styles/mixins/_dual-selector.scss +192 -0
- package/src/styles/mixins/_index.scss +1 -0
- package/src/styles/mixins/dualSelector.test.ts +151 -0
- package/src/styles/themes/_colorful.scss +25 -0
- package/src/styles/themes/_greyscale.scss +25 -0
- package/src/styles/themes/_shade-scale.scss +39 -0
- package/src/styles/tokens/_semantic-color-kinds.scss +66 -0
- package/src/{types.ts → types/index.ts} +19 -11
- package/web-types.json +970 -137
- package/dist/composables/useCustomColors.test.d.ts +0 -1
- package/dist/composables/useFocusTrap.test.d.ts +0 -1
- package/dist/composables/usePortalContext.test.d.ts +0 -1
- package/dist/styles/mixins/fluidSize.test.d.ts +0 -1
- package/dist/types.d.ts +0 -29
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// Vue <-> Static Parity Tests
|
|
3
|
+
//
|
|
4
|
+
// For each in-scope component, mount the Vue component with specific props, invoke the matching
|
|
5
|
+
// static helper with the same props, and verify the emitted DOM trees match. Fails loudly when
|
|
6
|
+
// Vue and static drift apart.
|
|
7
|
+
//
|
|
8
|
+
// Props must be passed explicitly (including defaults) so both the Vue component and the static
|
|
9
|
+
// helper see the same values — Vue applies withDefaults() internally, but the static helper only
|
|
10
|
+
// adds modifier classes for props that are explicitly provided.
|
|
11
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
import { describe, expect, it } from 'vitest';
|
|
14
|
+
|
|
15
|
+
import SkPanel from '../../components/Panel/SkPanel.vue';
|
|
16
|
+
import SkAlert from '../../components/Alert/SkAlert.vue';
|
|
17
|
+
import SkNavBar from '../../components/NavBar/SkNavBar.vue';
|
|
18
|
+
import SkToolbar from '../../components/Toolbar/SkToolbar.vue';
|
|
19
|
+
import SkSidebar from '../../components/Sidebar/SkSidebar.vue';
|
|
20
|
+
import SkDivider from '../../components/Divider/SkDivider.vue';
|
|
21
|
+
import SkSkeleton from '../../components/Skeleton/SkSkeleton.vue';
|
|
22
|
+
import SkSpinner from '../../components/Spinner/SkSpinner.vue';
|
|
23
|
+
import SkTag from '../../components/Tag/SkTag.vue';
|
|
24
|
+
import SkAvatar from '../../components/Avatar/SkAvatar.vue';
|
|
25
|
+
import SkField from '../../components/Field/SkField.vue';
|
|
26
|
+
import SkTable from '../../components/Table/SkTable.vue';
|
|
27
|
+
|
|
28
|
+
import { panel } from '../components/panel';
|
|
29
|
+
import { skeleton } from '../components/skeleton';
|
|
30
|
+
import { alert } from '../components/alert';
|
|
31
|
+
import { navBar } from '../components/navBar';
|
|
32
|
+
import { toolbar } from '../components/toolbar';
|
|
33
|
+
import { sidebar } from '../components/sidebar';
|
|
34
|
+
import { divider } from '../components/divider';
|
|
35
|
+
import { spinner } from '../components/spinner';
|
|
36
|
+
import { tag } from '../components/tag';
|
|
37
|
+
import { avatar } from '../components/avatar';
|
|
38
|
+
import { field } from '../components/field';
|
|
39
|
+
import { table } from '../components/table';
|
|
40
|
+
|
|
41
|
+
import { compareParity } from './parityHarness';
|
|
42
|
+
|
|
43
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
type StaticFn = (p : Record<string, unknown>, c ?: string) => string;
|
|
46
|
+
|
|
47
|
+
function expectParity(result : ReturnType<typeof compareParity>) : void
|
|
48
|
+
{
|
|
49
|
+
if(!result.match)
|
|
50
|
+
{
|
|
51
|
+
throw new Error(`Parity failed:\n${ result.diff }`);
|
|
52
|
+
}
|
|
53
|
+
expect(result.match).toBe(true);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Document an intentional Vue/static non-parity. Renders as a skipped test so the
|
|
57
|
+
// reason shows up in test output without ever executing.
|
|
58
|
+
function skipNonParity(reason : string) : void
|
|
59
|
+
{
|
|
60
|
+
it.skip(`parity not asserted — ${ reason }`, () =>
|
|
61
|
+
{
|
|
62
|
+
// intentionally not run; description above documents the divergence
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
describe('parity', () =>
|
|
69
|
+
{
|
|
70
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
71
|
+
// Panel
|
|
72
|
+
//
|
|
73
|
+
// Simple <div> container that directly matches the static helper. Props must be spelled out
|
|
74
|
+
// explicitly (including withDefaults values) so both sides emit the same modifier classes.
|
|
75
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
describe('panel', () =>
|
|
78
|
+
{
|
|
79
|
+
it('default props', () =>
|
|
80
|
+
{
|
|
81
|
+
expectParity(compareParity({
|
|
82
|
+
vueComponent: SkPanel,
|
|
83
|
+
staticHelper: panel as StaticFn,
|
|
84
|
+
// Spell out Panel's withDefaults values so static receives the same prop values.
|
|
85
|
+
props: { kind: 'neutral', size: 'md', corners: [ 'bottom-right' ], decorationCorner: 'bottom-right' },
|
|
86
|
+
children: 'content',
|
|
87
|
+
}));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('kind variant', () =>
|
|
91
|
+
{
|
|
92
|
+
expectParity(compareParity({
|
|
93
|
+
vueComponent: SkPanel,
|
|
94
|
+
staticHelper: panel as StaticFn,
|
|
95
|
+
props: { kind: 'primary', size: 'md', corners: [ 'bottom-right' ], decorationCorner: 'bottom-right' },
|
|
96
|
+
children: 'x',
|
|
97
|
+
}));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('size variant', () =>
|
|
101
|
+
{
|
|
102
|
+
expectParity(compareParity({
|
|
103
|
+
vueComponent: SkPanel,
|
|
104
|
+
staticHelper: panel as StaticFn,
|
|
105
|
+
props: { kind: 'neutral', size: 'lg', corners: [ 'bottom-right' ], decorationCorner: 'bottom-right' },
|
|
106
|
+
children: 'x',
|
|
107
|
+
}));
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('corners + decoration combo', () =>
|
|
111
|
+
{
|
|
112
|
+
expectParity(compareParity({
|
|
113
|
+
vueComponent: SkPanel,
|
|
114
|
+
staticHelper: panel as StaticFn,
|
|
115
|
+
props: {
|
|
116
|
+
kind: 'accent',
|
|
117
|
+
size: 'sm',
|
|
118
|
+
corners: [ 'top-left', 'bottom-right' ],
|
|
119
|
+
decorationCorner: 'top-left',
|
|
120
|
+
},
|
|
121
|
+
children: 'x',
|
|
122
|
+
}));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('noBorder flag', () =>
|
|
126
|
+
{
|
|
127
|
+
expectParity(compareParity({
|
|
128
|
+
vueComponent: SkPanel,
|
|
129
|
+
staticHelper: panel as StaticFn,
|
|
130
|
+
props: {
|
|
131
|
+
kind: 'neutral',
|
|
132
|
+
size: 'md',
|
|
133
|
+
noBorder: true,
|
|
134
|
+
corners: [ 'bottom-right' ],
|
|
135
|
+
decorationCorner: 'bottom-right',
|
|
136
|
+
},
|
|
137
|
+
children: 'x',
|
|
138
|
+
}));
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
143
|
+
// Card
|
|
144
|
+
//
|
|
145
|
+
// SKIP: SkCard wraps SkPanel — Vue emits a nested SkPanel structure with sk-card added to its
|
|
146
|
+
// class, while the static helper emits a plain <div class="sk-card">. These are structurally
|
|
147
|
+
// divergent by design (static layer intentionally flattens the panel hierarchy).
|
|
148
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
describe('card', () =>
|
|
151
|
+
{
|
|
152
|
+
skipNonParity('SkCard wraps SkPanel; static emits a flat <div class="sk-card"> with no inner panel structure');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
156
|
+
// Alert
|
|
157
|
+
//
|
|
158
|
+
// The static helper now emits the full alert structure: kind-appropriate icon div (for
|
|
159
|
+
// feedback kinds) + sk-alert-content wrapper, matching what SkAlert.vue renders.
|
|
160
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
describe('alert', () =>
|
|
163
|
+
{
|
|
164
|
+
it('info kind (has icon)', () =>
|
|
165
|
+
{
|
|
166
|
+
expectParity(compareParity({
|
|
167
|
+
vueComponent: SkAlert,
|
|
168
|
+
staticHelper: alert as StaticFn,
|
|
169
|
+
props: { kind: 'info', subtle: false },
|
|
170
|
+
children: 'Check this out.',
|
|
171
|
+
}));
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('neutral kind (no icon)', () =>
|
|
175
|
+
{
|
|
176
|
+
expectParity(compareParity({
|
|
177
|
+
vueComponent: SkAlert,
|
|
178
|
+
staticHelper: alert as StaticFn,
|
|
179
|
+
props: { kind: 'neutral', subtle: false },
|
|
180
|
+
children: 'Neutral message.',
|
|
181
|
+
}));
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('warning kind (has icon)', () =>
|
|
185
|
+
{
|
|
186
|
+
expectParity(compareParity({
|
|
187
|
+
vueComponent: SkAlert,
|
|
188
|
+
staticHelper: alert as StaticFn,
|
|
189
|
+
props: { kind: 'warning', subtle: false },
|
|
190
|
+
children: 'Watch out.',
|
|
191
|
+
}));
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
196
|
+
// Divider
|
|
197
|
+
//
|
|
198
|
+
// The static helper now emits <hr> with the same class structure as SkDivider.vue:
|
|
199
|
+
// sk-divider, sk-<orientation>, sk-<kind>, sk-<size>. The aria-orientation attribute is
|
|
200
|
+
// filtered by the parity harness (it's in REKA_UI_INJECTED_ATTRS) so only the tag name
|
|
201
|
+
// and class list are compared.
|
|
202
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
describe('divider', () =>
|
|
205
|
+
{
|
|
206
|
+
it('horizontal (default)', () =>
|
|
207
|
+
{
|
|
208
|
+
expectParity(compareParity({
|
|
209
|
+
vueComponent: SkDivider,
|
|
210
|
+
staticHelper: divider as StaticFn,
|
|
211
|
+
props: { orientation: 'horizontal', kind: 'neutral', size: 'md' },
|
|
212
|
+
}));
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('vertical orientation', () =>
|
|
216
|
+
{
|
|
217
|
+
expectParity(compareParity({
|
|
218
|
+
vueComponent: SkDivider,
|
|
219
|
+
staticHelper: divider as StaticFn,
|
|
220
|
+
props: { orientation: 'vertical', kind: 'neutral', size: 'md' },
|
|
221
|
+
}));
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('primary kind', () =>
|
|
225
|
+
{
|
|
226
|
+
expectParity(compareParity({
|
|
227
|
+
vueComponent: SkDivider,
|
|
228
|
+
staticHelper: divider as StaticFn,
|
|
229
|
+
props: { orientation: 'horizontal', kind: 'primary', size: 'md' },
|
|
230
|
+
}));
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
235
|
+
// Page
|
|
236
|
+
//
|
|
237
|
+
// SKIP: SkPage is a complex multi-slot layout shell with sidebars, drawers, headers, footers,
|
|
238
|
+
// and responsive behavior. The static helper emits a plain <div class="sk-page"> — there is no
|
|
239
|
+
// practical parity between the two at this level.
|
|
240
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
describe('page', () =>
|
|
243
|
+
{
|
|
244
|
+
skipNonParity('SkPage is a full layout shell; static emits a bare container');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
248
|
+
// Group
|
|
249
|
+
//
|
|
250
|
+
// SKIP: SkGroup emits `sk-${orientation}` (e.g. sk-horizontal) via a template literal, while
|
|
251
|
+
// the static spec correctly targets the CSS class family `sk-orientation-horizontal` used by
|
|
252
|
+
// the SCSS single-choice-modifier mixin. These diverge at the class name level. The static
|
|
253
|
+
// spec matches the CSS — Vue is applying the wrong class name (it works only because
|
|
254
|
+
// defaults-when-absent catches the unclassed case). Do not modify Vue; document this drift.
|
|
255
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
256
|
+
|
|
257
|
+
describe('group', () =>
|
|
258
|
+
{
|
|
259
|
+
skipNonParity('Vue emits sk-horizontal; static emits sk-orientation-horizontal (matches CSS selector)');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
263
|
+
// Skeleton
|
|
264
|
+
//
|
|
265
|
+
// The static helper now emits the same class names as Vue: sk-<variant> (e.g. sk-text) and
|
|
266
|
+
// sk-<animation> (e.g. sk-shimmer) when animation is not 'none'. Width/height are forwarded
|
|
267
|
+
// as inline style so the style attribute also matches. Pass all props explicitly (including
|
|
268
|
+
// Vue defaults) to ensure both sides emit the same output.
|
|
269
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
270
|
+
|
|
271
|
+
describe('skeleton', () =>
|
|
272
|
+
{
|
|
273
|
+
it('text variant, shimmer animation (Vue defaults)', () =>
|
|
274
|
+
{
|
|
275
|
+
expectParity(compareParity({
|
|
276
|
+
vueComponent: SkSkeleton,
|
|
277
|
+
staticHelper: skeleton as StaticFn,
|
|
278
|
+
// Vue defaults: variant='text', animation='shimmer', width='100%'
|
|
279
|
+
props: { variant: 'text', animation: 'shimmer', width: '100%' },
|
|
280
|
+
}));
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('circular variant', () =>
|
|
284
|
+
{
|
|
285
|
+
expectParity(compareParity({
|
|
286
|
+
vueComponent: SkSkeleton,
|
|
287
|
+
staticHelper: skeleton as StaticFn,
|
|
288
|
+
props: { variant: 'circular', animation: 'shimmer', width: '100%' },
|
|
289
|
+
}));
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('pulse animation', () =>
|
|
293
|
+
{
|
|
294
|
+
expectParity(compareParity({
|
|
295
|
+
vueComponent: SkSkeleton,
|
|
296
|
+
staticHelper: skeleton as StaticFn,
|
|
297
|
+
props: { variant: 'text', animation: 'pulse', width: '100%' },
|
|
298
|
+
}));
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
303
|
+
// Progress
|
|
304
|
+
//
|
|
305
|
+
// SKIP: SkProgress uses reka-ui ProgressRoot which renders as a role="progressbar" div
|
|
306
|
+
// with aria-valuenow/aria-valuemin/aria-valuemax. The static helper emits a void <progress />
|
|
307
|
+
// element — different tag, different ARIA model.
|
|
308
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
309
|
+
|
|
310
|
+
describe('progress', () =>
|
|
311
|
+
{
|
|
312
|
+
skipNonParity('Vue uses reka-ui ProgressRoot with aria-value* attrs; static emits <progress />');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
316
|
+
// Spinner
|
|
317
|
+
//
|
|
318
|
+
// The static helper now emits the full inner DOM structure for each variant, matching
|
|
319
|
+
// SkSpinner.vue's v-if/v-else-if template blocks. ARIA attributes (role, aria-live,
|
|
320
|
+
// aria-label) are emitted on the root element. Pass all props explicitly including defaults.
|
|
321
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
describe('spinner', () =>
|
|
324
|
+
{
|
|
325
|
+
it('circular variant (default)', () =>
|
|
326
|
+
{
|
|
327
|
+
expectParity(compareParity({
|
|
328
|
+
vueComponent: SkSpinner,
|
|
329
|
+
staticHelper: spinner as StaticFn,
|
|
330
|
+
props: { kind: 'primary', size: 'md', variant: 'circular' },
|
|
331
|
+
}));
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('dots variant', () =>
|
|
335
|
+
{
|
|
336
|
+
expectParity(compareParity({
|
|
337
|
+
vueComponent: SkSpinner,
|
|
338
|
+
staticHelper: spinner as StaticFn,
|
|
339
|
+
props: { kind: 'primary', size: 'md', variant: 'dots' },
|
|
340
|
+
}));
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('crosshair variant', () =>
|
|
344
|
+
{
|
|
345
|
+
expectParity(compareParity({
|
|
346
|
+
vueComponent: SkSpinner,
|
|
347
|
+
staticHelper: spinner as StaticFn,
|
|
348
|
+
props: { kind: 'primary', size: 'md', variant: 'crosshair' },
|
|
349
|
+
}));
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
354
|
+
// NavBar
|
|
355
|
+
//
|
|
356
|
+
// The static helper now emits the sk-navbar-content wrapper and conditional slot divs,
|
|
357
|
+
// matching SkNavBar's rendered output. Sticky defaults to true on both sides.
|
|
358
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
359
|
+
|
|
360
|
+
describe('navBar', () =>
|
|
361
|
+
{
|
|
362
|
+
it('default kind with nav children', () =>
|
|
363
|
+
{
|
|
364
|
+
expectParity(compareParity({
|
|
365
|
+
vueComponent: SkNavBar,
|
|
366
|
+
staticHelper: navBar as StaticFn,
|
|
367
|
+
props: { kind: 'neutral', sticky: true },
|
|
368
|
+
children: 'Nav Links',
|
|
369
|
+
}));
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('primary kind, sticky', () =>
|
|
373
|
+
{
|
|
374
|
+
expectParity(compareParity({
|
|
375
|
+
vueComponent: SkNavBar,
|
|
376
|
+
staticHelper: navBar as StaticFn,
|
|
377
|
+
props: { kind: 'primary', sticky: true },
|
|
378
|
+
children: 'x',
|
|
379
|
+
}));
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('accent kind, non-sticky', () =>
|
|
383
|
+
{
|
|
384
|
+
expectParity(compareParity({
|
|
385
|
+
vueComponent: SkNavBar,
|
|
386
|
+
staticHelper: navBar as StaticFn,
|
|
387
|
+
props: { kind: 'accent', sticky: false },
|
|
388
|
+
children: 'Link A',
|
|
389
|
+
}));
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
394
|
+
// Toolbar
|
|
395
|
+
//
|
|
396
|
+
// The static helper now emits the full class structure matching SkToolbar (sk-toolbar,
|
|
397
|
+
// sk-<kind>, sk-<orientation>, sk-cut-* for all corners). The parity harness filters
|
|
398
|
+
// reka-ui runtime attributes (aria-orientation, data-orientation, dir, tabindex, and the
|
|
399
|
+
// outline-reset inline style) since these are injected by ToolbarRoot and have no static
|
|
400
|
+
// equivalent. Only the class structure is compared — which is what the CSS consumes.
|
|
401
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
402
|
+
|
|
403
|
+
describe('toolbar', () =>
|
|
404
|
+
{
|
|
405
|
+
it('default props (all four corners, horizontal)', () =>
|
|
406
|
+
{
|
|
407
|
+
expectParity(compareParity({
|
|
408
|
+
vueComponent: SkToolbar,
|
|
409
|
+
staticHelper: toolbar as StaticFn,
|
|
410
|
+
props: {
|
|
411
|
+
kind: 'neutral',
|
|
412
|
+
orientation: 'horizontal',
|
|
413
|
+
corners: [ 'top-left', 'top-right', 'bottom-right', 'bottom-left' ],
|
|
414
|
+
},
|
|
415
|
+
children: 'content',
|
|
416
|
+
}));
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('primary kind, vertical', () =>
|
|
420
|
+
{
|
|
421
|
+
expectParity(compareParity({
|
|
422
|
+
vueComponent: SkToolbar,
|
|
423
|
+
staticHelper: toolbar as StaticFn,
|
|
424
|
+
props: {
|
|
425
|
+
kind: 'primary',
|
|
426
|
+
orientation: 'vertical',
|
|
427
|
+
corners: [ 'top-left', 'top-right', 'bottom-right', 'bottom-left' ],
|
|
428
|
+
},
|
|
429
|
+
children: 'x',
|
|
430
|
+
}));
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
435
|
+
// Sidebar
|
|
436
|
+
//
|
|
437
|
+
// The static helper now emits the full nested structure: aside → sk-panel (sk-sidebar-panel)
|
|
438
|
+
// → sk-panel-scroll-content → sk-sidebar-nav. The panel corner/decoration corner is computed
|
|
439
|
+
// from the `side` prop, matching SkSidebar's computed panelCorners/panelDecorationCorner.
|
|
440
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
441
|
+
|
|
442
|
+
describe('sidebar', () =>
|
|
443
|
+
{
|
|
444
|
+
it('left side (default)', () =>
|
|
445
|
+
{
|
|
446
|
+
expectParity(compareParity({
|
|
447
|
+
vueComponent: SkSidebar,
|
|
448
|
+
staticHelper: sidebar as StaticFn,
|
|
449
|
+
props: { kind: 'neutral', side: 'left', dense: false },
|
|
450
|
+
children: 'nav-content',
|
|
451
|
+
}));
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('right side', () =>
|
|
455
|
+
{
|
|
456
|
+
expectParity(compareParity({
|
|
457
|
+
vueComponent: SkSidebar,
|
|
458
|
+
staticHelper: sidebar as StaticFn,
|
|
459
|
+
props: { kind: 'neutral', side: 'right', dense: false },
|
|
460
|
+
children: 'x',
|
|
461
|
+
}));
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('primary kind', () =>
|
|
465
|
+
{
|
|
466
|
+
expectParity(compareParity({
|
|
467
|
+
vueComponent: SkSidebar,
|
|
468
|
+
staticHelper: sidebar as StaticFn,
|
|
469
|
+
props: { kind: 'primary', side: 'left', dense: false },
|
|
470
|
+
children: 'x',
|
|
471
|
+
}));
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
476
|
+
// Breadcrumbs
|
|
477
|
+
//
|
|
478
|
+
// SKIP: SkBreadcrumbs wraps slot content in <ol class="sk-breadcrumbs-list"> and inserts
|
|
479
|
+
// SkBreadcrumbSeparator components between items. The static helper emits a bare <nav>
|
|
480
|
+
// with no inner list structure.
|
|
481
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
482
|
+
|
|
483
|
+
describe('breadcrumbs', () =>
|
|
484
|
+
{
|
|
485
|
+
skipNonParity('Vue wraps items in <ol class="sk-breadcrumbs-list"> with separators; static emits bare <nav>');
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
489
|
+
// Pagination
|
|
490
|
+
//
|
|
491
|
+
// SKIP: SkPagination renders SkPaginationItem children (first/prev/page numbers/next/last)
|
|
492
|
+
// and requires a `total` prop to compute its item list. The static helper emits only the
|
|
493
|
+
// <nav class="sk-pagination ..."> container with no inner items.
|
|
494
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
495
|
+
|
|
496
|
+
describe('pagination', () =>
|
|
497
|
+
{
|
|
498
|
+
skipNonParity('Vue renders pagination items (prev/page/next); static emits bare <nav> container');
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
502
|
+
// Tag
|
|
503
|
+
//
|
|
504
|
+
// The static helper now wraps children in <span class="sk-tag-content"> and conditionally
|
|
505
|
+
// emits the remove button when `removable: true`, matching SkTag.vue's template structure.
|
|
506
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
507
|
+
|
|
508
|
+
describe('tag', () =>
|
|
509
|
+
{
|
|
510
|
+
it('default (neutral, solid, md)', () =>
|
|
511
|
+
{
|
|
512
|
+
expectParity(compareParity({
|
|
513
|
+
vueComponent: SkTag,
|
|
514
|
+
staticHelper: tag as StaticFn,
|
|
515
|
+
props: { kind: 'neutral', variant: 'solid', size: 'md', removable: false },
|
|
516
|
+
children: 'Label',
|
|
517
|
+
}));
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('primary kind, outline variant', () =>
|
|
521
|
+
{
|
|
522
|
+
expectParity(compareParity({
|
|
523
|
+
vueComponent: SkTag,
|
|
524
|
+
staticHelper: tag as StaticFn,
|
|
525
|
+
props: { kind: 'primary', variant: 'outline', size: 'md', removable: false },
|
|
526
|
+
children: 'Feature',
|
|
527
|
+
}));
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('removable: true emits remove button', () =>
|
|
531
|
+
{
|
|
532
|
+
expectParity(compareParity({
|
|
533
|
+
vueComponent: SkTag,
|
|
534
|
+
staticHelper: tag as StaticFn,
|
|
535
|
+
props: { kind: 'neutral', variant: 'solid', size: 'md', removable: true },
|
|
536
|
+
children: 'Close me',
|
|
537
|
+
}));
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
542
|
+
// Avatar
|
|
543
|
+
//
|
|
544
|
+
// The static helper now emits the correct inner content based on src/initials props:
|
|
545
|
+
// - src provided → <img class="sk-avatar-image" src="..." alt="...">
|
|
546
|
+
// - initials provided (no src) → <span class="sk-avatar-initials">AB</span>
|
|
547
|
+
// - neither → default SVG icon <svg class="sk-avatar-icon">...</svg>
|
|
548
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
549
|
+
|
|
550
|
+
describe('avatar', () =>
|
|
551
|
+
{
|
|
552
|
+
it('default (no src, no initials — SVG icon)', () =>
|
|
553
|
+
{
|
|
554
|
+
expectParity(compareParity({
|
|
555
|
+
vueComponent: SkAvatar,
|
|
556
|
+
staticHelper: avatar as StaticFn,
|
|
557
|
+
props: { kind: 'neutral', size: 'md' },
|
|
558
|
+
}));
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('with src and alt — img element', () =>
|
|
562
|
+
{
|
|
563
|
+
expectParity(compareParity({
|
|
564
|
+
vueComponent: SkAvatar,
|
|
565
|
+
staticHelper: avatar as StaticFn,
|
|
566
|
+
props: { kind: 'neutral', size: 'md', src: '/test.jpg', alt: 'Test User' },
|
|
567
|
+
}));
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('initials fallback', () =>
|
|
571
|
+
{
|
|
572
|
+
expectParity(compareParity({
|
|
573
|
+
vueComponent: SkAvatar,
|
|
574
|
+
staticHelper: avatar as StaticFn,
|
|
575
|
+
props: { kind: 'primary', size: 'lg', initials: 'JD' },
|
|
576
|
+
}));
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
581
|
+
// Field
|
|
582
|
+
//
|
|
583
|
+
// The static helper now emits the full field structure matching SkField.vue: sk-label-top
|
|
584
|
+
// default, sk-field-input-wrapper always wrapping children, and optional label/description/
|
|
585
|
+
// error elements. An explicit `id` prop is passed so that Vue's `for`/`id` attrs are
|
|
586
|
+
// deterministic and match the static output (Vue would otherwise auto-generate a random ID).
|
|
587
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
588
|
+
|
|
589
|
+
describe('field', () =>
|
|
590
|
+
{
|
|
591
|
+
it('no label, no error (input wrapper only)', () =>
|
|
592
|
+
{
|
|
593
|
+
expectParity(compareParity({
|
|
594
|
+
vueComponent: SkField,
|
|
595
|
+
staticHelper: field as StaticFn,
|
|
596
|
+
props: { labelPosition: 'top', id: 'f1' },
|
|
597
|
+
children: '<input class="sk-input" />',
|
|
598
|
+
}));
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it('with label', () =>
|
|
602
|
+
{
|
|
603
|
+
expectParity(compareParity({
|
|
604
|
+
vueComponent: SkField,
|
|
605
|
+
staticHelper: field as StaticFn,
|
|
606
|
+
props: { label: 'Email', labelPosition: 'top', id: 'f2' },
|
|
607
|
+
children: '<input class="sk-input" />',
|
|
608
|
+
}));
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it('with error', () =>
|
|
612
|
+
{
|
|
613
|
+
expectParity(compareParity({
|
|
614
|
+
vueComponent: SkField,
|
|
615
|
+
staticHelper: field as StaticFn,
|
|
616
|
+
props: { error: 'Required', labelPosition: 'top', id: 'f3' },
|
|
617
|
+
children: '<input class="sk-input" />',
|
|
618
|
+
}));
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it('with description (no error)', () =>
|
|
622
|
+
{
|
|
623
|
+
expectParity(compareParity({
|
|
624
|
+
vueComponent: SkField,
|
|
625
|
+
staticHelper: field as StaticFn,
|
|
626
|
+
props: { description: 'Help text', labelPosition: 'top', id: 'f4' },
|
|
627
|
+
children: '<input class="sk-input" />',
|
|
628
|
+
}));
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
633
|
+
// Table
|
|
634
|
+
//
|
|
635
|
+
// The static helper now emits the wrapper div + table, matching SkTable.vue's rendered output.
|
|
636
|
+
// Vue defaults (hoverable=true, bordered=true, innerBorders=false, variant='default') must be
|
|
637
|
+
// passed explicitly so both sides emit the same class sets.
|
|
638
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
639
|
+
|
|
640
|
+
describe('table', () =>
|
|
641
|
+
{
|
|
642
|
+
it('default props (neutral, default variant, hoverable, bordered)', () =>
|
|
643
|
+
{
|
|
644
|
+
expectParity(compareParity({
|
|
645
|
+
vueComponent: SkTable,
|
|
646
|
+
staticHelper: table as StaticFn,
|
|
647
|
+
props: {
|
|
648
|
+
kind: 'neutral',
|
|
649
|
+
variant: 'default',
|
|
650
|
+
striped: false,
|
|
651
|
+
hoverable: true,
|
|
652
|
+
bordered: true,
|
|
653
|
+
innerBorders: false,
|
|
654
|
+
darkBackground: false,
|
|
655
|
+
subtle: false,
|
|
656
|
+
},
|
|
657
|
+
children: '<thead><tr><th>Name</th></tr></thead>',
|
|
658
|
+
}));
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it('primary kind', () =>
|
|
662
|
+
{
|
|
663
|
+
expectParity(compareParity({
|
|
664
|
+
vueComponent: SkTable,
|
|
665
|
+
staticHelper: table as StaticFn,
|
|
666
|
+
props: {
|
|
667
|
+
kind: 'primary',
|
|
668
|
+
variant: 'default',
|
|
669
|
+
striped: false,
|
|
670
|
+
hoverable: true,
|
|
671
|
+
bordered: true,
|
|
672
|
+
innerBorders: false,
|
|
673
|
+
darkBackground: false,
|
|
674
|
+
subtle: false,
|
|
675
|
+
},
|
|
676
|
+
children: 'x',
|
|
677
|
+
}));
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
it('striped + compact variant', () =>
|
|
681
|
+
{
|
|
682
|
+
expectParity(compareParity({
|
|
683
|
+
vueComponent: SkTable,
|
|
684
|
+
staticHelper: table as StaticFn,
|
|
685
|
+
props: {
|
|
686
|
+
kind: 'neutral',
|
|
687
|
+
variant: 'compact',
|
|
688
|
+
striped: true,
|
|
689
|
+
hoverable: true,
|
|
690
|
+
bordered: true,
|
|
691
|
+
innerBorders: false,
|
|
692
|
+
darkBackground: false,
|
|
693
|
+
subtle: false,
|
|
694
|
+
},
|
|
695
|
+
children: 'x',
|
|
696
|
+
}));
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
701
|
+
// Tooltip
|
|
702
|
+
//
|
|
703
|
+
// SKIP: SkTooltip uses reka-ui's portal (TooltipPortal) — the tooltip content is rendered
|
|
704
|
+
// outside the component tree in a separate DOM node. The static helper emits a plain
|
|
705
|
+
// <div role="tooltip"> inline. These are fundamentally different rendering models.
|
|
706
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
707
|
+
|
|
708
|
+
describe('tooltip', () =>
|
|
709
|
+
{
|
|
710
|
+
skipNonParity(
|
|
711
|
+
'Vue uses reka-ui portal (tooltip content is teleported);'
|
|
712
|
+
+ ' static emits inline <div role="tooltip">'
|
|
713
|
+
);
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
//----------------------------------------------------------------------------------------------------------------------
|