@praxisjs/css 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/builder/css-builder.d.ts.map +1 -1
- package/dist/builder/css-builder.js +68 -74
- package/dist/builder/css-builder.js.map +1 -1
- package/dist/builder/keyframes.d.ts +5 -0
- package/dist/builder/keyframes.d.ts.map +1 -1
- package/dist/builder/keyframes.js +12 -14
- package/dist/builder/keyframes.js.map +1 -1
- package/dist/cx.d.ts.map +1 -1
- package/dist/cx.js +23 -18
- package/dist/cx.js.map +1 -1
- package/dist/decorators/param.d.ts +1 -2
- package/dist/decorators/param.d.ts.map +1 -1
- package/dist/decorators/param.js +9 -17
- package/dist/decorators/param.js.map +1 -1
- package/dist/decorators/style.d.ts.map +1 -1
- package/dist/decorators/style.js +61 -33
- package/dist/decorators/style.js.map +1 -1
- package/dist/decorators/styled.d.ts.map +1 -1
- package/dist/decorators/styled.js +112 -95
- package/dist/decorators/styled.js.map +1 -1
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +19 -35
- package/dist/extract.js.map +1 -1
- package/dist/global.d.ts +3 -0
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +9 -4
- package/dist/global.js.map +1 -1
- package/dist/internal/kebab.d.ts +5 -0
- package/dist/internal/kebab.d.ts.map +1 -0
- package/dist/internal/kebab.js +23 -0
- package/dist/internal/kebab.js.map +1 -0
- package/dist/internal/lifecycle.d.ts +15 -0
- package/dist/internal/lifecycle.d.ts.map +1 -0
- package/dist/internal/lifecycle.js +16 -0
- package/dist/internal/lifecycle.js.map +1 -0
- package/dist/internal/registry.d.ts +5 -1
- package/dist/internal/registry.d.ts.map +1 -1
- package/dist/internal/registry.js +9 -23
- package/dist/internal/registry.js.map +1 -1
- package/dist/internal/sheet.d.ts +20 -0
- package/dist/internal/sheet.d.ts.map +1 -0
- package/dist/internal/sheet.js +56 -0
- package/dist/internal/sheet.js.map +1 -0
- package/dist/internal/static-mode.d.ts +2 -0
- package/dist/internal/static-mode.d.ts.map +1 -0
- package/dist/internal/static-mode.js +2 -0
- package/dist/internal/static-mode.js.map +1 -0
- package/dist/preflight.d.ts +3 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +7 -3
- package/dist/preflight.js.map +1 -1
- package/dist/tokens/decorators.d.ts.map +1 -1
- package/dist/tokens/decorators.js +13 -4
- package/dist/tokens/decorators.js.map +1 -1
- package/dist/tokens/theme-instance.d.ts +5 -0
- package/dist/tokens/theme-instance.d.ts.map +1 -1
- package/dist/tokens/theme-instance.js +25 -17
- package/dist/tokens/theme-instance.js.map +1 -1
- package/dist/tokens/token-sheet.d.ts.map +1 -1
- package/dist/tokens/token-sheet.js +12 -9
- package/dist/tokens/token-sheet.js.map +1 -1
- package/package.json +13 -5
- package/src/builder/css-builder.ts +79 -83
- package/src/builder/keyframes.ts +12 -20
- package/src/cx.ts +20 -13
- package/src/decorators/param.ts +12 -16
- package/src/decorators/style.ts +79 -35
- package/src/decorators/styled.ts +127 -111
- package/src/extract.ts +18 -34
- package/src/global.ts +10 -9
- package/src/internal/kebab.ts +23 -0
- package/src/internal/lifecycle.ts +21 -0
- package/src/internal/registry.ts +9 -35
- package/src/internal/sheet.ts +71 -0
- package/src/internal/static-mode.ts +6 -0
- package/src/preflight.ts +8 -3
- package/src/tokens/decorators.ts +15 -3
- package/src/tokens/theme-instance.ts +26 -16
- package/src/tokens/token-sheet.ts +12 -12
- package/dist/__tests__/css-builder.test.d.ts +0 -2
- package/dist/__tests__/css-builder.test.d.ts.map +0 -1
- package/dist/__tests__/css-builder.test.js +0 -198
- package/dist/__tests__/css-builder.test.js.map +0 -1
- package/dist/__tests__/cx.test.d.ts +0 -2
- package/dist/__tests__/cx.test.d.ts.map +0 -1
- package/dist/__tests__/cx.test.js +0 -47
- package/dist/__tests__/cx.test.js.map +0 -1
- package/dist/__tests__/decorators.test.d.ts +0 -2
- package/dist/__tests__/decorators.test.d.ts.map +0 -1
- package/dist/__tests__/decorators.test.js +0 -264
- package/dist/__tests__/decorators.test.js.map +0 -1
- package/dist/__tests__/extract.test.d.ts +0 -2
- package/dist/__tests__/extract.test.d.ts.map +0 -1
- package/dist/__tests__/extract.test.js +0 -416
- package/dist/__tests__/extract.test.js.map +0 -1
- package/dist/__tests__/global.test.d.ts +0 -2
- package/dist/__tests__/global.test.d.ts.map +0 -1
- package/dist/__tests__/global.test.js +0 -136
- package/dist/__tests__/global.test.js.map +0 -1
- package/dist/__tests__/keyframes.test.d.ts +0 -2
- package/dist/__tests__/keyframes.test.d.ts.map +0 -1
- package/dist/__tests__/keyframes.test.js +0 -90
- package/dist/__tests__/keyframes.test.js.map +0 -1
- package/dist/__tests__/preflight.test.d.ts +0 -2
- package/dist/__tests__/preflight.test.d.ts.map +0 -1
- package/dist/__tests__/preflight.test.js +0 -74
- package/dist/__tests__/preflight.test.js.map +0 -1
- package/dist/__tests__/registry.test.d.ts +0 -2
- package/dist/__tests__/registry.test.d.ts.map +0 -1
- package/dist/__tests__/registry.test.js +0 -132
- package/dist/__tests__/registry.test.js.map +0 -1
- package/dist/__tests__/style.test.d.ts +0 -2
- package/dist/__tests__/style.test.d.ts.map +0 -1
- package/dist/__tests__/style.test.js +0 -281
- package/dist/__tests__/style.test.js.map +0 -1
- package/dist/__tests__/styled.test.d.ts +0 -2
- package/dist/__tests__/styled.test.d.ts.map +0 -1
- package/dist/__tests__/styled.test.js +0 -904
- package/dist/__tests__/styled.test.js.map +0 -1
- package/dist/__tests__/theme-instance.test.d.ts +0 -2
- package/dist/__tests__/theme-instance.test.d.ts.map +0 -1
- package/dist/__tests__/theme-instance.test.js +0 -309
- package/dist/__tests__/theme-instance.test.js.map +0 -1
- package/dist/__tests__/theme-uninit.test.d.ts +0 -2
- package/dist/__tests__/theme-uninit.test.d.ts.map +0 -1
- package/dist/__tests__/theme-uninit.test.js +0 -10
- package/dist/__tests__/theme-uninit.test.js.map +0 -1
- package/dist/__tests__/token-sheet.test.d.ts +0 -2
- package/dist/__tests__/token-sheet.test.d.ts.map +0 -1
- package/dist/__tests__/token-sheet.test.js +0 -74
- package/dist/__tests__/token-sheet.test.js.map +0 -1
- package/src/__tests__/css-builder.test.ts +0 -230
- package/src/__tests__/cx.test.ts +0 -63
- package/src/__tests__/decorators.test.ts +0 -127
- package/src/__tests__/extract.test.ts +0 -459
- package/src/__tests__/global.test.ts +0 -163
- package/src/__tests__/keyframes.test.ts +0 -103
- package/src/__tests__/preflight.test.ts +0 -85
- package/src/__tests__/registry.test.ts +0 -151
- package/src/__tests__/style.test.ts +0 -170
- package/src/__tests__/styled.test.ts +0 -443
- package/src/__tests__/theme-instance.test.ts +0 -374
- package/src/__tests__/theme-uninit.test.ts +0 -12
- package/src/__tests__/token-sheet.test.ts +0 -87
- package/tsconfig.json +0 -8
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { StatefulComponent } from "@praxisjs/core";
|
|
4
|
-
import { setComponentAnchor } from "@praxisjs/core/internal";
|
|
5
|
-
import { Component } from "@praxisjs/decorators";
|
|
6
|
-
import { Param } from "../decorators/param";
|
|
7
|
-
import { ReactiveStylesheet, Stylesheet } from "../base/stylesheet";
|
|
8
|
-
import { Styled } from "../decorators/styled";
|
|
9
|
-
|
|
10
|
-
type WithLifecycle = { onMount?(): void; onUnmount?(): void };
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
document
|
|
14
|
-
.head
|
|
15
|
-
.querySelectorAll("style[data-praxis-hash]")
|
|
16
|
-
.forEach((el) => el.remove());
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
document
|
|
21
|
-
.head
|
|
22
|
-
.querySelectorAll("style[data-praxis-hash]")
|
|
23
|
-
.forEach((el) => el.remove());
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// ─── CSS class fields ($-prefixed) ────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
class BtnStyle extends Stylesheet {
|
|
29
|
-
$base = `display: inline-flex; padding: 8px 16px;`;
|
|
30
|
-
$primary = `background: var(--accent); color: white;`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
describe("@Styled — $-prefixed CSS class fields", () => {
|
|
34
|
-
it("transforms $-fields to scoped class names", () => {
|
|
35
|
-
class Comp {
|
|
36
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const { $btn } = new Comp();
|
|
40
|
-
expect($btn.$base).toMatch(/^prx-base-/);
|
|
41
|
-
expect($btn.$primary).toMatch(/^prx-primary-/);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("class names are distinct for distinct CSS", () => {
|
|
45
|
-
class Comp {
|
|
46
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const { $btn } = new Comp();
|
|
50
|
-
expect($btn.$base).not.toBe($btn.$primary);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("same CSS content produces the same hash suffix across different keys", () => {
|
|
54
|
-
class SA extends Stylesheet { $x = `color: red;`; }
|
|
55
|
-
class SB extends Stylesheet { $y = `color: red;`; }
|
|
56
|
-
|
|
57
|
-
const hashA = new (class { @Styled(SA) $a!: SA; })().$a.$x.split("-").pop();
|
|
58
|
-
const hashB = new (class { @Styled(SB) $b!: SB; })().$b.$y.split("-").pop();
|
|
59
|
-
expect(hashA).toBe(hashB);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("caches the class map — same object reference across instances", () => {
|
|
63
|
-
class Comp {
|
|
64
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
expect(new Comp().$btn).toBe(new Comp().$btn);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("injects a style element into document.head eagerly at decoration time", () => {
|
|
71
|
-
// Before any class definition: no style element
|
|
72
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).toBeNull();
|
|
73
|
-
|
|
74
|
-
// Defining a class with @Styled injects immediately (before any mount)
|
|
75
|
-
class Comp {
|
|
76
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).not.toBeNull();
|
|
80
|
-
// Style is present even before instantiation or mount
|
|
81
|
-
void Comp;
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("style is present before onMount is called", () => {
|
|
85
|
-
class Comp {
|
|
86
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Style injected at decoration time — no mount needed
|
|
90
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).not.toBeNull();
|
|
91
|
-
const inst = new Comp() as WithLifecycle;
|
|
92
|
-
// Still present before mount
|
|
93
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).not.toBeNull();
|
|
94
|
-
inst.onMount?.();
|
|
95
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).not.toBeNull();
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("injected CSS contains the generated class names", () => {
|
|
99
|
-
class Comp {
|
|
100
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const css = document.head.querySelector("style[data-praxis-hash]")?.textContent ?? "";
|
|
104
|
-
expect(css).toContain(new Comp().$btn.$base);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("reference-counts — injects once for multiple instances", () => {
|
|
108
|
-
class Comp {
|
|
109
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const i1 = new Comp() as WithLifecycle;
|
|
113
|
-
const i2 = new Comp() as WithLifecycle;
|
|
114
|
-
i1.onMount?.();
|
|
115
|
-
i2.onMount?.();
|
|
116
|
-
expect(document.head.querySelectorAll("style[data-praxis-hash]").length).toBe(1);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("style persists after all instances unmount (decoration-time base reference)", () => {
|
|
120
|
-
// The eager injection at decoration time holds a permanent reference.
|
|
121
|
-
// Unmounting all instances decrements their ref-count but does not remove the style.
|
|
122
|
-
class Comp {
|
|
123
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const inst = new Comp() as WithLifecycle;
|
|
127
|
-
inst.onMount?.();
|
|
128
|
-
inst.onUnmount?.();
|
|
129
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).not.toBeNull();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("keeps style when one of two instances unmounts", () => {
|
|
133
|
-
class Comp {
|
|
134
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const i1 = new Comp() as WithLifecycle;
|
|
138
|
-
const i2 = new Comp() as WithLifecycle;
|
|
139
|
-
i1.onMount?.();
|
|
140
|
-
i2.onMount?.();
|
|
141
|
-
|
|
142
|
-
i1.onUnmount?.();
|
|
143
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).not.toBeNull();
|
|
144
|
-
|
|
145
|
-
i2.onUnmount?.();
|
|
146
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).not.toBeNull();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("multiple @Styled fields inject separate stylesheets", () => {
|
|
150
|
-
class SA extends Stylesheet { $foo = `color: tomato;`; }
|
|
151
|
-
class SB extends Stylesheet { $bar = `color: steelblue;`; }
|
|
152
|
-
|
|
153
|
-
class Comp {
|
|
154
|
-
@Styled(SA) $a!: SA;
|
|
155
|
-
@Styled(SB) $b!: SB;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Both injected at decoration time
|
|
159
|
-
expect(document.head.querySelectorAll("style[data-praxis-hash]").length).toBe(2);
|
|
160
|
-
|
|
161
|
-
const inst = new Comp() as WithLifecycle;
|
|
162
|
-
inst.onMount?.();
|
|
163
|
-
expect(document.head.querySelectorAll("style[data-praxis-hash]").length).toBe(2);
|
|
164
|
-
inst.onUnmount?.();
|
|
165
|
-
expect(document.head.querySelectorAll("style[data-praxis-hash]").length).toBe(2);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it("works with @Component() in the class stack", () => {
|
|
169
|
-
@Component()
|
|
170
|
-
class Card extends StatefulComponent {
|
|
171
|
-
@Styled(BtnStyle) $btn!: BtnStyle;
|
|
172
|
-
render() { return document.createElement("div"); }
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const inst = new Card();
|
|
176
|
-
expect(inst.$btn.$base).toMatch(/^prx-base-/);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it("skips $-prefixed fields that are neither string nor CSSBuilder", () => {
|
|
180
|
-
class S extends Stylesheet {
|
|
181
|
-
$root = 42 as unknown as string;
|
|
182
|
-
$base = `display: flex;`;
|
|
183
|
-
}
|
|
184
|
-
class Comp {
|
|
185
|
-
@Styled(S) $s!: S;
|
|
186
|
-
}
|
|
187
|
-
const inst = new Comp();
|
|
188
|
-
expect((inst.$s as unknown as Record<string, unknown>).$root).toBeUndefined();
|
|
189
|
-
expect(inst.$s.$base).toMatch(/^prx-base-/);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("processes CSSBuilder fields (this.css({}))", () => {
|
|
193
|
-
class S extends Stylesheet {
|
|
194
|
-
$root = this.css({ display: "flex", gap: "12px" })
|
|
195
|
-
.hover({ opacity: 0.9 });
|
|
196
|
-
}
|
|
197
|
-
class Comp {
|
|
198
|
-
@Styled(S) $s!: S;
|
|
199
|
-
}
|
|
200
|
-
const inst = new Comp() as WithLifecycle & Comp;
|
|
201
|
-
expect(inst.$s.$root).toMatch(/^prx-root-/);
|
|
202
|
-
|
|
203
|
-
const css = document.head.querySelector("style[data-praxis-hash]")?.textContent ?? "";
|
|
204
|
-
expect(css).toContain("display: flex;");
|
|
205
|
-
expect(css).toContain("&:hover");
|
|
206
|
-
inst.onUnmount?.();
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// ─── @Param() reactive CSS vars ─────────────────────────────────────────────────
|
|
211
|
-
|
|
212
|
-
function makeAnchor(parent: HTMLElement): Comment {
|
|
213
|
-
const anchor = document.createComment("[/Comp]");
|
|
214
|
-
parent.appendChild(anchor);
|
|
215
|
-
return anchor;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
describe("@Param — reactive CSS custom properties", () => {
|
|
219
|
-
it("@Param() field exposes a getter/setter on the stylesheet object", () => {
|
|
220
|
-
class S extends ReactiveStylesheet {
|
|
221
|
-
@Param() color = "#3b82f6";
|
|
222
|
-
$root = `background: var(--color);`;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
class Comp extends StatefulComponent {
|
|
226
|
-
@Styled(S) $s!: S;
|
|
227
|
-
render() { return document.createElement("div"); }
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const inst = new Comp();
|
|
231
|
-
expect(inst.$s.color).toBe("#3b82f6");
|
|
232
|
-
|
|
233
|
-
inst.$s.color = "red";
|
|
234
|
-
expect(inst.$s.color).toBe("red");
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it("sets the CSS var on the element when onMount fires", () => {
|
|
238
|
-
const container = document.createElement("div");
|
|
239
|
-
document.body.appendChild(container);
|
|
240
|
-
|
|
241
|
-
class S extends ReactiveStylesheet {
|
|
242
|
-
@Param() accent = "#3b82f6";
|
|
243
|
-
$root = `color: var(--accent);`;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
class Comp extends StatefulComponent {
|
|
247
|
-
constructor() {
|
|
248
|
-
super();
|
|
249
|
-
setComponentAnchor(this, makeAnchor(container));
|
|
250
|
-
}
|
|
251
|
-
@Styled(S) $s!: S;
|
|
252
|
-
render() { return document.createElement("div"); }
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const inst = new Comp() as WithLifecycle;
|
|
256
|
-
inst.onMount?.();
|
|
257
|
-
|
|
258
|
-
expect(container.style.getPropertyValue("--accent")).toBe("#3b82f6");
|
|
259
|
-
container.remove();
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("updates the CSS var reactively when the field is set", () => {
|
|
263
|
-
const container = document.createElement("div");
|
|
264
|
-
document.body.appendChild(container);
|
|
265
|
-
|
|
266
|
-
class S extends ReactiveStylesheet {
|
|
267
|
-
@Param() color = "blue";
|
|
268
|
-
$root = `color: var(--color);`;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
class Comp extends StatefulComponent {
|
|
272
|
-
constructor() {
|
|
273
|
-
super();
|
|
274
|
-
setComponentAnchor(this, makeAnchor(container));
|
|
275
|
-
}
|
|
276
|
-
@Styled(S) $s!: S;
|
|
277
|
-
render() { return document.createElement("div"); }
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const inst = new Comp() as WithLifecycle & Comp;
|
|
281
|
-
inst.onMount?.();
|
|
282
|
-
expect(container.style.getPropertyValue("--color")).toBe("blue");
|
|
283
|
-
|
|
284
|
-
inst.$s.color = "red";
|
|
285
|
-
expect(container.style.getPropertyValue("--color")).toBe("red");
|
|
286
|
-
|
|
287
|
-
container.remove();
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it("removes CSS vars on onUnmount", () => {
|
|
291
|
-
const container = document.createElement("div");
|
|
292
|
-
document.body.appendChild(container);
|
|
293
|
-
|
|
294
|
-
class S extends ReactiveStylesheet {
|
|
295
|
-
@Param() size = "16px";
|
|
296
|
-
$root = `font-size: var(--size);`;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
class Comp extends StatefulComponent {
|
|
300
|
-
constructor() {
|
|
301
|
-
super();
|
|
302
|
-
setComponentAnchor(this, makeAnchor(container));
|
|
303
|
-
}
|
|
304
|
-
@Styled(S) $s!: S;
|
|
305
|
-
render() { return document.createElement("div"); }
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const inst = new Comp() as WithLifecycle;
|
|
309
|
-
inst.onMount?.();
|
|
310
|
-
expect(container.style.getPropertyValue("--size")).toBe("16px");
|
|
311
|
-
|
|
312
|
-
inst.onUnmount?.();
|
|
313
|
-
expect(container.style.getPropertyValue("--size")).toBe("");
|
|
314
|
-
|
|
315
|
-
container.remove();
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it("each instance gets independent signal state", () => {
|
|
319
|
-
const c1 = document.createElement("div");
|
|
320
|
-
const c2 = document.createElement("div");
|
|
321
|
-
document.body.append(c1, c2);
|
|
322
|
-
|
|
323
|
-
class S extends ReactiveStylesheet {
|
|
324
|
-
@Param() color = "blue";
|
|
325
|
-
$root = `color: var(--color);`;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
class Comp extends StatefulComponent {
|
|
329
|
-
constructor(anchor: Comment) {
|
|
330
|
-
super();
|
|
331
|
-
setComponentAnchor(this, anchor);
|
|
332
|
-
}
|
|
333
|
-
@Styled(S) $s!: S;
|
|
334
|
-
render() { return document.createElement("div"); }
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const i1 = new Comp(makeAnchor(c1)) as WithLifecycle & Comp;
|
|
338
|
-
const i2 = new Comp(makeAnchor(c2)) as WithLifecycle & Comp;
|
|
339
|
-
i1.onMount?.();
|
|
340
|
-
i2.onMount?.();
|
|
341
|
-
|
|
342
|
-
i1.$s.color = "red";
|
|
343
|
-
expect(c1.style.getPropertyValue("--color")).toBe("red");
|
|
344
|
-
expect(c2.style.getPropertyValue("--color")).toBe("blue");
|
|
345
|
-
|
|
346
|
-
c1.remove();
|
|
347
|
-
c2.remove();
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
it("Stylesheet with only @Param() fields (no $ CSS) — no style element injected", () => {
|
|
351
|
-
class S extends ReactiveStylesheet {
|
|
352
|
-
@Param() color = "red";
|
|
353
|
-
}
|
|
354
|
-
class Comp extends StatefulComponent {
|
|
355
|
-
@Styled(S) $s!: S;
|
|
356
|
-
render() { return document.createElement("div"); }
|
|
357
|
-
}
|
|
358
|
-
const inst = new Comp() as WithLifecycle;
|
|
359
|
-
inst.onMount?.();
|
|
360
|
-
expect(document.head.querySelector("style[data-praxis-hash]")).toBeNull();
|
|
361
|
-
inst.onUnmount?.();
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
it("@Param() field with non-string/non-number value is skipped (defensive)", () => {
|
|
365
|
-
class S extends ReactiveStylesheet {
|
|
366
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
367
|
-
@(Param() as any) items = [] as unknown as string;
|
|
368
|
-
$root = `display: flex;`;
|
|
369
|
-
}
|
|
370
|
-
class Comp extends StatefulComponent {
|
|
371
|
-
@Styled(S) $s!: S;
|
|
372
|
-
render() { return document.createElement("div"); }
|
|
373
|
-
}
|
|
374
|
-
const inst = new Comp();
|
|
375
|
-
expect(inst.$s.$root).toMatch(/^prx-root-/);
|
|
376
|
-
expect((inst.$s as unknown as Record<string, unknown>).items).toBeUndefined();
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it("non-$ non-@Param() fields in Stylesheet are silently ignored", () => {
|
|
380
|
-
class S extends ReactiveStylesheet {
|
|
381
|
-
$root = `display: flex;`;
|
|
382
|
-
description = "a plain field that should be skipped";
|
|
383
|
-
}
|
|
384
|
-
class Comp extends StatefulComponent {
|
|
385
|
-
@Styled(S) $s!: S;
|
|
386
|
-
render() { return document.createElement("div"); }
|
|
387
|
-
}
|
|
388
|
-
const inst = new Comp();
|
|
389
|
-
expect(inst.$s.$root).toMatch(/^prx-root-/);
|
|
390
|
-
expect((inst.$s as unknown as Record<string, unknown>).description).toBeUndefined();
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it("two @Param() fields on the same class — PARAM_META entry reused", () => {
|
|
394
|
-
class S extends ReactiveStylesheet {
|
|
395
|
-
@Param() color = "blue";
|
|
396
|
-
@Param() radius = "8px";
|
|
397
|
-
$root = `color: var(--color); border-radius: var(--radius);`;
|
|
398
|
-
}
|
|
399
|
-
class Comp extends StatefulComponent {
|
|
400
|
-
@Styled(S) $s!: S;
|
|
401
|
-
render() { return document.createElement("div"); }
|
|
402
|
-
}
|
|
403
|
-
const inst = new Comp();
|
|
404
|
-
expect(inst.$s.color).toBe("blue");
|
|
405
|
-
expect(inst.$s.radius).toBe("8px");
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
it("onUnmount removes CSS vars from element when el is present", () => {
|
|
409
|
-
const container = document.createElement("div");
|
|
410
|
-
document.body.appendChild(container);
|
|
411
|
-
|
|
412
|
-
class S extends ReactiveStylesheet {
|
|
413
|
-
@Param() color = "red";
|
|
414
|
-
$root = `color: var(--color);`;
|
|
415
|
-
}
|
|
416
|
-
class Comp extends StatefulComponent {
|
|
417
|
-
constructor() {
|
|
418
|
-
super();
|
|
419
|
-
setComponentAnchor(this, makeAnchor(container));
|
|
420
|
-
}
|
|
421
|
-
@Styled(S) $s!: S;
|
|
422
|
-
render() { return document.createElement("div"); }
|
|
423
|
-
}
|
|
424
|
-
const inst = new Comp() as WithLifecycle;
|
|
425
|
-
inst.onMount?.();
|
|
426
|
-
expect(container.style.getPropertyValue("--color")).toBe("red");
|
|
427
|
-
inst.onUnmount?.();
|
|
428
|
-
expect(container.style.getPropertyValue("--color")).toBe("");
|
|
429
|
-
container.remove();
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
it("@Param() cannot be applied outside ReactiveStylesheet — enforced at TypeScript level", () => {
|
|
433
|
-
expect(() => {
|
|
434
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
435
|
-
const dec = (Param as any);
|
|
436
|
-
const fakeCtx = {
|
|
437
|
-
name: "foo",
|
|
438
|
-
addInitializer(fn: () => void) { fn.call({ constructor: class {} }); },
|
|
439
|
-
};
|
|
440
|
-
dec(undefined, fakeCtx);
|
|
441
|
-
}).not.toThrow();
|
|
442
|
-
});
|
|
443
|
-
});
|