@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,2575 @@
|
|
|
1
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
2
|
+
// Component Selector Tests (SCSS)
|
|
3
|
+
//
|
|
4
|
+
// Compiles component SCSS files and asserts that the dual-selector mixin output contains both
|
|
5
|
+
// class-form and attribute-form selectors for each kind, size, modifier, and default variant.
|
|
6
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
import { describe, expect, it } from 'vitest';
|
|
9
|
+
import { compileString } from 'sass';
|
|
10
|
+
import { dirname, resolve } from 'node:path';
|
|
11
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
12
|
+
|
|
13
|
+
// Utils
|
|
14
|
+
import { KINDS, SEMANTIC_KINDS, SIZES, attrValueRe, dualSelectorRe } from '../../__tests__/testHelpers';
|
|
15
|
+
|
|
16
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
// styles root: __tests__ → components → styles
|
|
20
|
+
const stylesDir = resolve(here, '../..');
|
|
21
|
+
|
|
22
|
+
function compileComponent(component : string) : string
|
|
23
|
+
{
|
|
24
|
+
const source = `@use "components/${ component }";`;
|
|
25
|
+
const { css } = compileString(source, {
|
|
26
|
+
loadPaths: [ stylesDir ],
|
|
27
|
+
// URL placed at styles root so relative imports inside _panel.scss work correctly.
|
|
28
|
+
url: pathToFileURL(resolve(stylesDir, `${ component }.test.scss`)),
|
|
29
|
+
});
|
|
30
|
+
return css;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
// Sass strips quotes from attribute-selector values that don't need them for CSS validity,
|
|
36
|
+
// so `[kind="primary"]` may be emitted as `[kind=primary]`. Accept both forms.
|
|
37
|
+
// `dualSelectorRe`, `attrValueRe`, `KINDS`, and `SIZES` are imported from testHelpers above.
|
|
38
|
+
|
|
39
|
+
//----------------------------------------------------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
describe('component selectors (SCSS)', () =>
|
|
42
|
+
{
|
|
43
|
+
describe('panel', () =>
|
|
44
|
+
{
|
|
45
|
+
// Compile once for the whole describe block — all assertions share the same CSS output.
|
|
46
|
+
let css : string;
|
|
47
|
+
|
|
48
|
+
// Vitest runs `beforeAll` lazily; we use a helper that memoises on first call.
|
|
49
|
+
function panelCSS() : string
|
|
50
|
+
{
|
|
51
|
+
if(!css) { css = compileComponent('panel'); }
|
|
52
|
+
return css;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
56
|
+
// Kind variants (17 values from $kinds in theme/_variables.scss)
|
|
57
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
const kinds = KINDS;
|
|
60
|
+
|
|
61
|
+
describe('kind variants', () =>
|
|
62
|
+
{
|
|
63
|
+
for(const kind of kinds)
|
|
64
|
+
{
|
|
65
|
+
it(`emits .sk-panel.sk-${ kind } and sk-panel[kind="${ kind }"]`, () =>
|
|
66
|
+
{
|
|
67
|
+
const out = panelCSS();
|
|
68
|
+
expect(out).toMatch(
|
|
69
|
+
new RegExp(dualSelectorRe('panel', kind, 'kind', kind))
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
76
|
+
// Size variants (xs, sm, md, lg, xl)
|
|
77
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
const sizes = SIZES;
|
|
80
|
+
|
|
81
|
+
describe('size variants', () =>
|
|
82
|
+
{
|
|
83
|
+
for(const size of sizes)
|
|
84
|
+
{
|
|
85
|
+
// Each size emits three selectors: .sk-<size>, .sk-size-<size>, and [size="<size>"].
|
|
86
|
+
// Check all three exist anywhere in the output (not necessarily adjacent).
|
|
87
|
+
it(`emits .sk-panel.sk-${ size } and sk-panel[size="${ size }"]`, () =>
|
|
88
|
+
{
|
|
89
|
+
const out = panelCSS();
|
|
90
|
+
expect(out).toMatch(new RegExp(`\\.sk-panel\\.sk-${ size }[,\\s{]`));
|
|
91
|
+
expect(out).toMatch(new RegExp(`sk-panel\\[size=${ attrValueRe(size) }\\]`));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
97
|
+
// Corner cut modifiers (list-modifier with `corners` attribute)
|
|
98
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
const corners = [ 'top-left', 'top-right', 'bottom-right', 'bottom-left' ];
|
|
101
|
+
|
|
102
|
+
describe('corner cut modifiers', () =>
|
|
103
|
+
{
|
|
104
|
+
for(const corner of corners)
|
|
105
|
+
{
|
|
106
|
+
it(`emits .sk-panel.sk-cut-${ corner } and sk-panel[corners~="${ corner }"]`, () =>
|
|
107
|
+
{
|
|
108
|
+
const out = panelCSS();
|
|
109
|
+
expect(out).toMatch(
|
|
110
|
+
new RegExp(dualSelectorRe('panel', `cut-${ corner }`, 'corners', corner, { attrOp: '~=' }))
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
117
|
+
// Decoration corner modifiers (single-choice-modifier with `decoration-corner` attribute)
|
|
118
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
describe('decoration corner modifiers', () =>
|
|
121
|
+
{
|
|
122
|
+
for(const corner of corners)
|
|
123
|
+
{
|
|
124
|
+
// Decoration rules are applied via `&::after`, so the emitted selector includes
|
|
125
|
+
// the `::after` pseudo-element on both the class and attribute forms.
|
|
126
|
+
it(`emits .sk-panel.sk-decoration-${ corner } and sk-panel[decoration-corner="${ corner }"]`, () =>
|
|
127
|
+
{
|
|
128
|
+
const out = panelCSS();
|
|
129
|
+
const opts = { suffix: '::after' };
|
|
130
|
+
expect(out).toMatch(
|
|
131
|
+
new RegExp(dualSelectorRe('panel', `decoration-${ corner }`, 'decoration-corner', corner, opts))
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
138
|
+
// Boolean modifiers
|
|
139
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
describe('boolean modifiers', () =>
|
|
142
|
+
{
|
|
143
|
+
it('emits .sk-panel.sk-no-border and sk-panel[no-border]', () =>
|
|
144
|
+
{
|
|
145
|
+
expect(panelCSS()).toMatch(/\.sk-panel\.sk-no-border,\s*sk-panel\[no-border\]/);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('emits .sk-panel.sk-no-decoration and sk-panel[no-decoration]', () =>
|
|
149
|
+
{
|
|
150
|
+
// no-decoration hides the ::after element, so the selector pair includes ::after.
|
|
151
|
+
expect(panelCSS()).toMatch(/\.sk-panel\.sk-no-decoration::after,\s*sk-panel\[no-decoration\]::after/);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
156
|
+
// Defaults-when-absent
|
|
157
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
describe('defaults-when-absent', () =>
|
|
160
|
+
{
|
|
161
|
+
it('applies default bottom-right cut when no sk-cut-* class is present', () =>
|
|
162
|
+
{
|
|
163
|
+
const out = panelCSS();
|
|
164
|
+
// Selector: .sk-panel:where(:not([class*="sk-cut-"])) { --sk-panel-cut-br: ... }
|
|
165
|
+
expect(out).toMatch(
|
|
166
|
+
new RegExp(`\\.sk-panel:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-cut-') }\\]\\)\\)`)
|
|
167
|
+
);
|
|
168
|
+
// The default must set the br cut variable
|
|
169
|
+
expect(out).toMatch(new RegExp(
|
|
170
|
+
String.raw`\.sk-panel:where\(:not\(\[class\*=["']?sk-cut-["']?\]\)\)[^{]*\{[^}]*--sk-panel-cut-br`
|
|
171
|
+
));
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('applies default bottom-right decoration when no sk-decoration-* class is present', () =>
|
|
175
|
+
{
|
|
176
|
+
const out = panelCSS();
|
|
177
|
+
// Selector: .sk-panel:where(:not([class*="sk-decoration-"])) { ... }
|
|
178
|
+
expect(out).toMatch(
|
|
179
|
+
new RegExp(`\\.sk-panel:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-decoration-') }\\]\\)\\)`)
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('applies default neutral kind via explicit :not() chain when no kind class present', () =>
|
|
184
|
+
{
|
|
185
|
+
const out = panelCSS();
|
|
186
|
+
// The kind default can't use [class*="sk-"] because every Sleekspace class starts
|
|
187
|
+
// with sk-. Instead we expect an explicit :not() chain for each of the 17 kinds.
|
|
188
|
+
// Check for at least the :where(:not(.sk-neutral):not(.sk-primary)...) pattern.
|
|
189
|
+
expect(out).toMatch(/:where\(:not\(\.sk-neutral\)/);
|
|
190
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
191
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
195
|
+
{
|
|
196
|
+
const out = panelCSS();
|
|
197
|
+
// Selector: .sk-panel:where(:not([class*="sk-size-"])) { --sk-panel-cut-size: ... }
|
|
198
|
+
expect(out).toMatch(
|
|
199
|
+
new RegExp(`\\.sk-panel:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
200
|
+
);
|
|
201
|
+
// The default size block must set the cut-size variable
|
|
202
|
+
expect(out).toMatch(new RegExp(
|
|
203
|
+
String.raw`\.sk-panel:where\(:not\(\[class\*=["']?sk-size-["']?\]\)\)`
|
|
204
|
+
+ String.raw`[^{]*\{[^}]*--sk-panel-cut-size`
|
|
205
|
+
));
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
210
|
+
// Base dual-selector (custom-element tag)
|
|
211
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
it('base rule includes the sk-panel custom-element tag', () =>
|
|
214
|
+
{
|
|
215
|
+
const out = panelCSS();
|
|
216
|
+
expect(out).toMatch(/\.sk-panel,\s*sk-panel\s*\{/);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
220
|
+
// Custom-element attribute-absence defaults
|
|
221
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
224
|
+
{
|
|
225
|
+
it('emits default-kind rule targeting bare <sk-panel> without [kind]', () =>
|
|
226
|
+
{
|
|
227
|
+
expect(panelCSS()).toMatch(/sk-panel:where\(:not\(\[kind\]\)\)/);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('emits default-size rule for bare <sk-panel>', () =>
|
|
231
|
+
{
|
|
232
|
+
expect(panelCSS()).toMatch(/sk-panel:where\(:not\(\[size\]\)\)/);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('emits default-cut rule for bare <sk-panel>', () =>
|
|
236
|
+
{
|
|
237
|
+
expect(panelCSS()).toMatch(/sk-panel:where\(:not\(\[corners\]\)\)/);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('emits default-decoration rule for bare <sk-panel>', () =>
|
|
241
|
+
{
|
|
242
|
+
expect(panelCSS()).toMatch(/sk-panel:where\(:not\(\[decoration-corner\]\)\)/);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
describe('card', () =>
|
|
250
|
+
{
|
|
251
|
+
let css : string;
|
|
252
|
+
|
|
253
|
+
function cardCSS() : string
|
|
254
|
+
{
|
|
255
|
+
if(!css) { css = compileComponent('card'); }
|
|
256
|
+
return css;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
260
|
+
// Card is a structural extension of Panel — it adds layout (flexbox sections) but has
|
|
261
|
+
// no modifier families of its own. Kind, size, and decoration come from Panel. This block
|
|
262
|
+
// verifies the base rule is present so compilation is exercised and regressions are caught.
|
|
263
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
it('compiles without errors', () =>
|
|
266
|
+
{
|
|
267
|
+
expect(() => cardCSS()).not.toThrow();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('emits .sk-panel.sk-card base rule', () =>
|
|
271
|
+
{
|
|
272
|
+
expect(cardCSS()).toMatch(/\.sk-panel\.sk-card/);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
276
|
+
// Kind variants — card inherits kind via Panel. When Card emits its own kind-variant
|
|
277
|
+
// selectors (after the refactor), each kind should appear as both .sk-card.sk-<kind>
|
|
278
|
+
// and sk-card[kind="<kind>"].
|
|
279
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
describe('kind variants', () =>
|
|
282
|
+
{
|
|
283
|
+
for(const kind of KINDS)
|
|
284
|
+
{
|
|
285
|
+
it(`emits .sk-card.sk-${ kind } and sk-card[kind="${ kind }"]`, () =>
|
|
286
|
+
{
|
|
287
|
+
const out = cardCSS();
|
|
288
|
+
expect(out).toMatch(
|
|
289
|
+
new RegExp(dualSelectorRe('card', kind, 'kind', kind))
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
296
|
+
// Defaults-when-absent: kind default (neutral)
|
|
297
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
298
|
+
|
|
299
|
+
describe('defaults-when-absent', () =>
|
|
300
|
+
{
|
|
301
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
302
|
+
{
|
|
303
|
+
const out = cardCSS();
|
|
304
|
+
expect(out).toMatch(/\.sk-card:where\(:not\(\.sk-neutral\)/);
|
|
305
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
306
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
311
|
+
// Base dual-selector (custom-element tag)
|
|
312
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
it('base rule includes the sk-card custom-element tag', () =>
|
|
315
|
+
{
|
|
316
|
+
expect(cardCSS()).toMatch(/\.sk-card,\s*sk-card\s*\{/);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
320
|
+
// Custom-element attribute-absence defaults
|
|
321
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
324
|
+
{
|
|
325
|
+
it('emits default-kind rule targeting bare <sk-card> without [kind]', () =>
|
|
326
|
+
{
|
|
327
|
+
expect(cardCSS()).toMatch(/sk-card:where\(:not\(\[kind\]\)\)/);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
333
|
+
|
|
334
|
+
describe('alert', () =>
|
|
335
|
+
{
|
|
336
|
+
let css : string;
|
|
337
|
+
|
|
338
|
+
function alertCSS() : string
|
|
339
|
+
{
|
|
340
|
+
if(!css) { css = compileComponent('alert'); }
|
|
341
|
+
return css;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
345
|
+
// Kind variants (full $kinds list — alert accepts all 17 kinds)
|
|
346
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
347
|
+
|
|
348
|
+
describe('kind variants', () =>
|
|
349
|
+
{
|
|
350
|
+
for(const kind of KINDS)
|
|
351
|
+
{
|
|
352
|
+
it(`emits .sk-alert.sk-${ kind } and sk-alert[kind="${ kind }"]`, () =>
|
|
353
|
+
{
|
|
354
|
+
const out = alertCSS();
|
|
355
|
+
expect(out).toMatch(
|
|
356
|
+
new RegExp(dualSelectorRe('alert', kind, 'kind', kind))
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
363
|
+
// Boolean modifiers
|
|
364
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
365
|
+
|
|
366
|
+
describe('boolean modifiers', () =>
|
|
367
|
+
{
|
|
368
|
+
it('emits .sk-alert.sk-subtle and sk-alert[subtle]', () =>
|
|
369
|
+
{
|
|
370
|
+
expect(alertCSS()).toMatch(/\.sk-alert\.sk-subtle,\s*sk-alert\[subtle\]/);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
375
|
+
// Defaults-when-absent: kind default (info)
|
|
376
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
377
|
+
|
|
378
|
+
describe('defaults-when-absent', () =>
|
|
379
|
+
{
|
|
380
|
+
it('applies default info kind via :not() chain', () =>
|
|
381
|
+
{
|
|
382
|
+
const out = alertCSS();
|
|
383
|
+
// Alert's default kind is 'info'. The :where(:not()) chain covers all kinds.
|
|
384
|
+
expect(out).toMatch(/\.sk-alert:where\(:not\(\.sk-neutral\)/);
|
|
385
|
+
expect(out).toMatch(/:not\(\.sk-info\)/);
|
|
386
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
391
|
+
// Base dual-selector (custom-element tag)
|
|
392
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
393
|
+
|
|
394
|
+
it('base rule includes the sk-alert custom-element tag', () =>
|
|
395
|
+
{
|
|
396
|
+
expect(alertCSS()).toMatch(/\.sk-alert,\s*sk-alert\s*\{/);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
400
|
+
// Custom-element attribute-absence defaults
|
|
401
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
402
|
+
|
|
403
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
404
|
+
{
|
|
405
|
+
it('emits default-kind rule targeting bare <sk-alert> without [kind]', () =>
|
|
406
|
+
{
|
|
407
|
+
expect(alertCSS()).toMatch(/sk-alert:where\(:not\(\[kind\]\)\)/);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
413
|
+
|
|
414
|
+
describe('divider', () =>
|
|
415
|
+
{
|
|
416
|
+
let css : string;
|
|
417
|
+
|
|
418
|
+
function dividerCSS() : string
|
|
419
|
+
{
|
|
420
|
+
if(!css) { css = compileComponent('divider'); }
|
|
421
|
+
return css;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
425
|
+
// Kind variants (full $kinds list)
|
|
426
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
427
|
+
|
|
428
|
+
describe('kind variants', () =>
|
|
429
|
+
{
|
|
430
|
+
for(const kind of KINDS)
|
|
431
|
+
{
|
|
432
|
+
it(`emits .sk-divider.sk-${ kind } and sk-divider[kind="${ kind }"]`, () =>
|
|
433
|
+
{
|
|
434
|
+
const out = dividerCSS();
|
|
435
|
+
expect(out).toMatch(
|
|
436
|
+
new RegExp(dualSelectorRe('divider', kind, 'kind', kind))
|
|
437
|
+
);
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
443
|
+
// Orientation: single-choice-modifier (horizontal | vertical)
|
|
444
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
445
|
+
|
|
446
|
+
describe('orientation modifiers', () =>
|
|
447
|
+
{
|
|
448
|
+
it('emits .sk-divider.sk-orientation-horizontal and sk-divider[orientation="horizontal"]', () =>
|
|
449
|
+
{
|
|
450
|
+
expect(dividerCSS()).toMatch(
|
|
451
|
+
new RegExp(dualSelectorRe('divider', 'orientation-horizontal', 'orientation', 'horizontal'))
|
|
452
|
+
);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('emits .sk-divider.sk-orientation-vertical and sk-divider[orientation="vertical"]', () =>
|
|
456
|
+
{
|
|
457
|
+
expect(dividerCSS()).toMatch(
|
|
458
|
+
new RegExp(dualSelectorRe('divider', 'orientation-vertical', 'orientation', 'vertical'))
|
|
459
|
+
);
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
464
|
+
// Boolean modifiers
|
|
465
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
466
|
+
|
|
467
|
+
describe('boolean modifiers', () =>
|
|
468
|
+
{
|
|
469
|
+
it('emits .sk-divider.sk-subtle and sk-divider[subtle]', () =>
|
|
470
|
+
{
|
|
471
|
+
expect(dividerCSS()).toMatch(/\.sk-divider\.sk-subtle,\s*sk-divider\[subtle\]/);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
476
|
+
// Defaults-when-absent
|
|
477
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
478
|
+
|
|
479
|
+
describe('defaults-when-absent', () =>
|
|
480
|
+
{
|
|
481
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
482
|
+
{
|
|
483
|
+
const out = dividerCSS();
|
|
484
|
+
expect(out).toMatch(/\.sk-divider:where\(:not\(\.sk-neutral\)/);
|
|
485
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('applies default horizontal orientation when no sk-orientation-* class is present', () =>
|
|
489
|
+
{
|
|
490
|
+
const out = dividerCSS();
|
|
491
|
+
expect(out).toMatch(
|
|
492
|
+
new RegExp(`\\.sk-divider:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-orientation-') }\\]\\)\\)`)
|
|
493
|
+
);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
498
|
+
// Base dual-selector (custom-element tag)
|
|
499
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
500
|
+
|
|
501
|
+
it('base rule includes the sk-divider custom-element tag', () =>
|
|
502
|
+
{
|
|
503
|
+
expect(dividerCSS()).toMatch(/\.sk-divider,\s*sk-divider\s*\{/);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
507
|
+
// Custom-element attribute-absence defaults
|
|
508
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
509
|
+
|
|
510
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
511
|
+
{
|
|
512
|
+
it('emits default-kind rule targeting bare <sk-divider> without [kind]', () =>
|
|
513
|
+
{
|
|
514
|
+
expect(dividerCSS()).toMatch(/sk-divider:where\(:not\(\[kind\]\)\)/);
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
it('emits default-orientation rule targeting bare <sk-divider> without [orientation]', () =>
|
|
518
|
+
{
|
|
519
|
+
expect(dividerCSS()).toMatch(/sk-divider:where\(:not\(\[orientation\]\)\)/);
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
525
|
+
|
|
526
|
+
describe('page', () =>
|
|
527
|
+
{
|
|
528
|
+
let css : string;
|
|
529
|
+
|
|
530
|
+
function pageCSS() : string
|
|
531
|
+
{
|
|
532
|
+
if(!css) { css = compileComponent('page'); }
|
|
533
|
+
return css;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
537
|
+
// Base dual-selector (custom-element tag)
|
|
538
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
539
|
+
|
|
540
|
+
it('base rule includes the sk-page custom-element tag', () =>
|
|
541
|
+
{
|
|
542
|
+
expect(pageCSS()).toMatch(/\.sk-page,\s*sk-page\s*\{/);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
546
|
+
// Boolean modifiers
|
|
547
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
548
|
+
|
|
549
|
+
describe('boolean modifiers', () =>
|
|
550
|
+
{
|
|
551
|
+
it('emits .sk-page.sk-flush and sk-page[flush]', () =>
|
|
552
|
+
{
|
|
553
|
+
expect(pageCSS()).toMatch(/\.sk-page\.sk-flush,\s*sk-page\[flush\]/);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it('emits .sk-page.sk-fixed-header and sk-page[fixed-header]', () =>
|
|
557
|
+
{
|
|
558
|
+
// fixed-header only contains nested child rules (.sk-page-header), so Sass emits
|
|
559
|
+
// .sk-page.sk-fixed-header .sk-page-header, sk-page[fixed-header] .sk-page-header.
|
|
560
|
+
const opts = { descendant: '\\.sk-page-header' };
|
|
561
|
+
expect(pageCSS()).toMatch(new RegExp(
|
|
562
|
+
dualSelectorRe('page', 'fixed-header', 'fixed-header', undefined, opts)
|
|
563
|
+
));
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('emits .sk-page.sk-fixed-footer and sk-page[fixed-footer]', () =>
|
|
567
|
+
{
|
|
568
|
+
// fixed-footer only contains nested child rules (.sk-page-footer), so Sass emits
|
|
569
|
+
// .sk-page.sk-fixed-footer .sk-page-footer, sk-page[fixed-footer] .sk-page-footer.
|
|
570
|
+
const opts = { descendant: '\\.sk-page-footer' };
|
|
571
|
+
expect(pageCSS()).toMatch(new RegExp(
|
|
572
|
+
dualSelectorRe('page', 'fixed-footer', 'fixed-footer', undefined, opts)
|
|
573
|
+
));
|
|
574
|
+
});
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
578
|
+
// Custom-element attribute-absence defaults
|
|
579
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
580
|
+
|
|
581
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
582
|
+
{
|
|
583
|
+
it('emits default-flush rule targeting bare <sk-page> without [flush]', () =>
|
|
584
|
+
{
|
|
585
|
+
expect(pageCSS()).toMatch(/sk-page:where\(:not\(\[flush\]\)\)/);
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
591
|
+
|
|
592
|
+
describe('group', () =>
|
|
593
|
+
{
|
|
594
|
+
let css : string;
|
|
595
|
+
|
|
596
|
+
function groupCSS() : string
|
|
597
|
+
{
|
|
598
|
+
if(!css) { css = compileComponent('group'); }
|
|
599
|
+
return css;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
603
|
+
// Base dual-selector (custom-element tag)
|
|
604
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
605
|
+
|
|
606
|
+
it('base rule includes the sk-group custom-element tag', () =>
|
|
607
|
+
{
|
|
608
|
+
expect(groupCSS()).toMatch(/\.sk-group,\s*sk-group\s*\{/);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
612
|
+
// Orientation: single-choice-modifier (horizontal | vertical)
|
|
613
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
614
|
+
|
|
615
|
+
describe('orientation modifiers', () =>
|
|
616
|
+
{
|
|
617
|
+
it('emits .sk-group.sk-orientation-horizontal and sk-group[orientation="horizontal"]', () =>
|
|
618
|
+
{
|
|
619
|
+
expect(groupCSS()).toMatch(
|
|
620
|
+
new RegExp(dualSelectorRe('group', 'orientation-horizontal', 'orientation', 'horizontal'))
|
|
621
|
+
);
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('emits .sk-group.sk-orientation-vertical and sk-group[orientation="vertical"]', () =>
|
|
625
|
+
{
|
|
626
|
+
expect(groupCSS()).toMatch(
|
|
627
|
+
new RegExp(dualSelectorRe('group', 'orientation-vertical', 'orientation', 'vertical'))
|
|
628
|
+
);
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
633
|
+
// Defaults-when-absent
|
|
634
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
635
|
+
|
|
636
|
+
describe('defaults-when-absent', () =>
|
|
637
|
+
{
|
|
638
|
+
it('applies default horizontal orientation when no sk-orientation-* class is present', () =>
|
|
639
|
+
{
|
|
640
|
+
expect(groupCSS()).toMatch(
|
|
641
|
+
new RegExp(`\\.sk-group:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-orientation-') }\\]\\)\\)`)
|
|
642
|
+
);
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
647
|
+
// Custom-element attribute-absence defaults
|
|
648
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
649
|
+
|
|
650
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
651
|
+
{
|
|
652
|
+
it('emits default-orientation rule targeting bare <sk-group> without [orientation]', () =>
|
|
653
|
+
{
|
|
654
|
+
expect(groupCSS()).toMatch(/sk-group:where\(:not\(\[orientation\]\)\)/);
|
|
655
|
+
});
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
660
|
+
|
|
661
|
+
describe('skeleton', () =>
|
|
662
|
+
{
|
|
663
|
+
let css : string;
|
|
664
|
+
|
|
665
|
+
function skeletonCSS() : string
|
|
666
|
+
{
|
|
667
|
+
if(!css) { css = compileComponent('skeleton'); }
|
|
668
|
+
return css;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
672
|
+
// Base dual-selector (custom-element tag)
|
|
673
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
674
|
+
|
|
675
|
+
it('base rule includes the sk-skeleton custom-element tag', () =>
|
|
676
|
+
{
|
|
677
|
+
expect(skeletonCSS()).toMatch(/\.sk-skeleton,\s*sk-skeleton\s*\{/);
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
681
|
+
// Shape: single-choice-modifier (text | circular | square | rectangular)
|
|
682
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
683
|
+
|
|
684
|
+
describe('shape modifiers', () =>
|
|
685
|
+
{
|
|
686
|
+
const shapes = [ 'text', 'circular', 'square', 'rectangular' ];
|
|
687
|
+
|
|
688
|
+
for(const shape of shapes)
|
|
689
|
+
{
|
|
690
|
+
it(`emits .sk-skeleton.sk-shape-${ shape } and sk-skeleton[shape="${ shape }"]`, () =>
|
|
691
|
+
{
|
|
692
|
+
expect(skeletonCSS()).toMatch(
|
|
693
|
+
new RegExp(
|
|
694
|
+
`\\.sk-skeleton\\.sk-shape-${ shape },\\s*sk-skeleton\\[shape=${ attrValueRe(shape) }\\]`
|
|
695
|
+
)
|
|
696
|
+
);
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
702
|
+
// Animation boolean modifiers
|
|
703
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
704
|
+
|
|
705
|
+
describe('animation modifiers', () =>
|
|
706
|
+
{
|
|
707
|
+
it('emits .sk-skeleton.sk-shimmer and sk-skeleton[shimmer]', () =>
|
|
708
|
+
{
|
|
709
|
+
// shimmer only applies to ::after, so Sass emits the ::after-suffixed selector pair.
|
|
710
|
+
expect(skeletonCSS()).toMatch(/\.sk-skeleton\.sk-shimmer::after,\s*sk-skeleton\[shimmer\]::after/);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it('emits .sk-skeleton.sk-pulse and sk-skeleton[pulse]', () =>
|
|
714
|
+
{
|
|
715
|
+
expect(skeletonCSS()).toMatch(/\.sk-skeleton\.sk-pulse,\s*sk-skeleton\[pulse\]/);
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
720
|
+
// Defaults-when-absent
|
|
721
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
722
|
+
|
|
723
|
+
describe('defaults-when-absent', () =>
|
|
724
|
+
{
|
|
725
|
+
it('applies default rectangular shape when no sk-shape-* class is present', () =>
|
|
726
|
+
{
|
|
727
|
+
expect(skeletonCSS()).toMatch(
|
|
728
|
+
new RegExp(`\\.sk-skeleton:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-shape-') }\\]\\)\\)`)
|
|
729
|
+
);
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
734
|
+
// Custom-element attribute-absence defaults
|
|
735
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
736
|
+
|
|
737
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
738
|
+
{
|
|
739
|
+
it('emits default-shape rule targeting bare <sk-skeleton> without [shape]', () =>
|
|
740
|
+
{
|
|
741
|
+
expect(skeletonCSS()).toMatch(/sk-skeleton:where\(:not\(\[shape\]\)\)/);
|
|
742
|
+
});
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
747
|
+
|
|
748
|
+
describe('progress', () =>
|
|
749
|
+
{
|
|
750
|
+
let css : string;
|
|
751
|
+
|
|
752
|
+
function progressCSS() : string
|
|
753
|
+
{
|
|
754
|
+
if(!css) { css = compileComponent('progress'); }
|
|
755
|
+
return css;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
759
|
+
// Base dual-selector (custom-element tag)
|
|
760
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
761
|
+
|
|
762
|
+
it('base rule includes the sk-progress custom-element tag', () =>
|
|
763
|
+
{
|
|
764
|
+
expect(progressCSS()).toMatch(/\.sk-progress,\s*sk-progress\s*\{/);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
768
|
+
// Kind variants (full $kinds list)
|
|
769
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
770
|
+
|
|
771
|
+
describe('kind variants', () =>
|
|
772
|
+
{
|
|
773
|
+
for(const kind of KINDS)
|
|
774
|
+
{
|
|
775
|
+
it(`emits .sk-progress.sk-${ kind } and sk-progress[kind="${ kind }"]`, () =>
|
|
776
|
+
{
|
|
777
|
+
expect(progressCSS()).toMatch(
|
|
778
|
+
new RegExp(
|
|
779
|
+
dualSelectorRe('progress', kind, 'kind', kind)
|
|
780
|
+
)
|
|
781
|
+
);
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
787
|
+
// Size variants (xs, sm, md, lg, xl)
|
|
788
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
789
|
+
|
|
790
|
+
describe('size variants', () =>
|
|
791
|
+
{
|
|
792
|
+
for(const size of SIZES)
|
|
793
|
+
{
|
|
794
|
+
it(`emits .sk-progress.sk-${ size } and sk-progress[size="${ size }"]`, () =>
|
|
795
|
+
{
|
|
796
|
+
const out = progressCSS();
|
|
797
|
+
expect(out).toMatch(new RegExp(`\\.sk-progress\\.sk-${ size }[,\\s{]`));
|
|
798
|
+
expect(out).toMatch(new RegExp(`sk-progress\\[size=${ attrValueRe(size) }\\]`));
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
804
|
+
// Indeterminate boolean modifier
|
|
805
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
806
|
+
|
|
807
|
+
describe('boolean modifiers', () =>
|
|
808
|
+
{
|
|
809
|
+
it('emits .sk-progress.sk-indeterminate and sk-progress[indeterminate]', () =>
|
|
810
|
+
{
|
|
811
|
+
// indeterminate only targets .sk-progress-indicator, so Sass emits the child selector pair.
|
|
812
|
+
expect(progressCSS()).toMatch(new RegExp(
|
|
813
|
+
dualSelectorRe('progress', 'indeterminate', 'indeterminate', undefined, {
|
|
814
|
+
descendant: '\\.sk-progress-indicator',
|
|
815
|
+
})
|
|
816
|
+
));
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
821
|
+
// Defaults-when-absent
|
|
822
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
823
|
+
|
|
824
|
+
describe('defaults-when-absent', () =>
|
|
825
|
+
{
|
|
826
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
827
|
+
{
|
|
828
|
+
expect(progressCSS()).toMatch(
|
|
829
|
+
new RegExp(`\\.sk-progress:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
830
|
+
);
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
it('applies default primary kind via explicit :not() chain', () =>
|
|
834
|
+
{
|
|
835
|
+
const out = progressCSS();
|
|
836
|
+
expect(out).toMatch(/\.sk-progress:where\(:not\(\.sk-neutral\)/);
|
|
837
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
838
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
839
|
+
});
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
843
|
+
// Custom-element attribute-absence defaults
|
|
844
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
845
|
+
|
|
846
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
847
|
+
{
|
|
848
|
+
it('emits default-kind rule targeting bare <sk-progress> without [kind]', () =>
|
|
849
|
+
{
|
|
850
|
+
expect(progressCSS()).toMatch(/sk-progress:where\(:not\(\[kind\]\)\)/);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
it('emits default-size rule targeting bare <sk-progress> without [size]', () =>
|
|
854
|
+
{
|
|
855
|
+
expect(progressCSS()).toMatch(/sk-progress:where\(:not\(\[size\]\)\)/);
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
861
|
+
|
|
862
|
+
describe('spinner', () =>
|
|
863
|
+
{
|
|
864
|
+
let css : string;
|
|
865
|
+
|
|
866
|
+
function spinnerCSS() : string
|
|
867
|
+
{
|
|
868
|
+
if(!css) { css = compileComponent('spinner'); }
|
|
869
|
+
return css;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
873
|
+
// Base dual-selector (custom-element tag)
|
|
874
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
875
|
+
|
|
876
|
+
it('base rule includes the sk-spinner custom-element tag', () =>
|
|
877
|
+
{
|
|
878
|
+
expect(spinnerCSS()).toMatch(/\.sk-spinner,\s*sk-spinner\s*\{/);
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
882
|
+
// Kind variants (full $kinds list)
|
|
883
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
884
|
+
|
|
885
|
+
describe('kind variants', () =>
|
|
886
|
+
{
|
|
887
|
+
for(const kind of KINDS)
|
|
888
|
+
{
|
|
889
|
+
it(`emits .sk-spinner.sk-${ kind } and sk-spinner[kind="${ kind }"]`, () =>
|
|
890
|
+
{
|
|
891
|
+
expect(spinnerCSS()).toMatch(
|
|
892
|
+
new RegExp(
|
|
893
|
+
dualSelectorRe('spinner', kind, 'kind', kind)
|
|
894
|
+
)
|
|
895
|
+
);
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
901
|
+
// Size variants (xs, sm, md, lg, xl)
|
|
902
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
903
|
+
|
|
904
|
+
describe('size variants', () =>
|
|
905
|
+
{
|
|
906
|
+
for(const size of SIZES)
|
|
907
|
+
{
|
|
908
|
+
it(`emits .sk-spinner.sk-${ size } and sk-spinner[size="${ size }"]`, () =>
|
|
909
|
+
{
|
|
910
|
+
const out = spinnerCSS();
|
|
911
|
+
expect(out).toMatch(new RegExp(`\\.sk-spinner\\.sk-${ size }[,\\s{]`));
|
|
912
|
+
expect(out).toMatch(new RegExp(`sk-spinner\\[size=${ attrValueRe(size) }\\]`));
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
918
|
+
// Defaults-when-absent
|
|
919
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
920
|
+
|
|
921
|
+
describe('defaults-when-absent', () =>
|
|
922
|
+
{
|
|
923
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
924
|
+
{
|
|
925
|
+
expect(spinnerCSS()).toMatch(
|
|
926
|
+
new RegExp(`\\.sk-spinner:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
927
|
+
);
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it('applies default primary kind via explicit :not() chain', () =>
|
|
931
|
+
{
|
|
932
|
+
const out = spinnerCSS();
|
|
933
|
+
expect(out).toMatch(/\.sk-spinner:where\(:not\(\.sk-neutral\)/);
|
|
934
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
935
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
936
|
+
});
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
940
|
+
// Custom-element attribute-absence defaults
|
|
941
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
942
|
+
|
|
943
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
944
|
+
{
|
|
945
|
+
it('emits default-kind rule targeting bare <sk-spinner> without [kind]', () =>
|
|
946
|
+
{
|
|
947
|
+
expect(spinnerCSS()).toMatch(/sk-spinner:where\(:not\(\[kind\]\)\)/);
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
it('emits default-size rule targeting bare <sk-spinner> without [size]', () =>
|
|
951
|
+
{
|
|
952
|
+
expect(spinnerCSS()).toMatch(/sk-spinner:where\(:not\(\[size\]\)\)/);
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
958
|
+
|
|
959
|
+
describe('navbar', () =>
|
|
960
|
+
{
|
|
961
|
+
let css : string;
|
|
962
|
+
|
|
963
|
+
function navbarCSS() : string
|
|
964
|
+
{
|
|
965
|
+
if(!css) { css = compileComponent('navbar'); }
|
|
966
|
+
return css;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
970
|
+
// Base dual-selector (custom-element tag)
|
|
971
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
972
|
+
|
|
973
|
+
it('base rule includes the sk-navbar custom-element tag', () =>
|
|
974
|
+
{
|
|
975
|
+
expect(navbarCSS()).toMatch(/\.sk-navbar,\s*sk-navbar\s*\{/);
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
979
|
+
// Kind variants (full $kinds list)
|
|
980
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
981
|
+
|
|
982
|
+
describe('kind variants', () =>
|
|
983
|
+
{
|
|
984
|
+
for(const kind of KINDS)
|
|
985
|
+
{
|
|
986
|
+
it(`emits .sk-navbar.sk-${ kind } and sk-navbar[kind="${ kind }"]`, () =>
|
|
987
|
+
{
|
|
988
|
+
expect(navbarCSS()).toMatch(
|
|
989
|
+
new RegExp(
|
|
990
|
+
dualSelectorRe('navbar', kind, 'kind', kind)
|
|
991
|
+
)
|
|
992
|
+
);
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
998
|
+
// Boolean modifiers
|
|
999
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1000
|
+
|
|
1001
|
+
describe('boolean modifiers', () =>
|
|
1002
|
+
{
|
|
1003
|
+
it('emits .sk-navbar.sk-sticky and sk-navbar[sticky]', () =>
|
|
1004
|
+
{
|
|
1005
|
+
expect(navbarCSS()).toMatch(/\.sk-navbar\.sk-sticky,\s*sk-navbar\[sticky\]/);
|
|
1006
|
+
});
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1010
|
+
// Defaults-when-absent
|
|
1011
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1012
|
+
|
|
1013
|
+
describe('defaults-when-absent', () =>
|
|
1014
|
+
{
|
|
1015
|
+
it('applies default neutral kind via explicit :not() chain when no kind class present', () =>
|
|
1016
|
+
{
|
|
1017
|
+
const out = navbarCSS();
|
|
1018
|
+
expect(out).toMatch(/\.sk-navbar:where\(:not\(\.sk-neutral\)/);
|
|
1019
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1020
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1025
|
+
// Custom-element attribute-absence defaults
|
|
1026
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1027
|
+
|
|
1028
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1029
|
+
{
|
|
1030
|
+
it('emits default-kind rule targeting bare <sk-navbar> without [kind]', () =>
|
|
1031
|
+
{
|
|
1032
|
+
expect(navbarCSS()).toMatch(/sk-navbar:where\(:not\(\[kind\]\)\)/);
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1038
|
+
|
|
1039
|
+
describe('toolbar', () =>
|
|
1040
|
+
{
|
|
1041
|
+
let css : string;
|
|
1042
|
+
|
|
1043
|
+
function toolbarCSS() : string
|
|
1044
|
+
{
|
|
1045
|
+
if(!css) { css = compileComponent('toolbar'); }
|
|
1046
|
+
return css;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1050
|
+
// Base dual-selector (custom-element tag)
|
|
1051
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1052
|
+
|
|
1053
|
+
it('base rule includes the sk-toolbar custom-element tag', () =>
|
|
1054
|
+
{
|
|
1055
|
+
expect(toolbarCSS()).toMatch(/\.sk-toolbar,\s*sk-toolbar\s*\{/);
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1059
|
+
// Kind variants (full $kinds list)
|
|
1060
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1061
|
+
|
|
1062
|
+
describe('kind variants', () =>
|
|
1063
|
+
{
|
|
1064
|
+
for(const kind of KINDS)
|
|
1065
|
+
{
|
|
1066
|
+
it(`emits .sk-toolbar.sk-${ kind } and sk-toolbar[kind="${ kind }"]`, () =>
|
|
1067
|
+
{
|
|
1068
|
+
expect(toolbarCSS()).toMatch(
|
|
1069
|
+
new RegExp(
|
|
1070
|
+
dualSelectorRe('toolbar', kind, 'kind', kind)
|
|
1071
|
+
)
|
|
1072
|
+
);
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1078
|
+
// Orientation modifiers (single-choice)
|
|
1079
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1080
|
+
|
|
1081
|
+
describe('orientation modifiers', () =>
|
|
1082
|
+
{
|
|
1083
|
+
it('emits .sk-toolbar.sk-orientation-vertical and sk-toolbar[orientation="vertical"]', () =>
|
|
1084
|
+
{
|
|
1085
|
+
expect(toolbarCSS()).toMatch(
|
|
1086
|
+
new RegExp(dualSelectorRe('toolbar', 'orientation-vertical', 'orientation', 'vertical'))
|
|
1087
|
+
);
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
it('emits .sk-toolbar.sk-orientation-horizontal and sk-toolbar[orientation="horizontal"]', () =>
|
|
1091
|
+
{
|
|
1092
|
+
expect(toolbarCSS()).toMatch(
|
|
1093
|
+
new RegExp(dualSelectorRe('toolbar', 'orientation-horizontal', 'orientation', 'horizontal'))
|
|
1094
|
+
);
|
|
1095
|
+
});
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1099
|
+
// Defaults-when-absent
|
|
1100
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1101
|
+
|
|
1102
|
+
describe('defaults-when-absent', () =>
|
|
1103
|
+
{
|
|
1104
|
+
it('applies default neutral kind via explicit :not() chain when no kind class present', () =>
|
|
1105
|
+
{
|
|
1106
|
+
const out = toolbarCSS();
|
|
1107
|
+
expect(out).toMatch(/\.sk-toolbar:where\(:not\(\.sk-neutral\)/);
|
|
1108
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1109
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
it('applies default horizontal orientation when no sk-orientation-* class is present', () =>
|
|
1113
|
+
{
|
|
1114
|
+
expect(toolbarCSS()).toMatch(
|
|
1115
|
+
new RegExp(`\\.sk-toolbar:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-orientation-') }\\]\\)\\)`)
|
|
1116
|
+
);
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1121
|
+
// Custom-element attribute-absence defaults
|
|
1122
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1123
|
+
|
|
1124
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1125
|
+
{
|
|
1126
|
+
it('emits default-kind rule targeting bare <sk-toolbar> without [kind]', () =>
|
|
1127
|
+
{
|
|
1128
|
+
expect(toolbarCSS()).toMatch(/sk-toolbar:where\(:not\(\[kind\]\)\)/);
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
it('emits default-orientation rule targeting bare <sk-toolbar> without [orientation]', () =>
|
|
1132
|
+
{
|
|
1133
|
+
expect(toolbarCSS()).toMatch(/sk-toolbar:where\(:not\(\[orientation\]\)\)/);
|
|
1134
|
+
});
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1139
|
+
|
|
1140
|
+
describe('sidebar', () =>
|
|
1141
|
+
{
|
|
1142
|
+
let css : string;
|
|
1143
|
+
|
|
1144
|
+
function sidebarCSS() : string
|
|
1145
|
+
{
|
|
1146
|
+
if(!css) { css = compileComponent('sidebar'); }
|
|
1147
|
+
return css;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1151
|
+
// Base dual-selector (custom-element tag)
|
|
1152
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1153
|
+
|
|
1154
|
+
it('base rule includes the sk-sidebar custom-element tag', () =>
|
|
1155
|
+
{
|
|
1156
|
+
expect(sidebarCSS()).toMatch(/\.sk-sidebar,\s*sk-sidebar\s*\{/);
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1160
|
+
// Kind variants (semantic kinds only)
|
|
1161
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1162
|
+
|
|
1163
|
+
describe('kind variants', () =>
|
|
1164
|
+
{
|
|
1165
|
+
for(const kind of SEMANTIC_KINDS)
|
|
1166
|
+
{
|
|
1167
|
+
it(`emits .sk-sidebar.sk-${ kind } and sk-sidebar[kind="${ kind }"]`, () =>
|
|
1168
|
+
{
|
|
1169
|
+
expect(sidebarCSS()).toMatch(
|
|
1170
|
+
new RegExp(
|
|
1171
|
+
dualSelectorRe('sidebar', kind, 'kind', kind)
|
|
1172
|
+
)
|
|
1173
|
+
);
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1179
|
+
// Boolean modifiers
|
|
1180
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1181
|
+
|
|
1182
|
+
describe('boolean modifiers', () =>
|
|
1183
|
+
{
|
|
1184
|
+
it('emits .sk-sidebar.sk-dense and sk-sidebar[dense]', () =>
|
|
1185
|
+
{
|
|
1186
|
+
expect(sidebarCSS()).toMatch(/\.sk-sidebar\.sk-dense,\s*sk-sidebar\[dense\]/);
|
|
1187
|
+
});
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1191
|
+
// Defaults-when-absent
|
|
1192
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1193
|
+
|
|
1194
|
+
describe('defaults-when-absent', () =>
|
|
1195
|
+
{
|
|
1196
|
+
it('applies default neutral kind via explicit :not() chain when no kind class present', () =>
|
|
1197
|
+
{
|
|
1198
|
+
const out = sidebarCSS();
|
|
1199
|
+
expect(out).toMatch(/\.sk-sidebar:where\(:not\(\.sk-neutral\)/);
|
|
1200
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1201
|
+
expect(out).toMatch(/:not\(\.sk-danger\)/);
|
|
1202
|
+
expect(out).toMatch(/:not\(\.sk-neon-pink\)/);
|
|
1203
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
1204
|
+
});
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1208
|
+
// Custom-element attribute-absence defaults
|
|
1209
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1210
|
+
|
|
1211
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1212
|
+
{
|
|
1213
|
+
it('emits default-kind rule targeting bare <sk-sidebar> without [kind]', () =>
|
|
1214
|
+
{
|
|
1215
|
+
expect(sidebarCSS()).toMatch(/sk-sidebar:where\(:not\(\[kind\]\)\)/);
|
|
1216
|
+
});
|
|
1217
|
+
});
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1221
|
+
|
|
1222
|
+
describe('breadcrumbs', () =>
|
|
1223
|
+
{
|
|
1224
|
+
let css : string;
|
|
1225
|
+
|
|
1226
|
+
function breadcrumbsCSS() : string
|
|
1227
|
+
{
|
|
1228
|
+
if(!css) { css = compileComponent('breadcrumbs'); }
|
|
1229
|
+
return css;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1233
|
+
// Base dual-selector (custom-element tag)
|
|
1234
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1235
|
+
|
|
1236
|
+
it('base rule includes the sk-breadcrumbs custom-element tag', () =>
|
|
1237
|
+
{
|
|
1238
|
+
expect(breadcrumbsCSS()).toMatch(/\.sk-breadcrumbs,\s*sk-breadcrumbs\s*\{/);
|
|
1239
|
+
});
|
|
1240
|
+
|
|
1241
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1242
|
+
// Kind variants (semantic kinds only)
|
|
1243
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1244
|
+
|
|
1245
|
+
describe('kind variants', () =>
|
|
1246
|
+
{
|
|
1247
|
+
for(const kind of SEMANTIC_KINDS)
|
|
1248
|
+
{
|
|
1249
|
+
it(`emits .sk-breadcrumbs.sk-${ kind } and sk-breadcrumbs[kind="${ kind }"]`, () =>
|
|
1250
|
+
{
|
|
1251
|
+
expect(breadcrumbsCSS()).toMatch(
|
|
1252
|
+
new RegExp(
|
|
1253
|
+
dualSelectorRe('breadcrumbs', kind, 'kind', kind)
|
|
1254
|
+
)
|
|
1255
|
+
);
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1261
|
+
// Defaults-when-absent
|
|
1262
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1263
|
+
|
|
1264
|
+
describe('defaults-when-absent', () =>
|
|
1265
|
+
{
|
|
1266
|
+
it('applies default neutral kind via explicit :not() chain when no kind class present', () =>
|
|
1267
|
+
{
|
|
1268
|
+
const out = breadcrumbsCSS();
|
|
1269
|
+
expect(out).toMatch(/\.sk-breadcrumbs:where\(:not\(\.sk-neutral\)/);
|
|
1270
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1271
|
+
expect(out).toMatch(/:not\(\.sk-danger\)\)/);
|
|
1272
|
+
});
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1275
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1276
|
+
// Custom-element attribute-absence defaults
|
|
1277
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1278
|
+
|
|
1279
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1280
|
+
{
|
|
1281
|
+
it('emits default-kind rule targeting bare <sk-breadcrumbs> without [kind]', () =>
|
|
1282
|
+
{
|
|
1283
|
+
expect(breadcrumbsCSS()).toMatch(/sk-breadcrumbs:where\(:not\(\[kind\]\)\)/);
|
|
1284
|
+
});
|
|
1285
|
+
});
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1289
|
+
|
|
1290
|
+
describe('pagination', () =>
|
|
1291
|
+
{
|
|
1292
|
+
let css : string;
|
|
1293
|
+
|
|
1294
|
+
function paginationCSS() : string
|
|
1295
|
+
{
|
|
1296
|
+
if(!css) { css = compileComponent('pagination'); }
|
|
1297
|
+
return css;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1301
|
+
// Base dual-selector (custom-element tag)
|
|
1302
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1303
|
+
|
|
1304
|
+
it('base rule includes the sk-pagination custom-element tag', () =>
|
|
1305
|
+
{
|
|
1306
|
+
expect(paginationCSS()).toMatch(/\.sk-pagination,\s*sk-pagination\s*\{/);
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1310
|
+
// Kind variants on root (semantic kinds only)
|
|
1311
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1312
|
+
|
|
1313
|
+
describe('kind variants', () =>
|
|
1314
|
+
{
|
|
1315
|
+
for(const kind of SEMANTIC_KINDS)
|
|
1316
|
+
{
|
|
1317
|
+
it(`emits .sk-pagination.sk-${ kind } and sk-pagination[kind="${ kind }"]`, () =>
|
|
1318
|
+
{
|
|
1319
|
+
expect(paginationCSS()).toMatch(
|
|
1320
|
+
new RegExp(
|
|
1321
|
+
dualSelectorRe('pagination', kind, 'kind', kind)
|
|
1322
|
+
)
|
|
1323
|
+
);
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1329
|
+
// Size variants on .sk-pagination-item
|
|
1330
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1331
|
+
|
|
1332
|
+
describe('size variants (pagination-item)', () =>
|
|
1333
|
+
{
|
|
1334
|
+
for(const size of SIZES)
|
|
1335
|
+
{
|
|
1336
|
+
it(`emits .sk-pagination-item.sk-${ size } and sk-pagination-item[size="${ size }"]`, () =>
|
|
1337
|
+
{
|
|
1338
|
+
const out = paginationCSS();
|
|
1339
|
+
expect(out).toMatch(new RegExp(`\\.sk-pagination-item\\.sk-${ size }[,\\s{]`));
|
|
1340
|
+
expect(out).toMatch(new RegExp(`sk-pagination-item\\[size=${ attrValueRe(size) }\\]`));
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1346
|
+
// Variant variants on .sk-pagination-item (solid, outline, subtle, ghost)
|
|
1347
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1348
|
+
|
|
1349
|
+
describe('variant modifiers (pagination-item)', () =>
|
|
1350
|
+
{
|
|
1351
|
+
const variants = [ 'solid', 'outline', 'subtle', 'ghost' ];
|
|
1352
|
+
|
|
1353
|
+
for(const variant of variants)
|
|
1354
|
+
{
|
|
1355
|
+
it(`emits .sk-pagination-item.sk-${ variant } and sk-pagination-item[variant="${ variant }"]`, () =>
|
|
1356
|
+
{
|
|
1357
|
+
expect(paginationCSS()).toMatch(
|
|
1358
|
+
new RegExp(
|
|
1359
|
+
dualSelectorRe('pagination-item', variant, 'variant', variant)
|
|
1360
|
+
)
|
|
1361
|
+
);
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1367
|
+
// Defaults-when-absent
|
|
1368
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1369
|
+
|
|
1370
|
+
describe('defaults-when-absent', () =>
|
|
1371
|
+
{
|
|
1372
|
+
it('applies default neutral kind via explicit :not() chain when no kind class present', () =>
|
|
1373
|
+
{
|
|
1374
|
+
const out = paginationCSS();
|
|
1375
|
+
expect(out).toMatch(/\.sk-pagination:where\(:not\(\.sk-neutral\)/);
|
|
1376
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1377
|
+
expect(out).toMatch(/:not\(\.sk-danger\)\)/);
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
it('applies default md size on pagination-item when no sk-size-* class is present', () =>
|
|
1381
|
+
{
|
|
1382
|
+
expect(paginationCSS()).toMatch(
|
|
1383
|
+
new RegExp(`\\.sk-pagination-item:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
1384
|
+
);
|
|
1385
|
+
});
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1389
|
+
// Custom-element attribute-absence defaults
|
|
1390
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1391
|
+
|
|
1392
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1393
|
+
{
|
|
1394
|
+
it('emits default-kind rule targeting bare <sk-pagination> without [kind]', () =>
|
|
1395
|
+
{
|
|
1396
|
+
expect(paginationCSS()).toMatch(/sk-pagination:where\(:not\(\[kind\]\)\)/);
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
it('emits default-size rule targeting bare <sk-pagination-item> without [size]', () =>
|
|
1400
|
+
{
|
|
1401
|
+
expect(paginationCSS()).toMatch(/sk-pagination-item:where\(:not\(\[size\]\)\)/);
|
|
1402
|
+
});
|
|
1403
|
+
});
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1407
|
+
|
|
1408
|
+
describe('tag', () =>
|
|
1409
|
+
{
|
|
1410
|
+
let css : string;
|
|
1411
|
+
|
|
1412
|
+
function tagCSS() : string
|
|
1413
|
+
{
|
|
1414
|
+
if(!css) { css = compileComponent('tag'); }
|
|
1415
|
+
return css;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1419
|
+
// Base dual-selector (custom-element tag)
|
|
1420
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1421
|
+
|
|
1422
|
+
it('base rule includes the sk-tag custom-element tag', () =>
|
|
1423
|
+
{
|
|
1424
|
+
expect(tagCSS()).toMatch(/\.sk-tag,\s*sk-tag\s*\{/);
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1428
|
+
// Kind variants (full $kinds list)
|
|
1429
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1430
|
+
|
|
1431
|
+
describe('kind variants', () =>
|
|
1432
|
+
{
|
|
1433
|
+
for(const kind of KINDS)
|
|
1434
|
+
{
|
|
1435
|
+
it(`emits .sk-tag.sk-${ kind } and sk-tag[kind="${ kind }"]`, () =>
|
|
1436
|
+
{
|
|
1437
|
+
expect(tagCSS()).toMatch(
|
|
1438
|
+
new RegExp(
|
|
1439
|
+
dualSelectorRe('tag', kind, 'kind', kind)
|
|
1440
|
+
)
|
|
1441
|
+
);
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1447
|
+
// Size variants (xs, sm, md, lg, xl)
|
|
1448
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1449
|
+
|
|
1450
|
+
describe('size variants', () =>
|
|
1451
|
+
{
|
|
1452
|
+
for(const size of SIZES)
|
|
1453
|
+
{
|
|
1454
|
+
it(`emits .sk-tag.sk-${ size } and sk-tag[size="${ size }"]`, () =>
|
|
1455
|
+
{
|
|
1456
|
+
const out = tagCSS();
|
|
1457
|
+
expect(out).toMatch(new RegExp(`\\.sk-tag\\.sk-${ size }[,\\s{]`));
|
|
1458
|
+
expect(out).toMatch(new RegExp(`sk-tag\\[size=${ attrValueRe(size) }\\]`));
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1464
|
+
// Variant variants (solid, outline, subtle, ghost)
|
|
1465
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1466
|
+
|
|
1467
|
+
describe('variant modifiers', () =>
|
|
1468
|
+
{
|
|
1469
|
+
const variants = [ 'solid', 'outline', 'subtle', 'ghost' ];
|
|
1470
|
+
|
|
1471
|
+
for(const variant of variants)
|
|
1472
|
+
{
|
|
1473
|
+
it(`emits .sk-tag.sk-${ variant } and sk-tag[variant="${ variant }"]`, () =>
|
|
1474
|
+
{
|
|
1475
|
+
expect(tagCSS()).toMatch(
|
|
1476
|
+
new RegExp(
|
|
1477
|
+
dualSelectorRe('tag', variant, 'variant', variant)
|
|
1478
|
+
)
|
|
1479
|
+
);
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1485
|
+
// Boolean modifiers
|
|
1486
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1487
|
+
|
|
1488
|
+
describe('boolean modifiers', () =>
|
|
1489
|
+
{
|
|
1490
|
+
it('emits .sk-tag.sk-removable and sk-tag[removable]', () =>
|
|
1491
|
+
{
|
|
1492
|
+
expect(tagCSS()).toMatch(/\.sk-tag\.sk-removable,\s*sk-tag\[removable\]/);
|
|
1493
|
+
});
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1497
|
+
// Defaults-when-absent
|
|
1498
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1499
|
+
|
|
1500
|
+
describe('defaults-when-absent', () =>
|
|
1501
|
+
{
|
|
1502
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
1503
|
+
{
|
|
1504
|
+
const out = tagCSS();
|
|
1505
|
+
expect(out).toMatch(/\.sk-tag:where\(:not\(\.sk-neutral\)/);
|
|
1506
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1507
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
1511
|
+
{
|
|
1512
|
+
expect(tagCSS()).toMatch(
|
|
1513
|
+
new RegExp(`\\.sk-tag:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
1514
|
+
);
|
|
1515
|
+
});
|
|
1516
|
+
});
|
|
1517
|
+
|
|
1518
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1519
|
+
// Custom-element attribute-absence defaults
|
|
1520
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1521
|
+
|
|
1522
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1523
|
+
{
|
|
1524
|
+
it('emits default-kind rule targeting bare <sk-tag> without [kind]', () =>
|
|
1525
|
+
{
|
|
1526
|
+
expect(tagCSS()).toMatch(/sk-tag:where\(:not\(\[kind\]\)\)/);
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
it('emits default-size rule targeting bare <sk-tag> without [size]', () =>
|
|
1530
|
+
{
|
|
1531
|
+
expect(tagCSS()).toMatch(/sk-tag:where\(:not\(\[size\]\)\)/);
|
|
1532
|
+
});
|
|
1533
|
+
});
|
|
1534
|
+
});
|
|
1535
|
+
|
|
1536
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1537
|
+
|
|
1538
|
+
describe('avatar', () =>
|
|
1539
|
+
{
|
|
1540
|
+
let css : string;
|
|
1541
|
+
|
|
1542
|
+
function avatarCSS() : string
|
|
1543
|
+
{
|
|
1544
|
+
if(!css) { css = compileComponent('avatar'); }
|
|
1545
|
+
return css;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1549
|
+
// Base dual-selector (custom-element tag)
|
|
1550
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1551
|
+
|
|
1552
|
+
it('base rule includes the sk-avatar custom-element tag', () =>
|
|
1553
|
+
{
|
|
1554
|
+
expect(avatarCSS()).toMatch(/\.sk-avatar,\s*sk-avatar\s*\{/);
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1558
|
+
// Kind variants (full $kinds list)
|
|
1559
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1560
|
+
|
|
1561
|
+
describe('kind variants', () =>
|
|
1562
|
+
{
|
|
1563
|
+
for(const kind of KINDS)
|
|
1564
|
+
{
|
|
1565
|
+
it(`emits .sk-avatar.sk-${ kind } and sk-avatar[kind="${ kind }"]`, () =>
|
|
1566
|
+
{
|
|
1567
|
+
expect(avatarCSS()).toMatch(
|
|
1568
|
+
new RegExp(
|
|
1569
|
+
dualSelectorRe('avatar', kind, 'kind', kind)
|
|
1570
|
+
)
|
|
1571
|
+
);
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1577
|
+
// Size variants (xs, sm, md, lg, xl)
|
|
1578
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1579
|
+
|
|
1580
|
+
describe('size variants', () =>
|
|
1581
|
+
{
|
|
1582
|
+
for(const size of SIZES)
|
|
1583
|
+
{
|
|
1584
|
+
it(`emits .sk-avatar.sk-${ size } and sk-avatar[size="${ size }"]`, () =>
|
|
1585
|
+
{
|
|
1586
|
+
const out = avatarCSS();
|
|
1587
|
+
expect(out).toMatch(new RegExp(`\\.sk-avatar\\.sk-${ size }[,\\s{]`));
|
|
1588
|
+
expect(out).toMatch(new RegExp(`sk-avatar\\[size=${ attrValueRe(size) }\\]`));
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1594
|
+
// Shape modifiers (single-choice)
|
|
1595
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1596
|
+
|
|
1597
|
+
describe('shape modifiers', () =>
|
|
1598
|
+
{
|
|
1599
|
+
const shapes = [ 'circle', 'square' ];
|
|
1600
|
+
|
|
1601
|
+
for(const shape of shapes)
|
|
1602
|
+
{
|
|
1603
|
+
it(`emits .sk-avatar.sk-shape-${ shape } and sk-avatar[shape="${ shape }"]`, () =>
|
|
1604
|
+
{
|
|
1605
|
+
expect(avatarCSS()).toMatch(
|
|
1606
|
+
new RegExp(
|
|
1607
|
+
`\\.sk-avatar\\.sk-shape-${ shape },\\s*sk-avatar\\[shape=${ attrValueRe(shape) }\\]`
|
|
1608
|
+
)
|
|
1609
|
+
);
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
|
|
1614
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1615
|
+
// Defaults-when-absent
|
|
1616
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1617
|
+
|
|
1618
|
+
describe('defaults-when-absent', () =>
|
|
1619
|
+
{
|
|
1620
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
1621
|
+
{
|
|
1622
|
+
const out = avatarCSS();
|
|
1623
|
+
expect(out).toMatch(/\.sk-avatar:where\(:not\(\.sk-neutral\)/);
|
|
1624
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1625
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
1629
|
+
{
|
|
1630
|
+
expect(avatarCSS()).toMatch(
|
|
1631
|
+
new RegExp(`\\.sk-avatar:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
1632
|
+
);
|
|
1633
|
+
});
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1637
|
+
// Custom-element attribute-absence defaults
|
|
1638
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1639
|
+
|
|
1640
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1641
|
+
{
|
|
1642
|
+
it('emits default-kind rule targeting bare <sk-avatar> without [kind]', () =>
|
|
1643
|
+
{
|
|
1644
|
+
expect(avatarCSS()).toMatch(/sk-avatar:where\(:not\(\[kind\]\)\)/);
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1647
|
+
it('emits default-size rule targeting bare <sk-avatar> without [size]', () =>
|
|
1648
|
+
{
|
|
1649
|
+
expect(avatarCSS()).toMatch(/sk-avatar:where\(:not\(\[size\]\)\)/);
|
|
1650
|
+
});
|
|
1651
|
+
});
|
|
1652
|
+
});
|
|
1653
|
+
|
|
1654
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1655
|
+
|
|
1656
|
+
describe('field', () =>
|
|
1657
|
+
{
|
|
1658
|
+
let css : string;
|
|
1659
|
+
|
|
1660
|
+
function fieldCSS() : string
|
|
1661
|
+
{
|
|
1662
|
+
if(!css) { css = compileComponent('field'); }
|
|
1663
|
+
return css;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1667
|
+
// Base dual-selector (custom-element tag)
|
|
1668
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1669
|
+
|
|
1670
|
+
it('base rule includes the sk-field custom-element tag', () =>
|
|
1671
|
+
{
|
|
1672
|
+
expect(fieldCSS()).toMatch(/\.sk-field,\s*sk-field\s*\{/);
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1676
|
+
// Label position modifiers (single-choice)
|
|
1677
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1678
|
+
|
|
1679
|
+
describe('label position modifiers', () =>
|
|
1680
|
+
{
|
|
1681
|
+
it('emits .sk-field.sk-label-top and sk-field[label-position="top"]', () =>
|
|
1682
|
+
{
|
|
1683
|
+
expect(fieldCSS()).toMatch(
|
|
1684
|
+
new RegExp(`\\.sk-field\\.sk-label-top,\\s*sk-field\\[label-position=${ attrValueRe('top') }\\]`)
|
|
1685
|
+
);
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1688
|
+
it('emits .sk-field.sk-label-left and sk-field[label-position="left"]', () =>
|
|
1689
|
+
{
|
|
1690
|
+
expect(fieldCSS()).toMatch(
|
|
1691
|
+
new RegExp(`\\.sk-field\\.sk-label-left,\\s*sk-field\\[label-position=${ attrValueRe('left') }\\]`)
|
|
1692
|
+
);
|
|
1693
|
+
});
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1697
|
+
// Boolean modifiers
|
|
1698
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1699
|
+
|
|
1700
|
+
describe('boolean modifiers', () =>
|
|
1701
|
+
{
|
|
1702
|
+
it('emits .sk-field.sk-has-error and sk-field[has-error]', () =>
|
|
1703
|
+
{
|
|
1704
|
+
expect(fieldCSS()).toMatch(/\.sk-field\.sk-has-error,\s*sk-field\[has-error\]/);
|
|
1705
|
+
});
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1709
|
+
// Defaults-when-absent
|
|
1710
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1711
|
+
|
|
1712
|
+
describe('defaults-when-absent', () =>
|
|
1713
|
+
{
|
|
1714
|
+
it('applies default column (top) layout when no sk-label-* class is present', () =>
|
|
1715
|
+
{
|
|
1716
|
+
expect(fieldCSS()).toMatch(
|
|
1717
|
+
new RegExp(`\\.sk-field:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-label-') }\\]\\)\\)`)
|
|
1718
|
+
);
|
|
1719
|
+
});
|
|
1720
|
+
});
|
|
1721
|
+
|
|
1722
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1723
|
+
// Custom-element attribute-absence defaults
|
|
1724
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1725
|
+
|
|
1726
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1727
|
+
{
|
|
1728
|
+
it('emits default-label-position rule targeting bare <sk-field> without [label-position]', () =>
|
|
1729
|
+
{
|
|
1730
|
+
expect(fieldCSS()).toMatch(/sk-field:where\(:not\(\[label-position\]\)\)/);
|
|
1731
|
+
});
|
|
1732
|
+
});
|
|
1733
|
+
});
|
|
1734
|
+
|
|
1735
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1736
|
+
|
|
1737
|
+
describe('table', () =>
|
|
1738
|
+
{
|
|
1739
|
+
let css : string;
|
|
1740
|
+
|
|
1741
|
+
function tableCSS() : string
|
|
1742
|
+
{
|
|
1743
|
+
if(!css) { css = compileComponent('table'); }
|
|
1744
|
+
return css;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1748
|
+
// Base dual-selector (custom-element tag)
|
|
1749
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1750
|
+
|
|
1751
|
+
it('base rule includes the sk-table custom-element tag', () =>
|
|
1752
|
+
{
|
|
1753
|
+
expect(tableCSS()).toMatch(/\.sk-table,\s*sk-table\s*\{/);
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1757
|
+
// Kind variants (full $kinds list)
|
|
1758
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1759
|
+
|
|
1760
|
+
describe('kind variants', () =>
|
|
1761
|
+
{
|
|
1762
|
+
for(const kind of KINDS)
|
|
1763
|
+
{
|
|
1764
|
+
it(`emits .sk-table.sk-${ kind } and sk-table[kind="${ kind }"]`, () =>
|
|
1765
|
+
{
|
|
1766
|
+
expect(tableCSS()).toMatch(
|
|
1767
|
+
new RegExp(
|
|
1768
|
+
dualSelectorRe('table', kind, 'kind', kind)
|
|
1769
|
+
)
|
|
1770
|
+
);
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1774
|
+
|
|
1775
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1776
|
+
// Variant variants (subtle)
|
|
1777
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1778
|
+
|
|
1779
|
+
describe('variant modifiers', () =>
|
|
1780
|
+
{
|
|
1781
|
+
it('emits .sk-table.sk-subtle and sk-table[variant="subtle"]', () =>
|
|
1782
|
+
{
|
|
1783
|
+
expect(tableCSS()).toMatch(
|
|
1784
|
+
new RegExp(dualSelectorRe('table', 'subtle', 'variant', 'subtle'))
|
|
1785
|
+
);
|
|
1786
|
+
});
|
|
1787
|
+
});
|
|
1788
|
+
|
|
1789
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1790
|
+
// Boolean modifiers
|
|
1791
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1792
|
+
|
|
1793
|
+
describe('boolean modifiers', () =>
|
|
1794
|
+
{
|
|
1795
|
+
// Bool-modifier blocks only contain nested child selectors, so Sass emits the
|
|
1796
|
+
// child-selector form rather than a bare top-level pair. Check the sub-selector.
|
|
1797
|
+
it('emits .sk-table.sk-striped (striped row rule)', () =>
|
|
1798
|
+
{
|
|
1799
|
+
expect(tableCSS()).toMatch(new RegExp(
|
|
1800
|
+
dualSelectorRe('table', 'striped', 'striped', undefined, {
|
|
1801
|
+
descendant: 'tbody\\s+tr:nth-child\\(even\\)',
|
|
1802
|
+
})
|
|
1803
|
+
));
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1806
|
+
it('emits .sk-table.sk-bordered (bordered cell rule)', () =>
|
|
1807
|
+
{
|
|
1808
|
+
expect(tableCSS()).toMatch(/\.sk-table\.sk-bordered\s+td,/);
|
|
1809
|
+
expect(tableCSS()).toMatch(/sk-table\[bordered\]\s+td,/);
|
|
1810
|
+
});
|
|
1811
|
+
|
|
1812
|
+
it('emits .sk-table.sk-compact (compact cell rule)', () =>
|
|
1813
|
+
{
|
|
1814
|
+
expect(tableCSS()).toMatch(/\.sk-table\.sk-compact\s+thead\s+th,/);
|
|
1815
|
+
expect(tableCSS()).toMatch(/sk-table\[compact\]\s+thead\s+th,/);
|
|
1816
|
+
});
|
|
1817
|
+
|
|
1818
|
+
it('emits .sk-table.sk-comfortable (comfortable cell rule)', () =>
|
|
1819
|
+
{
|
|
1820
|
+
expect(tableCSS()).toMatch(/\.sk-table\.sk-comfortable\s+thead\s+th,/);
|
|
1821
|
+
expect(tableCSS()).toMatch(/sk-table\[comfortable\]\s+thead\s+th,/);
|
|
1822
|
+
});
|
|
1823
|
+
});
|
|
1824
|
+
|
|
1825
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1826
|
+
// Defaults-when-absent
|
|
1827
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1828
|
+
|
|
1829
|
+
describe('defaults-when-absent', () =>
|
|
1830
|
+
{
|
|
1831
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
1832
|
+
{
|
|
1833
|
+
const out = tableCSS();
|
|
1834
|
+
expect(out).toMatch(/\.sk-table:where\(:not\(\.sk-neutral\)/);
|
|
1835
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1836
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
1837
|
+
});
|
|
1838
|
+
});
|
|
1839
|
+
|
|
1840
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1841
|
+
// Custom-element attribute-absence defaults
|
|
1842
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1843
|
+
|
|
1844
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1845
|
+
{
|
|
1846
|
+
it('emits default-kind rule targeting bare <sk-table> without [kind]', () =>
|
|
1847
|
+
{
|
|
1848
|
+
expect(tableCSS()).toMatch(/sk-table:where\(:not\(\[kind\]\)\)/);
|
|
1849
|
+
});
|
|
1850
|
+
});
|
|
1851
|
+
});
|
|
1852
|
+
|
|
1853
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1854
|
+
|
|
1855
|
+
describe('tooltip', () =>
|
|
1856
|
+
{
|
|
1857
|
+
let css : string;
|
|
1858
|
+
|
|
1859
|
+
function tooltipCSS() : string
|
|
1860
|
+
{
|
|
1861
|
+
if(!css) { css = compileComponent('tooltip'); }
|
|
1862
|
+
return css;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1866
|
+
// Base dual-selector (custom-element tag)
|
|
1867
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1868
|
+
|
|
1869
|
+
it('base rule includes the sk-tooltip custom-element tag', () =>
|
|
1870
|
+
{
|
|
1871
|
+
expect(tooltipCSS()).toMatch(/\.sk-tooltip,\s*sk-tooltip\s*\{/);
|
|
1872
|
+
});
|
|
1873
|
+
|
|
1874
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1875
|
+
// Kind variants (full $kinds list)
|
|
1876
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1877
|
+
|
|
1878
|
+
describe('kind variants', () =>
|
|
1879
|
+
{
|
|
1880
|
+
for(const kind of KINDS)
|
|
1881
|
+
{
|
|
1882
|
+
it(`emits .sk-tooltip.sk-${ kind } and sk-tooltip[kind="${ kind }"]`, () =>
|
|
1883
|
+
{
|
|
1884
|
+
expect(tooltipCSS()).toMatch(
|
|
1885
|
+
new RegExp(
|
|
1886
|
+
dualSelectorRe('tooltip', kind, 'kind', kind)
|
|
1887
|
+
)
|
|
1888
|
+
);
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
|
|
1893
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1894
|
+
// Variant variants (solid, outline)
|
|
1895
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1896
|
+
|
|
1897
|
+
describe('variant modifiers', () =>
|
|
1898
|
+
{
|
|
1899
|
+
it('emits .sk-tooltip.sk-solid and sk-tooltip[variant="solid"]', () =>
|
|
1900
|
+
{
|
|
1901
|
+
expect(tooltipCSS()).toMatch(
|
|
1902
|
+
new RegExp(dualSelectorRe('tooltip', 'solid', 'variant', 'solid'))
|
|
1903
|
+
);
|
|
1904
|
+
});
|
|
1905
|
+
|
|
1906
|
+
it('emits .sk-tooltip.sk-outline and sk-tooltip[variant="outline"]', () =>
|
|
1907
|
+
{
|
|
1908
|
+
expect(tooltipCSS()).toMatch(
|
|
1909
|
+
new RegExp(dualSelectorRe('tooltip', 'outline', 'variant', 'outline'))
|
|
1910
|
+
);
|
|
1911
|
+
});
|
|
1912
|
+
});
|
|
1913
|
+
|
|
1914
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1915
|
+
// Placement modifiers (single-choice)
|
|
1916
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1917
|
+
|
|
1918
|
+
describe('placement modifiers', () =>
|
|
1919
|
+
{
|
|
1920
|
+
const placements = [ 'top', 'bottom', 'left', 'right' ];
|
|
1921
|
+
|
|
1922
|
+
for(const placement of placements)
|
|
1923
|
+
{
|
|
1924
|
+
it(`emits .sk-tooltip.sk-placement-${ placement } and sk-tooltip[placement="${ placement }"]`, () =>
|
|
1925
|
+
{
|
|
1926
|
+
expect(tooltipCSS()).toMatch(
|
|
1927
|
+
new RegExp(dualSelectorRe('tooltip', `placement-${ placement }`, 'placement', placement))
|
|
1928
|
+
);
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1934
|
+
// Defaults-when-absent
|
|
1935
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1936
|
+
|
|
1937
|
+
describe('defaults-when-absent', () =>
|
|
1938
|
+
{
|
|
1939
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
1940
|
+
{
|
|
1941
|
+
const out = tooltipCSS();
|
|
1942
|
+
expect(out).toMatch(/\.sk-tooltip:where\(:not\(\.sk-neutral\)/);
|
|
1943
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
1944
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
1945
|
+
});
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1948
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1949
|
+
// Custom-element attribute-absence defaults
|
|
1950
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1951
|
+
|
|
1952
|
+
describe('custom-element defaults (defaults-when-no-attr)', () =>
|
|
1953
|
+
{
|
|
1954
|
+
it('emits default-kind rule targeting bare <sk-tooltip> without [kind]', () =>
|
|
1955
|
+
{
|
|
1956
|
+
expect(tooltipCSS()).toMatch(/sk-tooltip:where\(:not\(\[kind\]\)\)/);
|
|
1957
|
+
});
|
|
1958
|
+
});
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1962
|
+
// Tasks 27-32: Class-only form controls (Button, Input, Textarea, NumberInput, Checkbox, Radio)
|
|
1963
|
+
//
|
|
1964
|
+
// These are class-only — no custom-element tag, no attribute-selector siblings.
|
|
1965
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1966
|
+
|
|
1967
|
+
describe('button', () =>
|
|
1968
|
+
{
|
|
1969
|
+
let css : string;
|
|
1970
|
+
|
|
1971
|
+
function buttonCSS() : string
|
|
1972
|
+
{
|
|
1973
|
+
if(!css) { css = compileComponent('button'); }
|
|
1974
|
+
return css;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1978
|
+
// No attribute selectors emitted for button — class-only component
|
|
1979
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1980
|
+
|
|
1981
|
+
it('does not emit sk-button[kind=...] attribute selectors', () =>
|
|
1982
|
+
{
|
|
1983
|
+
expect(buttonCSS()).not.toMatch(/sk-button\[kind=/);
|
|
1984
|
+
});
|
|
1985
|
+
|
|
1986
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1987
|
+
// Defaults-when-absent: size (md)
|
|
1988
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
1989
|
+
|
|
1990
|
+
describe('defaults-when-absent', () =>
|
|
1991
|
+
{
|
|
1992
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
1993
|
+
{
|
|
1994
|
+
expect(buttonCSS()).toMatch(
|
|
1995
|
+
new RegExp(`\\.sk-button:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
1996
|
+
);
|
|
1997
|
+
});
|
|
1998
|
+
|
|
1999
|
+
it('applies default solid variant when no explicit variant class is present', () =>
|
|
2000
|
+
{
|
|
2001
|
+
const variantNotChain = [ 'solid', 'outline', 'subtle', 'ghost', 'link' ]
|
|
2002
|
+
.map((variant) => `:not\\(\\.sk-${ variant }\\)`).join('');
|
|
2003
|
+
expect(buttonCSS()).toMatch(new RegExp(`\\.sk-button:where\\(${ variantNotChain }\\)`));
|
|
2004
|
+
});
|
|
2005
|
+
|
|
2006
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
2007
|
+
{
|
|
2008
|
+
const out = buttonCSS();
|
|
2009
|
+
expect(out).toMatch(/\.sk-button:where\(:not\(\.sk-neutral\)/);
|
|
2010
|
+
expect(out).toMatch(/:not\(\.sk-primary\)/);
|
|
2011
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
2012
|
+
});
|
|
2013
|
+
});
|
|
2014
|
+
|
|
2015
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2016
|
+
// sk-size-* alias selectors exist alongside sk-<size>
|
|
2017
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2018
|
+
|
|
2019
|
+
describe('size alias selectors', () =>
|
|
2020
|
+
{
|
|
2021
|
+
for(const size of [ 'xs', 'sm', 'md', 'lg', 'xl' ])
|
|
2022
|
+
{
|
|
2023
|
+
it(`emits .sk-button.sk-size-${ size } alongside .sk-button.sk-${ size }`, () =>
|
|
2024
|
+
{
|
|
2025
|
+
expect(buttonCSS()).toMatch(new RegExp(`\\.sk-button\\.sk-size-${ size }[,\\s{]`));
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
});
|
|
2029
|
+
});
|
|
2030
|
+
|
|
2031
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2032
|
+
|
|
2033
|
+
describe('input', () =>
|
|
2034
|
+
{
|
|
2035
|
+
let css : string;
|
|
2036
|
+
|
|
2037
|
+
function inputCSS() : string
|
|
2038
|
+
{
|
|
2039
|
+
if(!css) { css = compileComponent('input'); }
|
|
2040
|
+
return css;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2044
|
+
// No attribute selectors emitted — class-only component
|
|
2045
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2046
|
+
|
|
2047
|
+
it('does not emit sk-input[kind=...] attribute selectors', () =>
|
|
2048
|
+
{
|
|
2049
|
+
expect(inputCSS()).not.toMatch(/sk-input\[kind=/);
|
|
2050
|
+
});
|
|
2051
|
+
|
|
2052
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2053
|
+
// Defaults-when-absent
|
|
2054
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2055
|
+
|
|
2056
|
+
describe('defaults-when-absent', () =>
|
|
2057
|
+
{
|
|
2058
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2059
|
+
{
|
|
2060
|
+
expect(inputCSS()).toMatch(
|
|
2061
|
+
new RegExp(`\\.sk-input:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2062
|
+
);
|
|
2063
|
+
});
|
|
2064
|
+
|
|
2065
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
2066
|
+
{
|
|
2067
|
+
const out = inputCSS();
|
|
2068
|
+
expect(out).toMatch(/\.sk-input:where\(:not\(\.sk-neutral\)/);
|
|
2069
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
2070
|
+
});
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2074
|
+
// sk-size-* alias selectors
|
|
2075
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2076
|
+
|
|
2077
|
+
describe('size alias selectors', () =>
|
|
2078
|
+
{
|
|
2079
|
+
for(const size of [ 'sm', 'md', 'lg', 'xl' ])
|
|
2080
|
+
{
|
|
2081
|
+
it(`emits .sk-input.sk-size-${ size } alongside .sk-input.sk-${ size }`, () =>
|
|
2082
|
+
{
|
|
2083
|
+
expect(inputCSS()).toMatch(new RegExp(`\\.sk-input\\.sk-size-${ size }[,\\s{]`));
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
});
|
|
2087
|
+
});
|
|
2088
|
+
|
|
2089
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2090
|
+
|
|
2091
|
+
describe('textarea', () =>
|
|
2092
|
+
{
|
|
2093
|
+
let css : string;
|
|
2094
|
+
|
|
2095
|
+
function textareaCSS() : string
|
|
2096
|
+
{
|
|
2097
|
+
if(!css) { css = compileComponent('textarea'); }
|
|
2098
|
+
return css;
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2102
|
+
// No attribute selectors emitted — class-only component
|
|
2103
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2104
|
+
|
|
2105
|
+
it('does not emit sk-textarea[kind=...] attribute selectors', () =>
|
|
2106
|
+
{
|
|
2107
|
+
expect(textareaCSS()).not.toMatch(/sk-textarea\[kind=/);
|
|
2108
|
+
});
|
|
2109
|
+
|
|
2110
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2111
|
+
// Defaults-when-absent
|
|
2112
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2113
|
+
|
|
2114
|
+
describe('defaults-when-absent', () =>
|
|
2115
|
+
{
|
|
2116
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2117
|
+
{
|
|
2118
|
+
expect(textareaCSS()).toMatch(
|
|
2119
|
+
new RegExp(`\\.sk-textarea:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2120
|
+
);
|
|
2121
|
+
});
|
|
2122
|
+
|
|
2123
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
2124
|
+
{
|
|
2125
|
+
const out = textareaCSS();
|
|
2126
|
+
expect(out).toMatch(/\.sk-textarea:where\(:not\(\.sk-neutral\)/);
|
|
2127
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
2128
|
+
});
|
|
2129
|
+
});
|
|
2130
|
+
|
|
2131
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2132
|
+
// sk-size-* alias selectors
|
|
2133
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2134
|
+
|
|
2135
|
+
describe('size alias selectors', () =>
|
|
2136
|
+
{
|
|
2137
|
+
for(const size of [ 'sm', 'md', 'lg', 'xl' ])
|
|
2138
|
+
{
|
|
2139
|
+
it(`emits .sk-textarea.sk-size-${ size } alongside .sk-textarea.sk-${ size }`, () =>
|
|
2140
|
+
{
|
|
2141
|
+
expect(textareaCSS()).toMatch(new RegExp(`\\.sk-textarea\\.sk-size-${ size }[,\\s{]`));
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
});
|
|
2145
|
+
});
|
|
2146
|
+
|
|
2147
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2148
|
+
|
|
2149
|
+
describe('number-input', () =>
|
|
2150
|
+
{
|
|
2151
|
+
let css : string;
|
|
2152
|
+
|
|
2153
|
+
function numberInputCSS() : string
|
|
2154
|
+
{
|
|
2155
|
+
if(!css) { css = compileComponent('number-input'); }
|
|
2156
|
+
return css;
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2160
|
+
// No attribute selectors emitted — class-only component
|
|
2161
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2162
|
+
|
|
2163
|
+
it('does not emit sk-number-input[kind=...] attribute selectors', () =>
|
|
2164
|
+
{
|
|
2165
|
+
expect(numberInputCSS()).not.toMatch(/sk-number-input\[kind=/);
|
|
2166
|
+
});
|
|
2167
|
+
|
|
2168
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2169
|
+
// Defaults-when-absent
|
|
2170
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2171
|
+
|
|
2172
|
+
describe('defaults-when-absent', () =>
|
|
2173
|
+
{
|
|
2174
|
+
it('applies default md size when no sk-size-* class is present on the wrapper', () =>
|
|
2175
|
+
{
|
|
2176
|
+
const sizeMatch = `\\[class\\*=${ attrValueRe('sk-size-') }\\]`;
|
|
2177
|
+
expect(numberInputCSS()).toMatch(
|
|
2178
|
+
new RegExp(`\\.sk-number-input-wrapper:where\\(:not\\(${ sizeMatch }\\)\\)`)
|
|
2179
|
+
);
|
|
2180
|
+
});
|
|
2181
|
+
|
|
2182
|
+
it('applies default neutral kind via :not() chain on the wrapper', () =>
|
|
2183
|
+
{
|
|
2184
|
+
const out = numberInputCSS();
|
|
2185
|
+
expect(out).toMatch(/\.sk-number-input-wrapper:where\(:not\(\.sk-neutral\)/);
|
|
2186
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
2187
|
+
});
|
|
2188
|
+
});
|
|
2189
|
+
|
|
2190
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2191
|
+
// sk-size-* alias selectors on wrapper
|
|
2192
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2193
|
+
|
|
2194
|
+
describe('size alias selectors', () =>
|
|
2195
|
+
{
|
|
2196
|
+
for(const size of [ 'sm', 'md', 'lg', 'xl' ])
|
|
2197
|
+
{
|
|
2198
|
+
it(`emits both .sk-size-${ size } and .sk-${ size } on .sk-number-input-wrapper`, () =>
|
|
2199
|
+
{
|
|
2200
|
+
expect(numberInputCSS()).toMatch(
|
|
2201
|
+
new RegExp(`\\.sk-number-input-wrapper\\.sk-size-${ size }[,\\s{\\s]`)
|
|
2202
|
+
);
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
});
|
|
2206
|
+
});
|
|
2207
|
+
|
|
2208
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2209
|
+
|
|
2210
|
+
describe('checkbox', () =>
|
|
2211
|
+
{
|
|
2212
|
+
let css : string;
|
|
2213
|
+
|
|
2214
|
+
function checkboxCSS() : string
|
|
2215
|
+
{
|
|
2216
|
+
if(!css) { css = compileComponent('checkbox'); }
|
|
2217
|
+
return css;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2221
|
+
// No attribute selectors emitted — class-only component
|
|
2222
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2223
|
+
|
|
2224
|
+
it('does not emit sk-checkbox[kind=...] attribute selectors', () =>
|
|
2225
|
+
{
|
|
2226
|
+
expect(checkboxCSS()).not.toMatch(/sk-checkbox\[kind=/);
|
|
2227
|
+
});
|
|
2228
|
+
|
|
2229
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2230
|
+
// Defaults-when-absent
|
|
2231
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2232
|
+
|
|
2233
|
+
describe('defaults-when-absent', () =>
|
|
2234
|
+
{
|
|
2235
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2236
|
+
{
|
|
2237
|
+
expect(checkboxCSS()).toMatch(
|
|
2238
|
+
new RegExp(`\\.sk-checkbox:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2239
|
+
);
|
|
2240
|
+
});
|
|
2241
|
+
|
|
2242
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
2243
|
+
{
|
|
2244
|
+
const out = checkboxCSS();
|
|
2245
|
+
expect(out).toMatch(/\.sk-checkbox:where\(:not\(\.sk-neutral\)/);
|
|
2246
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
2247
|
+
});
|
|
2248
|
+
});
|
|
2249
|
+
|
|
2250
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2251
|
+
// sk-size-* alias selectors
|
|
2252
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2253
|
+
|
|
2254
|
+
describe('size alias selectors', () =>
|
|
2255
|
+
{
|
|
2256
|
+
for(const size of [ 'sm', 'md', 'lg', 'xl' ])
|
|
2257
|
+
{
|
|
2258
|
+
it(`emits .sk-checkbox.sk-size-${ size } alongside .sk-checkbox.sk-${ size }`, () =>
|
|
2259
|
+
{
|
|
2260
|
+
expect(checkboxCSS()).toMatch(new RegExp(`\\.sk-checkbox\\.sk-size-${ size }[,\\s{]`));
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
});
|
|
2264
|
+
});
|
|
2265
|
+
|
|
2266
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2267
|
+
|
|
2268
|
+
describe('radio', () =>
|
|
2269
|
+
{
|
|
2270
|
+
let css : string;
|
|
2271
|
+
|
|
2272
|
+
function radioCSS() : string
|
|
2273
|
+
{
|
|
2274
|
+
if(!css) { css = compileComponent('radio'); }
|
|
2275
|
+
return css;
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2279
|
+
// No attribute selectors emitted — class-only component
|
|
2280
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2281
|
+
|
|
2282
|
+
it('does not emit sk-radio[kind=...] attribute selectors', () =>
|
|
2283
|
+
{
|
|
2284
|
+
expect(radioCSS()).not.toMatch(/sk-radio\[kind=/);
|
|
2285
|
+
});
|
|
2286
|
+
|
|
2287
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2288
|
+
// Defaults-when-absent
|
|
2289
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2290
|
+
|
|
2291
|
+
describe('defaults-when-absent', () =>
|
|
2292
|
+
{
|
|
2293
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2294
|
+
{
|
|
2295
|
+
expect(radioCSS()).toMatch(
|
|
2296
|
+
new RegExp(`\\.sk-radio:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2297
|
+
);
|
|
2298
|
+
});
|
|
2299
|
+
|
|
2300
|
+
it('applies default neutral kind via :not() chain', () =>
|
|
2301
|
+
{
|
|
2302
|
+
const out = radioCSS();
|
|
2303
|
+
expect(out).toMatch(/\.sk-radio:where\(:not\(\.sk-neutral\)/);
|
|
2304
|
+
expect(out).toMatch(/:not\(\.sk-red\)\)/);
|
|
2305
|
+
});
|
|
2306
|
+
});
|
|
2307
|
+
|
|
2308
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2309
|
+
// sk-size-* alias selectors
|
|
2310
|
+
//--------------------------------------------------------------------------------------------------------------
|
|
2311
|
+
|
|
2312
|
+
describe('size alias selectors', () =>
|
|
2313
|
+
{
|
|
2314
|
+
for(const size of [ 'sm', 'md', 'lg', 'xl' ])
|
|
2315
|
+
{
|
|
2316
|
+
it(`emits .sk-radio.sk-size-${ size } alongside .sk-radio.sk-${ size }`, () =>
|
|
2317
|
+
{
|
|
2318
|
+
expect(radioCSS()).toMatch(new RegExp(`\\.sk-radio\\.sk-size-${ size }[,\\s{]`));
|
|
2319
|
+
});
|
|
2320
|
+
}
|
|
2321
|
+
});
|
|
2322
|
+
});
|
|
2323
|
+
|
|
2324
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2325
|
+
|
|
2326
|
+
describe('switch', () =>
|
|
2327
|
+
{
|
|
2328
|
+
const css = compileComponent('switch');
|
|
2329
|
+
|
|
2330
|
+
it('does NOT emit sk-switch[kind=...] attribute selectors', () =>
|
|
2331
|
+
{
|
|
2332
|
+
expect(css).not.toMatch(/sk-switch\[kind=/);
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
describe('defaults-when-absent', () =>
|
|
2336
|
+
{
|
|
2337
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2338
|
+
{
|
|
2339
|
+
expect(css).toMatch(
|
|
2340
|
+
new RegExp(`\\.sk-switch:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2341
|
+
);
|
|
2342
|
+
});
|
|
2343
|
+
|
|
2344
|
+
it('applies default kind via :not() chain when no kind class is present', () =>
|
|
2345
|
+
{
|
|
2346
|
+
expect(css).toMatch(/\.sk-switch:where\(:not\(\.sk-neutral\)/);
|
|
2347
|
+
expect(css).toMatch(/:not\(\.sk-red\)\)/);
|
|
2348
|
+
});
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
describe('size alias selectors', () =>
|
|
2352
|
+
{
|
|
2353
|
+
for(const size of [ 'xs', 'sm', 'md', 'lg', 'xl' ])
|
|
2354
|
+
{
|
|
2355
|
+
it(`emits sk-size-${ size } alias alongside .sk-switch.sk-${ size } on wrapper`, () =>
|
|
2356
|
+
{
|
|
2357
|
+
expect(css).toMatch(new RegExp(`sk-switch\\.sk-size-${ size }`));
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
});
|
|
2361
|
+
});
|
|
2362
|
+
|
|
2363
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2364
|
+
|
|
2365
|
+
describe('select', () =>
|
|
2366
|
+
{
|
|
2367
|
+
const css = compileComponent('select');
|
|
2368
|
+
|
|
2369
|
+
it('does NOT emit sk-select[kind=...] attribute selectors', () =>
|
|
2370
|
+
{
|
|
2371
|
+
expect(css).not.toMatch(/sk-select\[kind=/);
|
|
2372
|
+
});
|
|
2373
|
+
|
|
2374
|
+
describe('defaults-when-absent', () =>
|
|
2375
|
+
{
|
|
2376
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2377
|
+
{
|
|
2378
|
+
expect(css).toMatch(
|
|
2379
|
+
new RegExp(`\\.sk-select:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2380
|
+
);
|
|
2381
|
+
});
|
|
2382
|
+
|
|
2383
|
+
it('applies default neutral kind via :not() chain when no kind class is present', () =>
|
|
2384
|
+
{
|
|
2385
|
+
expect(css).toMatch(/\.sk-select:where\(:not\(\.sk-neutral\)/);
|
|
2386
|
+
expect(css).toMatch(/:not\(\.sk-red\)\)/);
|
|
2387
|
+
});
|
|
2388
|
+
});
|
|
2389
|
+
|
|
2390
|
+
describe('size alias selectors', () =>
|
|
2391
|
+
{
|
|
2392
|
+
for(const size of [ 'sm', 'md', 'lg', 'xl' ])
|
|
2393
|
+
{
|
|
2394
|
+
it(`emits .sk-select.sk-size-${ size } alongside .sk-select.sk-${ size }`, () =>
|
|
2395
|
+
{
|
|
2396
|
+
expect(css).toMatch(new RegExp(`\\.sk-select\\.sk-size-${ size }[,\\s{]`));
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
});
|
|
2400
|
+
});
|
|
2401
|
+
|
|
2402
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2403
|
+
|
|
2404
|
+
describe('slider', () =>
|
|
2405
|
+
{
|
|
2406
|
+
const css = compileComponent('slider');
|
|
2407
|
+
|
|
2408
|
+
it('does NOT emit sk-slider[kind=...] attribute selectors', () =>
|
|
2409
|
+
{
|
|
2410
|
+
expect(css).not.toMatch(/sk-slider\[kind=/);
|
|
2411
|
+
});
|
|
2412
|
+
|
|
2413
|
+
describe('defaults-when-absent', () =>
|
|
2414
|
+
{
|
|
2415
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2416
|
+
{
|
|
2417
|
+
expect(css).toMatch(
|
|
2418
|
+
new RegExp(`\\.sk-slider:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2419
|
+
);
|
|
2420
|
+
});
|
|
2421
|
+
|
|
2422
|
+
it('applies default kind via :not() chain when no kind class is present', () =>
|
|
2423
|
+
{
|
|
2424
|
+
expect(css).toMatch(/\.sk-slider:where\(:not\(\.sk-neutral\)/);
|
|
2425
|
+
expect(css).toMatch(/:not\(\.sk-red\)\)/);
|
|
2426
|
+
});
|
|
2427
|
+
});
|
|
2428
|
+
|
|
2429
|
+
describe('size alias selectors', () =>
|
|
2430
|
+
{
|
|
2431
|
+
for(const size of [ 'xs', 'sm', 'md', 'lg', 'xl' ])
|
|
2432
|
+
{
|
|
2433
|
+
it(`emits .sk-slider.sk-size-${ size } alongside .sk-slider.sk-${ size }`, () =>
|
|
2434
|
+
{
|
|
2435
|
+
expect(css).toMatch(new RegExp(`\\.sk-slider\\.sk-size-${ size }[,\\s{]`));
|
|
2436
|
+
});
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2439
|
+
|
|
2440
|
+
it('preserves native range pseudo-element rules (::-webkit-slider-thumb etc.)', () =>
|
|
2441
|
+
{
|
|
2442
|
+
// Slider uses Reka UI primitives that render range-like elements.
|
|
2443
|
+
// The thumb is a class-based element; verify thumb rules still compile.
|
|
2444
|
+
expect(css).toMatch(/\.sk-slider-thumb/);
|
|
2445
|
+
});
|
|
2446
|
+
});
|
|
2447
|
+
|
|
2448
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2449
|
+
|
|
2450
|
+
describe('color-picker', () =>
|
|
2451
|
+
{
|
|
2452
|
+
const css = compileComponent('color-picker');
|
|
2453
|
+
|
|
2454
|
+
it('does NOT emit sk-color-picker[kind=...] attribute selectors', () =>
|
|
2455
|
+
{
|
|
2456
|
+
expect(css).not.toMatch(/sk-color-picker\[kind=/);
|
|
2457
|
+
});
|
|
2458
|
+
|
|
2459
|
+
describe('defaults-when-absent', () =>
|
|
2460
|
+
{
|
|
2461
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2462
|
+
{
|
|
2463
|
+
expect(css).toMatch(
|
|
2464
|
+
new RegExp(`\\.sk-color-picker:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2465
|
+
);
|
|
2466
|
+
});
|
|
2467
|
+
|
|
2468
|
+
it('applies default neutral kind via :not() chain when no kind class is present', () =>
|
|
2469
|
+
{
|
|
2470
|
+
expect(css).toMatch(/\.sk-color-picker:where\(:not\(\.sk-neutral\)/);
|
|
2471
|
+
expect(css).toMatch(/:not\(\.sk-red\)\)/);
|
|
2472
|
+
});
|
|
2473
|
+
});
|
|
2474
|
+
|
|
2475
|
+
describe('size alias selectors', () =>
|
|
2476
|
+
{
|
|
2477
|
+
for(const size of [ 'sm', 'md', 'lg', 'xl' ])
|
|
2478
|
+
{
|
|
2479
|
+
it(`emits .sk-color-picker.sk-size-${ size } alongside .sk-color-picker.sk-${ size }`, () =>
|
|
2480
|
+
{
|
|
2481
|
+
expect(css).toMatch(new RegExp(`\\.sk-color-picker\\.sk-size-${ size }[,\\s{]`));
|
|
2482
|
+
});
|
|
2483
|
+
}
|
|
2484
|
+
});
|
|
2485
|
+
});
|
|
2486
|
+
|
|
2487
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2488
|
+
|
|
2489
|
+
describe('tags-input', () =>
|
|
2490
|
+
{
|
|
2491
|
+
const css = compileComponent('tags-input');
|
|
2492
|
+
|
|
2493
|
+
it('does NOT emit sk-tags-input[kind=...] attribute selectors', () =>
|
|
2494
|
+
{
|
|
2495
|
+
expect(css).not.toMatch(/sk-tags-input\[kind=/);
|
|
2496
|
+
});
|
|
2497
|
+
|
|
2498
|
+
describe('defaults-when-absent', () =>
|
|
2499
|
+
{
|
|
2500
|
+
it('applies default md size when no sk-size-* class is present', () =>
|
|
2501
|
+
{
|
|
2502
|
+
expect(css).toMatch(
|
|
2503
|
+
new RegExp(`\\.sk-tags-input:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2504
|
+
);
|
|
2505
|
+
});
|
|
2506
|
+
|
|
2507
|
+
it('applies default neutral kind via :not() chain when no kind class is present', () =>
|
|
2508
|
+
{
|
|
2509
|
+
expect(css).toMatch(/\.sk-tags-input:where\(:not\(\.sk-neutral\)/);
|
|
2510
|
+
expect(css).toMatch(/:not\(\.sk-red\)\)/);
|
|
2511
|
+
});
|
|
2512
|
+
});
|
|
2513
|
+
|
|
2514
|
+
describe('size alias selectors', () =>
|
|
2515
|
+
{
|
|
2516
|
+
for(const size of [ 'sm', 'md', 'lg', 'xl' ])
|
|
2517
|
+
{
|
|
2518
|
+
it(`emits .sk-tags-input.sk-size-${ size } alongside .sk-tags-input.sk-${ size }`, () =>
|
|
2519
|
+
{
|
|
2520
|
+
expect(css).toMatch(new RegExp(`\\.sk-tags-input\\.sk-size-${ size }[,\\s{]`));
|
|
2521
|
+
});
|
|
2522
|
+
}
|
|
2523
|
+
});
|
|
2524
|
+
|
|
2525
|
+
it('preserves inner tag chip rules (.sk-tag)', () =>
|
|
2526
|
+
{
|
|
2527
|
+
// tags-input container hosts .sk-tag chips — verify no regression in base rules.
|
|
2528
|
+
expect(css).toMatch(/\.sk-tags-input-container/);
|
|
2529
|
+
});
|
|
2530
|
+
});
|
|
2531
|
+
|
|
2532
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2533
|
+
|
|
2534
|
+
describe('dropdown', () =>
|
|
2535
|
+
{
|
|
2536
|
+
const css = compileComponent('dropdown');
|
|
2537
|
+
|
|
2538
|
+
it('does NOT emit sk-dropdown[kind=...] modifier attribute selectors', () =>
|
|
2539
|
+
{
|
|
2540
|
+
// [open] on <details> is fine; we're only guarding against emitting kind= or size= attribute selectors.
|
|
2541
|
+
expect(css).not.toMatch(/sk-dropdown\[kind=/);
|
|
2542
|
+
expect(css).not.toMatch(/sk-dropdown\[size=/);
|
|
2543
|
+
});
|
|
2544
|
+
|
|
2545
|
+
it('styles the <summary> trigger', () =>
|
|
2546
|
+
{
|
|
2547
|
+
expect(css).toMatch(/\.sk-dropdown\s*>\s*summary/);
|
|
2548
|
+
});
|
|
2549
|
+
|
|
2550
|
+
it('applies default size when no sk-size-* class is present', () =>
|
|
2551
|
+
{
|
|
2552
|
+
expect(css).toMatch(
|
|
2553
|
+
new RegExp(`\\.sk-dropdown:where\\(:not\\(\\[class\\*=${ attrValueRe('sk-size-') }\\]\\)\\)`)
|
|
2554
|
+
);
|
|
2555
|
+
});
|
|
2556
|
+
|
|
2557
|
+
it('applies default kind when no sk-<kind> class is present', () =>
|
|
2558
|
+
{
|
|
2559
|
+
expect(css).toMatch(/\.sk-dropdown:where\(:not\(\.sk-neutral\)/);
|
|
2560
|
+
});
|
|
2561
|
+
|
|
2562
|
+
describe('size alias selectors', () =>
|
|
2563
|
+
{
|
|
2564
|
+
for(const size of [ 'sm', 'md', 'lg' ])
|
|
2565
|
+
{
|
|
2566
|
+
it(`emits .sk-dropdown.sk-size-${ size } alongside .sk-dropdown.sk-${ size }`, () =>
|
|
2567
|
+
{
|
|
2568
|
+
expect(css).toMatch(new RegExp(`\\.sk-dropdown\\.sk-size-${ size }[,\\s{]`));
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
});
|
|
2572
|
+
});
|
|
2573
|
+
});
|
|
2574
|
+
|
|
2575
|
+
//----------------------------------------------------------------------------------------------------------------------
|