@luna_ui/luna 0.7.3 → 0.17.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/api-DAWeanTX.js +1 -0
- package/dist/api-qXll116-.d.ts +80 -0
- package/dist/cli.mjs +27 -22
- package/dist/css/index.js +1 -0
- package/dist/event-utils.d.ts +1 -1
- package/dist/event-utils.js +1 -1
- package/dist/{index-vO066aMd.d.ts → index-VY8G32hr.d.ts} +16 -76
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/jsx-dev-runtime.js +1 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/dist/raw.d.ts +2 -0
- package/dist/raw.js +1 -0
- package/dist/resource.d.ts +41 -0
- package/dist/resource.js +1 -0
- package/dist/router-lite.d.ts +44 -0
- package/dist/router-lite.js +1 -0
- package/dist/signals-shared.d.ts +12 -0
- package/dist/signals-shared.js +1 -0
- package/dist/signals.d.ts +50 -0
- package/dist/signals.js +1 -0
- package/dist/vite-plugin.d.ts +7708 -2
- package/dist/vite-plugin.js +7 -6
- package/package.json +34 -11
- package/dist/event-utils-C_M2XBNj.js +0 -1
- package/dist/src-BbjOW18q.js +0 -1
- package/src/css/extract.ts +0 -798
- package/src/css/index.ts +0 -10
- package/src/css/inject.ts +0 -205
- package/src/css/inline.ts +0 -182
- package/src/css/minify.ts +0 -70
- package/src/css/optimizer.ts +0 -6
- package/src/css/runtime.ts +0 -344
- package/src/css-optimizer/README.md +0 -353
- package/src/css-optimizer/cooccurrence.ts +0 -100
- package/src/css-optimizer/core.ts +0 -263
- package/src/css-optimizer/extractors.ts +0 -243
- package/src/css-optimizer/hash.ts +0 -54
- package/src/css-optimizer/index.ts +0 -129
- package/src/css-optimizer/merge.ts +0 -109
- package/src/css-optimizer/moonbit-analyzer.ts +0 -210
- package/src/css-optimizer/parser.ts +0 -120
- package/src/css-optimizer/pattern.ts +0 -171
- package/src/css-optimizer/transformers.ts +0 -301
- package/src/css-optimizer/types.ts +0 -128
- package/src/event-utils.ts +0 -227
- package/src/hydration/createHydrator.ts +0 -62
- package/src/hydration/delegate.ts +0 -62
- package/src/hydration/drag.ts +0 -214
- package/src/hydration/index.ts +0 -12
- package/src/hydration/keyboard.ts +0 -64
- package/src/hydration/toggle.ts +0 -101
- package/src/index.ts +0 -908
- package/src/jsx-dev-runtime.ts +0 -2
- package/src/jsx-runtime.ts +0 -398
- package/src/vite-plugin.ts +0 -718
- package/tests/__screenshots__/apg.test.ts/APG-Components---Accessibility-Tests-Button-Pattern-disabled-button-has-aria-disabled-1.png +0 -0
- package/tests/apg.test.ts +0 -466
- package/tests/context.test.ts +0 -118
- package/tests/css-optimizer-extractors.test.ts +0 -264
- package/tests/css-optimizer-integration.test.ts +0 -566
- package/tests/css-optimizer-transformers.test.ts +0 -301
- package/tests/css-optimizer.test.ts +0 -646
- package/tests/css-runtime.bench.ts +0 -442
- package/tests/css-runtime.test.ts +0 -342
- package/tests/debounced.test.ts +0 -165
- package/tests/dom.test.ts +0 -873
- package/tests/integration.test.ts +0 -405
- package/tests/issue-11-show-null-to-truthy.test.ts +0 -176
- package/tests/issue-5-for-infinite-loop.test.ts +0 -516
- package/tests/jsx-runtime.test.tsx +0 -393
- package/tests/lifecycle.test.ts +0 -833
- package/tests/move-before.bench.ts +0 -304
- package/tests/preact-signals-comparison.test.ts +0 -1608
- package/tests/resource.test.ts +0 -160
- package/tests/router.test.ts +0 -117
- package/tests/show-initial-mount-leak.test.tsx +0 -182
- package/tests/solidjs-api.test.ts +0 -660
- package/tests/static-perf.bench.ts +0 -64
- package/tests/store.test.ts +0 -263
- package/tests/tsx-syntax.test.tsx +0 -404
- /package/dist/{event-utils-Cd5f3Njd.d.ts → event-utils-BvAf0NwN.d.ts} +0 -0
package/tests/apg.test.ts
DELETED
|
@@ -1,466 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
createElement,
|
|
4
|
-
render,
|
|
5
|
-
text,
|
|
6
|
-
} from "../src/index";
|
|
7
|
-
import axe from "axe-core";
|
|
8
|
-
|
|
9
|
-
function attr(name: string, value: unknown) {
|
|
10
|
-
return { _0: name, _1: value };
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const AttrValue = {
|
|
14
|
-
Static: (value: string) => ({ $tag: 0, _0: value }),
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
async function checkA11y(container: HTMLElement): Promise<axe.Result[]> {
|
|
18
|
-
const results = await axe.run(container);
|
|
19
|
-
return results.violations;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe("APG Components - Accessibility Tests", () => {
|
|
23
|
-
let container: HTMLElement;
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
container = document.createElement("div");
|
|
27
|
-
document.body.appendChild(container);
|
|
28
|
-
return () => {
|
|
29
|
-
container.remove();
|
|
30
|
-
};
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe("Link Pattern", () => {
|
|
34
|
-
test("native link has no a11y violations", async () => {
|
|
35
|
-
const node = createElement(
|
|
36
|
-
"a",
|
|
37
|
-
[
|
|
38
|
-
attr("href", AttrValue.Static("https://example.com")),
|
|
39
|
-
],
|
|
40
|
-
[text("Visit Example")]
|
|
41
|
-
);
|
|
42
|
-
render(container, node);
|
|
43
|
-
|
|
44
|
-
const violations = await checkA11y(container);
|
|
45
|
-
expect(violations).toHaveLength(0);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("link with aria-label for icon is accessible", async () => {
|
|
49
|
-
const node = createElement(
|
|
50
|
-
"a",
|
|
51
|
-
[
|
|
52
|
-
attr("href", AttrValue.Static("/home")),
|
|
53
|
-
attr("aria-label", AttrValue.Static("Go to home page")),
|
|
54
|
-
],
|
|
55
|
-
[text("🏠")]
|
|
56
|
-
);
|
|
57
|
-
render(container, node);
|
|
58
|
-
|
|
59
|
-
const violations = await checkA11y(container);
|
|
60
|
-
expect(violations).toHaveLength(0);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("link with target=_blank should have accessible text", async () => {
|
|
64
|
-
const node = createElement(
|
|
65
|
-
"a",
|
|
66
|
-
[
|
|
67
|
-
attr("href", AttrValue.Static("https://example.com")),
|
|
68
|
-
attr("target", AttrValue.Static("_blank")),
|
|
69
|
-
],
|
|
70
|
-
[text("External Link (opens in new tab)")]
|
|
71
|
-
);
|
|
72
|
-
render(container, node);
|
|
73
|
-
|
|
74
|
-
const violations = await checkA11y(container);
|
|
75
|
-
expect(violations).toHaveLength(0);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("link-button with role=link has proper attributes", async () => {
|
|
79
|
-
const node = createElement(
|
|
80
|
-
"span",
|
|
81
|
-
[
|
|
82
|
-
attr("role", AttrValue.Static("link")),
|
|
83
|
-
attr("tabindex", AttrValue.Static("0")),
|
|
84
|
-
],
|
|
85
|
-
[text("Click here")]
|
|
86
|
-
);
|
|
87
|
-
render(container, node);
|
|
88
|
-
|
|
89
|
-
const span = container.querySelector("span");
|
|
90
|
-
expect(span?.getAttribute("role")).toBe("link");
|
|
91
|
-
expect(span?.getAttribute("tabindex")).toBe("0");
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe("Button Pattern", () => {
|
|
96
|
-
test("native button has no a11y violations", async () => {
|
|
97
|
-
const node = createElement(
|
|
98
|
-
"button",
|
|
99
|
-
[attr("type", AttrValue.Static("button"))],
|
|
100
|
-
[text("Click me")]
|
|
101
|
-
);
|
|
102
|
-
render(container, node);
|
|
103
|
-
|
|
104
|
-
const violations = await checkA11y(container);
|
|
105
|
-
expect(violations).toHaveLength(0);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("icon button with aria-label is accessible", async () => {
|
|
109
|
-
const node = createElement(
|
|
110
|
-
"button",
|
|
111
|
-
[
|
|
112
|
-
attr("type", AttrValue.Static("button")),
|
|
113
|
-
attr("aria-label", AttrValue.Static("Close dialog")),
|
|
114
|
-
],
|
|
115
|
-
[text("×")]
|
|
116
|
-
);
|
|
117
|
-
render(container, node);
|
|
118
|
-
|
|
119
|
-
const violations = await checkA11y(container);
|
|
120
|
-
expect(violations).toHaveLength(0);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("disabled button has aria-disabled", async () => {
|
|
124
|
-
const node = createElement(
|
|
125
|
-
"button",
|
|
126
|
-
[
|
|
127
|
-
attr("type", AttrValue.Static("button")),
|
|
128
|
-
attr("disabled", AttrValue.Static("true")),
|
|
129
|
-
attr("aria-disabled", AttrValue.Static("true")),
|
|
130
|
-
],
|
|
131
|
-
[text("Disabled")]
|
|
132
|
-
);
|
|
133
|
-
render(container, node);
|
|
134
|
-
|
|
135
|
-
const button = container.querySelector("button");
|
|
136
|
-
expect(button?.getAttribute("aria-disabled")).toBe("true");
|
|
137
|
-
|
|
138
|
-
const violations = await checkA11y(container);
|
|
139
|
-
expect(violations).toHaveLength(0);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test("toggle button has aria-pressed", async () => {
|
|
143
|
-
const node = createElement(
|
|
144
|
-
"button",
|
|
145
|
-
[
|
|
146
|
-
attr("type", AttrValue.Static("button")),
|
|
147
|
-
attr("aria-pressed", AttrValue.Static("false")),
|
|
148
|
-
],
|
|
149
|
-
[text("Mute")]
|
|
150
|
-
);
|
|
151
|
-
render(container, node);
|
|
152
|
-
|
|
153
|
-
const button = container.querySelector("button");
|
|
154
|
-
expect(button?.getAttribute("aria-pressed")).toBe("false");
|
|
155
|
-
|
|
156
|
-
const violations = await checkA11y(container);
|
|
157
|
-
expect(violations).toHaveLength(0);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
test("toggle button pressed state", async () => {
|
|
161
|
-
const node = createElement(
|
|
162
|
-
"button",
|
|
163
|
-
[
|
|
164
|
-
attr("type", AttrValue.Static("button")),
|
|
165
|
-
attr("aria-pressed", AttrValue.Static("true")),
|
|
166
|
-
],
|
|
167
|
-
[text("Mute")]
|
|
168
|
-
);
|
|
169
|
-
render(container, node);
|
|
170
|
-
|
|
171
|
-
const button = container.querySelector("button");
|
|
172
|
-
expect(button?.getAttribute("aria-pressed")).toBe("true");
|
|
173
|
-
|
|
174
|
-
const violations = await checkA11y(container);
|
|
175
|
-
expect(violations).toHaveLength(0);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
test("menu button has aria-haspopup and aria-expanded", async () => {
|
|
179
|
-
const node = createElement(
|
|
180
|
-
"button",
|
|
181
|
-
[
|
|
182
|
-
attr("type", AttrValue.Static("button")),
|
|
183
|
-
attr("aria-haspopup", AttrValue.Static("menu")),
|
|
184
|
-
attr("aria-expanded", AttrValue.Static("false")),
|
|
185
|
-
],
|
|
186
|
-
[text("Options")]
|
|
187
|
-
);
|
|
188
|
-
render(container, node);
|
|
189
|
-
|
|
190
|
-
const button = container.querySelector("button");
|
|
191
|
-
expect(button?.getAttribute("aria-haspopup")).toBe("menu");
|
|
192
|
-
expect(button?.getAttribute("aria-expanded")).toBe("false");
|
|
193
|
-
|
|
194
|
-
const violations = await checkA11y(container);
|
|
195
|
-
expect(violations).toHaveLength(0);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test("menu button expanded state", async () => {
|
|
199
|
-
const node = createElement(
|
|
200
|
-
"button",
|
|
201
|
-
[
|
|
202
|
-
attr("type", AttrValue.Static("button")),
|
|
203
|
-
attr("aria-haspopup", AttrValue.Static("menu")),
|
|
204
|
-
attr("aria-expanded", AttrValue.Static("true")),
|
|
205
|
-
attr("aria-controls", AttrValue.Static("menu-id")),
|
|
206
|
-
],
|
|
207
|
-
[text("Options")]
|
|
208
|
-
);
|
|
209
|
-
render(container, node);
|
|
210
|
-
|
|
211
|
-
const button = container.querySelector("button");
|
|
212
|
-
expect(button?.getAttribute("aria-haspopup")).toBe("menu");
|
|
213
|
-
expect(button?.getAttribute("aria-expanded")).toBe("true");
|
|
214
|
-
expect(button?.getAttribute("aria-controls")).toBe("menu-id");
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe("Meter Pattern", () => {
|
|
219
|
-
test("meter with role and aria attributes is accessible", async () => {
|
|
220
|
-
const node = createElement(
|
|
221
|
-
"div",
|
|
222
|
-
[
|
|
223
|
-
attr("role", AttrValue.Static("meter")),
|
|
224
|
-
attr("aria-valuenow", AttrValue.Static("75")),
|
|
225
|
-
attr("aria-valuemin", AttrValue.Static("0")),
|
|
226
|
-
attr("aria-valuemax", AttrValue.Static("100")),
|
|
227
|
-
attr("aria-label", AttrValue.Static("Battery level")),
|
|
228
|
-
],
|
|
229
|
-
[text("75%")]
|
|
230
|
-
);
|
|
231
|
-
render(container, node);
|
|
232
|
-
|
|
233
|
-
const meter = container.querySelector("[role='meter']");
|
|
234
|
-
expect(meter?.getAttribute("aria-valuenow")).toBe("75");
|
|
235
|
-
expect(meter?.getAttribute("aria-valuemin")).toBe("0");
|
|
236
|
-
expect(meter?.getAttribute("aria-valuemax")).toBe("100");
|
|
237
|
-
|
|
238
|
-
const violations = await checkA11y(container);
|
|
239
|
-
expect(violations).toHaveLength(0);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test("meter with aria-valuetext for human-readable value", async () => {
|
|
243
|
-
const node = createElement(
|
|
244
|
-
"div",
|
|
245
|
-
[
|
|
246
|
-
attr("role", AttrValue.Static("meter")),
|
|
247
|
-
attr("aria-valuenow", AttrValue.Static("50")),
|
|
248
|
-
attr("aria-valuemin", AttrValue.Static("0")),
|
|
249
|
-
attr("aria-valuemax", AttrValue.Static("100")),
|
|
250
|
-
attr("aria-valuetext", AttrValue.Static("50% (6 hours) remaining")),
|
|
251
|
-
attr("aria-label", AttrValue.Static("Battery")),
|
|
252
|
-
],
|
|
253
|
-
[text("50%")]
|
|
254
|
-
);
|
|
255
|
-
render(container, node);
|
|
256
|
-
|
|
257
|
-
const meter = container.querySelector("[role='meter']");
|
|
258
|
-
expect(meter?.getAttribute("aria-valuetext")).toBe("50% (6 hours) remaining");
|
|
259
|
-
|
|
260
|
-
const violations = await checkA11y(container);
|
|
261
|
-
expect(violations).toHaveLength(0);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
test("native meter element is accessible", async () => {
|
|
265
|
-
const node = createElement(
|
|
266
|
-
"meter",
|
|
267
|
-
[
|
|
268
|
-
attr("value", AttrValue.Static("75")),
|
|
269
|
-
attr("min", AttrValue.Static("0")),
|
|
270
|
-
attr("max", AttrValue.Static("100")),
|
|
271
|
-
attr("aria-label", AttrValue.Static("Disk usage")),
|
|
272
|
-
],
|
|
273
|
-
[text("75%")]
|
|
274
|
-
);
|
|
275
|
-
render(container, node);
|
|
276
|
-
|
|
277
|
-
const meter = container.querySelector("meter");
|
|
278
|
-
expect(meter?.getAttribute("value")).toBe("75");
|
|
279
|
-
|
|
280
|
-
const violations = await checkA11y(container);
|
|
281
|
-
expect(violations).toHaveLength(0);
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
describe("Landmarks Pattern", () => {
|
|
286
|
-
test("header element creates banner landmark", async () => {
|
|
287
|
-
const node = createElement("header", [], [text("Site Header")]);
|
|
288
|
-
render(container, node);
|
|
289
|
-
|
|
290
|
-
const header = container.querySelector("header");
|
|
291
|
-
expect(header).not.toBeNull();
|
|
292
|
-
|
|
293
|
-
const violations = await checkA11y(container);
|
|
294
|
-
expect(violations).toHaveLength(0);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
test("nav element creates navigation landmark", async () => {
|
|
298
|
-
const node = createElement(
|
|
299
|
-
"nav",
|
|
300
|
-
[attr("aria-label", AttrValue.Static("Main navigation"))],
|
|
301
|
-
[
|
|
302
|
-
createElement("a", [attr("href", AttrValue.Static("/"))], [text("Home")]),
|
|
303
|
-
]
|
|
304
|
-
);
|
|
305
|
-
render(container, node);
|
|
306
|
-
|
|
307
|
-
const nav = container.querySelector("nav");
|
|
308
|
-
expect(nav?.getAttribute("aria-label")).toBe("Main navigation");
|
|
309
|
-
|
|
310
|
-
const violations = await checkA11y(container);
|
|
311
|
-
expect(violations).toHaveLength(0);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
test("main element creates main landmark", async () => {
|
|
315
|
-
const node = createElement("main", [], [text("Main Content")]);
|
|
316
|
-
render(container, node);
|
|
317
|
-
|
|
318
|
-
const main = container.querySelector("main");
|
|
319
|
-
expect(main).not.toBeNull();
|
|
320
|
-
|
|
321
|
-
const violations = await checkA11y(container);
|
|
322
|
-
expect(violations).toHaveLength(0);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
test("aside element creates complementary landmark", async () => {
|
|
326
|
-
const node = createElement(
|
|
327
|
-
"aside",
|
|
328
|
-
[attr("aria-label", AttrValue.Static("Related content"))],
|
|
329
|
-
[text("Sidebar")]
|
|
330
|
-
);
|
|
331
|
-
render(container, node);
|
|
332
|
-
|
|
333
|
-
const aside = container.querySelector("aside");
|
|
334
|
-
expect(aside?.getAttribute("aria-label")).toBe("Related content");
|
|
335
|
-
|
|
336
|
-
const violations = await checkA11y(container);
|
|
337
|
-
expect(violations).toHaveLength(0);
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
test("footer element creates contentinfo landmark", async () => {
|
|
341
|
-
const node = createElement("footer", [], [text("© 2024")]);
|
|
342
|
-
render(container, node);
|
|
343
|
-
|
|
344
|
-
const footer = container.querySelector("footer");
|
|
345
|
-
expect(footer).not.toBeNull();
|
|
346
|
-
|
|
347
|
-
const violations = await checkA11y(container);
|
|
348
|
-
expect(violations).toHaveLength(0);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
test("search element creates search landmark", async () => {
|
|
352
|
-
const node = createElement(
|
|
353
|
-
"search",
|
|
354
|
-
[attr("aria-label", AttrValue.Static("Site search"))],
|
|
355
|
-
[
|
|
356
|
-
createElement(
|
|
357
|
-
"input",
|
|
358
|
-
[
|
|
359
|
-
attr("type", AttrValue.Static("search")),
|
|
360
|
-
attr("aria-label", AttrValue.Static("Search query")),
|
|
361
|
-
],
|
|
362
|
-
[]
|
|
363
|
-
),
|
|
364
|
-
]
|
|
365
|
-
);
|
|
366
|
-
render(container, node);
|
|
367
|
-
|
|
368
|
-
const search = container.querySelector("search");
|
|
369
|
-
expect(search).not.toBeNull();
|
|
370
|
-
|
|
371
|
-
const violations = await checkA11y(container);
|
|
372
|
-
expect(violations).toHaveLength(0);
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
test("form with role=search creates search landmark", async () => {
|
|
376
|
-
const node = createElement(
|
|
377
|
-
"form",
|
|
378
|
-
[
|
|
379
|
-
attr("role", AttrValue.Static("search")),
|
|
380
|
-
attr("aria-label", AttrValue.Static("Site search")),
|
|
381
|
-
],
|
|
382
|
-
[
|
|
383
|
-
createElement(
|
|
384
|
-
"input",
|
|
385
|
-
[
|
|
386
|
-
attr("type", AttrValue.Static("search")),
|
|
387
|
-
attr("aria-label", AttrValue.Static("Search query")),
|
|
388
|
-
],
|
|
389
|
-
[]
|
|
390
|
-
),
|
|
391
|
-
]
|
|
392
|
-
);
|
|
393
|
-
render(container, node);
|
|
394
|
-
|
|
395
|
-
const form = container.querySelector("form[role='search']");
|
|
396
|
-
expect(form).not.toBeNull();
|
|
397
|
-
|
|
398
|
-
const violations = await checkA11y(container);
|
|
399
|
-
expect(violations).toHaveLength(0);
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
test("section with aria-label creates region landmark", async () => {
|
|
403
|
-
const node = createElement(
|
|
404
|
-
"section",
|
|
405
|
-
[attr("aria-label", AttrValue.Static("Quick Stats"))],
|
|
406
|
-
[text("Statistics content")]
|
|
407
|
-
);
|
|
408
|
-
render(container, node);
|
|
409
|
-
|
|
410
|
-
const section = container.querySelector("section");
|
|
411
|
-
expect(section?.getAttribute("aria-label")).toBe("Quick Stats");
|
|
412
|
-
|
|
413
|
-
const violations = await checkA11y(container);
|
|
414
|
-
expect(violations).toHaveLength(0);
|
|
415
|
-
});
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
describe("Keyboard Interaction", () => {
|
|
419
|
-
test("button responds to Enter key", () => {
|
|
420
|
-
let clicked = false;
|
|
421
|
-
const node = createElement(
|
|
422
|
-
"button",
|
|
423
|
-
[
|
|
424
|
-
attr("type", AttrValue.Static("button")),
|
|
425
|
-
attr("click", { $tag: 2, _0: () => { clicked = true; } }),
|
|
426
|
-
],
|
|
427
|
-
[text("Click")]
|
|
428
|
-
);
|
|
429
|
-
render(container, node);
|
|
430
|
-
|
|
431
|
-
const button = container.querySelector("button");
|
|
432
|
-
button?.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" }));
|
|
433
|
-
button?.click();
|
|
434
|
-
expect(clicked).toBe(true);
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
test("button is focusable", () => {
|
|
438
|
-
const node = createElement(
|
|
439
|
-
"button",
|
|
440
|
-
[attr("type", AttrValue.Static("button"))],
|
|
441
|
-
[text("Focusable")]
|
|
442
|
-
);
|
|
443
|
-
render(container, node);
|
|
444
|
-
|
|
445
|
-
const button = container.querySelector("button");
|
|
446
|
-
button?.focus();
|
|
447
|
-
expect(document.activeElement).toBe(button);
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
test("link-button with role=link is focusable via tabindex", () => {
|
|
451
|
-
const node = createElement(
|
|
452
|
-
"span",
|
|
453
|
-
[
|
|
454
|
-
attr("role", AttrValue.Static("link")),
|
|
455
|
-
attr("tabindex", AttrValue.Static("0")),
|
|
456
|
-
],
|
|
457
|
-
[text("Focusable link")]
|
|
458
|
-
);
|
|
459
|
-
render(container, node);
|
|
460
|
-
|
|
461
|
-
const span = container.querySelector("span");
|
|
462
|
-
span?.focus();
|
|
463
|
-
expect(document.activeElement).toBe(span);
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
});
|
package/tests/context.test.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
createContext,
|
|
4
|
-
provide,
|
|
5
|
-
useContext,
|
|
6
|
-
createRoot,
|
|
7
|
-
createRenderEffect,
|
|
8
|
-
} from "../src/index";
|
|
9
|
-
|
|
10
|
-
describe("Context API", () => {
|
|
11
|
-
describe("createContext", () => {
|
|
12
|
-
test("creates a context with default value", () => {
|
|
13
|
-
const ctx = createContext("default");
|
|
14
|
-
expect(ctx).toBeDefined();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("useContext returns default value when not provided", () => {
|
|
18
|
-
const ctx = createContext("default-value");
|
|
19
|
-
const value = useContext(ctx);
|
|
20
|
-
expect(value).toBe("default-value");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test("useContext returns default for number", () => {
|
|
24
|
-
const ctx = createContext(42);
|
|
25
|
-
expect(useContext(ctx)).toBe(42);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test("useContext returns default for object", () => {
|
|
29
|
-
const defaultObj = { name: "test", count: 0 };
|
|
30
|
-
const ctx = createContext(defaultObj);
|
|
31
|
-
expect(useContext(ctx)).toBe(defaultObj);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe("provide", () => {
|
|
36
|
-
test("provides value within function scope", () => {
|
|
37
|
-
const ctx = createContext("default");
|
|
38
|
-
let capturedValue: string | undefined;
|
|
39
|
-
|
|
40
|
-
provide(ctx, "provided-value", () => {
|
|
41
|
-
capturedValue = useContext(ctx);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
expect(capturedValue).toBe("provided-value");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("value reverts after provide scope ends", () => {
|
|
48
|
-
const ctx = createContext("default");
|
|
49
|
-
|
|
50
|
-
provide(ctx, "inner", () => {
|
|
51
|
-
expect(useContext(ctx)).toBe("inner");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// After provide scope, should return to default
|
|
55
|
-
expect(useContext(ctx)).toBe("default");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("nested provide overrides outer value", () => {
|
|
59
|
-
const ctx = createContext("default");
|
|
60
|
-
const values: string[] = [];
|
|
61
|
-
|
|
62
|
-
provide(ctx, "outer", () => {
|
|
63
|
-
values.push(useContext(ctx));
|
|
64
|
-
|
|
65
|
-
provide(ctx, "inner", () => {
|
|
66
|
-
values.push(useContext(ctx));
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
values.push(useContext(ctx));
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
expect(values).toEqual(["outer", "inner", "outer"]);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("returns value from provided function", () => {
|
|
76
|
-
const ctx = createContext(0);
|
|
77
|
-
const result = provide(ctx, 10, () => {
|
|
78
|
-
return useContext(ctx) * 2;
|
|
79
|
-
});
|
|
80
|
-
expect(result).toBe(20);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe("multiple contexts", () => {
|
|
85
|
-
test("different contexts are independent", () => {
|
|
86
|
-
const themeCtx = createContext("light");
|
|
87
|
-
const langCtx = createContext("en");
|
|
88
|
-
|
|
89
|
-
provide(themeCtx, "dark", () => {
|
|
90
|
-
expect(useContext(themeCtx)).toBe("dark");
|
|
91
|
-
expect(useContext(langCtx)).toBe("en");
|
|
92
|
-
|
|
93
|
-
provide(langCtx, "ja", () => {
|
|
94
|
-
expect(useContext(themeCtx)).toBe("dark");
|
|
95
|
-
expect(useContext(langCtx)).toBe("ja");
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe("context with reactive effects", () => {
|
|
102
|
-
test("context value accessible in effect", () => {
|
|
103
|
-
const ctx = createContext("initial");
|
|
104
|
-
let effectValue: string | undefined;
|
|
105
|
-
|
|
106
|
-
createRoot((dispose) => {
|
|
107
|
-
provide(ctx, "effect-value", () => {
|
|
108
|
-
createRenderEffect(() => {
|
|
109
|
-
effectValue = useContext(ctx);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
dispose();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
expect(effectValue).toBe("effect-value");
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
});
|