@marianmeres/stuic 3.170.0 → 3.172.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/AGENTS.md +10 -8
- package/API.md +90 -0
- package/README.md +1 -1
- package/dist/components/AssetsPreview/AssetsPreview.fixture.svelte +1 -1
- package/dist/components/Calendar/Calendar.svelte +799 -0
- package/dist/components/Calendar/Calendar.svelte.d.ts +135 -0
- package/dist/components/Calendar/README.md +294 -0
- package/dist/components/Calendar/calendar-i18n-sk.d.ts +20 -0
- package/dist/components/Calendar/calendar-i18n-sk.js +46 -0
- package/dist/components/Calendar/calendar-i18n.d.ts +61 -0
- package/dist/components/Calendar/calendar-i18n.js +73 -0
- package/dist/components/Calendar/index.css +307 -0
- package/dist/components/Calendar/index.d.ts +5 -0
- package/dist/components/Calendar/index.js +5 -0
- package/dist/components/Calendar/iso-date.d.ts +107 -0
- package/dist/components/Calendar/iso-date.js +247 -0
- package/dist/components/CommandMenu/CommandMenu.fixture.svelte +1 -1
- package/dist/components/ContextMenu/ContextMenu.svelte +1 -1
- package/dist/components/ContextMenu/ContextMenu.svelte.d.ts +1 -1
- package/dist/components/ContextMenu/README.md +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.svelte +57 -3
- package/dist/components/DropdownMenu/DropdownMenu.svelte.d.ts +4 -2
- package/dist/components/DropdownMenu/README.md +1 -0
- package/dist/components/Input/FieldDate.svelte +349 -0
- package/dist/components/Input/FieldDate.svelte.d.ts +79 -0
- package/dist/components/Input/FieldDateRange.svelte +373 -0
- package/dist/components/Input/FieldDateRange.svelte.d.ts +90 -0
- package/dist/components/Input/README.md +149 -16
- package/dist/components/Input/_internal/FieldDateShell.svelte +327 -0
- package/dist/components/Input/_internal/FieldDateShell.svelte.d.ts +66 -0
- package/dist/components/Input/index.css +119 -0
- package/dist/components/Input/index.d.ts +2 -0
- package/dist/components/Input/index.js +2 -0
- package/dist/components/ModalDialog/ModalDialog.fixture.svelte +1 -1
- package/dist/components/SlidingPanels/SlidingPanels.fixture.svelte +1 -1
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/docs/_archive/README.md +10 -0
- package/docs/{component-testing → _archive/component-testing}/00-overview-and-roadmap.md +8 -6
- package/docs/{component-testing → _archive/component-testing}/01-framework-setup.md +4 -2
- package/docs/{component-testing → _archive/component-testing}/03-component-coverage-roadmap.md +3 -1
- package/docs/{component-testing → _archive/component-testing}/04-hard-cases-and-e2e.md +5 -3
- package/docs/{component-testing → _archive/component-testing}/05-ci.md +2 -0
- package/docs/{component-testing → _archive/component-testing}/PROGRESS.md +3 -1
- package/docs/_archive/component-testing/README.md +28 -0
- package/docs/{upgrading.md → _archive/upgrading.md} +2 -0
- package/docs/architecture.md +19 -11
- package/docs/domains/actions.md +4 -3
- package/docs/domains/components.md +22 -19
- package/docs/domains/utils.md +2 -1
- package/docs/maybe-todo.md +6 -6
- package/docs/tasks.md +19 -9
- package/docs/{component-testing/02-test-conventions.md → testing-components.md} +30 -31
- package/docs/testing.md +3 -3
- package/package.json +2 -1
- package/docs/component-testing/README.md +0 -38
|
@@ -7,6 +7,8 @@ Planning artifact; no code was changed.
|
|
|
7
7
|
|
|
8
8
|
# Framework Setup
|
|
9
9
|
|
|
10
|
+
> **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
|
|
11
|
+
|
|
10
12
|
> This is the infrastructure dimension: the one-time work that turns "no DOM, no `$effect`"
|
|
11
13
|
> into a working browser test harness. The single most important takeaway: **a Vitest
|
|
12
14
|
> `projects` split routed by filename** (`*.test.ts` → fast node, `*.svelte.test.ts` → real
|
|
@@ -18,7 +20,7 @@ Planning artifact; no code was changed.
|
|
|
18
20
|
|
|
19
21
|
- **Package manager:** pnpm. **Svelte:** 5.56.2. **SvelteKit:** 2.63.0 (adapter-auto). **Vitest:** 3.2.6.
|
|
20
22
|
- **Test config:** none dedicated. Vitest is configured only implicitly through
|
|
21
|
-
[`vite.config.ts`](
|
|
23
|
+
[`vite.config.ts`](../../../vite.config.ts) (`plugins: [tailwindcss(), sveltekit()]`), and the
|
|
22
24
|
script is `"test": "vitest --dir src/"`.
|
|
23
25
|
- **What runs today:** 9 suites / ~59 tests, all **node environment, pure logic** — validation
|
|
24
26
|
helpers, stack classes (`NotificationsStack`, `AlertConfirmPromptStack`), `tr`, `replace-map`,
|
|
@@ -70,7 +72,7 @@ do **not** need `@testing-library/svelte` or `@testing-library/jest-dom` — `vi
|
|
|
70
72
|
|
|
71
73
|
## Step 3 — The `projects` config
|
|
72
74
|
|
|
73
|
-
Add a `test` block to [`vite.config.ts`](
|
|
75
|
+
Add a `test` block to [`vite.config.ts`](../../../vite.config.ts) (keep the file; just extend it).
|
|
74
76
|
Verified against the live Vitest 4 docs (`provider: playwright()` is an imported **function**, not
|
|
75
77
|
the old `'playwright'` string; `instances` is required):
|
|
76
78
|
|
package/docs/{component-testing → _archive/component-testing}/03-component-coverage-roadmap.md
RENAMED
|
@@ -7,6 +7,8 @@ Planning artifact; no code was changed.
|
|
|
7
7
|
|
|
8
8
|
# Component Coverage Roadmap
|
|
9
9
|
|
|
10
|
+
> **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
|
|
11
|
+
|
|
10
12
|
> The library has **74 components** (≈105 `.svelte` files incl. sub-components). Roughly **26 are
|
|
11
13
|
> "easy"** (deterministic prop→DOM, no portals/traps), **23 "medium"** (actions, focus jumps, layout
|
|
12
14
|
> reads), **30 "hard/E2E-only"** (portals, focus traps, anchor positioning, drag, Milkdown). The plan:
|
|
@@ -82,7 +84,7 @@ E2E. [04](./04-hard-cases-and-e2e.md) draws that line.
|
|
|
82
84
|
- **One component per commit**, message like `test(Button): browser-mode coverage`.
|
|
83
85
|
- Each commit: write `ComponentName.svelte.test.ts`, run `pnpm test` (both projects green), tick the
|
|
84
86
|
row in [`PROGRESS.md`](./PROGRESS.md), commit.
|
|
85
|
-
- Don't chase coverage %. Stop at the behavior contracts in [02](
|
|
87
|
+
- Don't chase coverage %. Stop at the behavior contracts in [02](../../testing-components.md)'s checklist.
|
|
86
88
|
|
|
87
89
|
## Open questions / decisions needed
|
|
88
90
|
|
|
@@ -6,6 +6,8 @@ Claims verified against src/lib at commit cc9958b. Planning artifact; no code wa
|
|
|
6
6
|
|
|
7
7
|
# Hard Cases & E2E Strategy
|
|
8
8
|
|
|
9
|
+
> **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
|
|
10
|
+
|
|
9
11
|
> ~30 of the 74 components are "hard". But "hard" splits two ways: **most are hard for _jsdom_ yet
|
|
10
12
|
> perfectly testable in Vitest browser mode** (focus traps, anchor positioning, ResizeObserver — all
|
|
11
13
|
> work in a real Chromium); only **drag-heavy and Milkdown-class** components genuinely need a
|
|
@@ -32,7 +34,7 @@ candidates, both verified to exist:
|
|
|
32
34
|
|
|
33
35
|
### Candidate A (recommended) — anchor-position viewport clamping
|
|
34
36
|
|
|
35
|
-
- **Code:** [`src/lib/utils/anchor-position.ts`](
|
|
37
|
+
- **Code:** [`src/lib/utils/anchor-position.ts`](../../../src/lib/utils/anchor-position.ts), consumed by
|
|
36
38
|
`DropdownMenu/DropdownMenu.svelte` (and others).
|
|
37
39
|
- **Why:** this is precisely what regressed in `9d8c974` _"clamp anchor-positioned annotations to
|
|
38
40
|
viewport on all paths"_ and `8c52afe`. A test here has immediate, proven value and prevents
|
|
@@ -45,7 +47,7 @@ candidates, both verified to exist:
|
|
|
45
47
|
|
|
46
48
|
### Candidate B (alternative) — focus trap
|
|
47
49
|
|
|
48
|
-
- **Code:** [`src/lib/actions/focus-trap.ts`](
|
|
50
|
+
- **Code:** [`src/lib/actions/focus-trap.ts`](../../../src/lib/actions/focus-trap.ts), used by
|
|
49
51
|
`ModalDialog`, `Backdrop`, `Drawer`.
|
|
50
52
|
- **Why:** Tab/Shift-Tab cycling and `returnFocus`-on-teardown are core a11y contracts that node
|
|
51
53
|
cannot exercise at all.
|
|
@@ -59,7 +61,7 @@ candidates, both verified to exist:
|
|
|
59
61
|
## Portals & focus traps (browser-mode, later sprint)
|
|
60
62
|
|
|
61
63
|
`Modal`, `ModalDialog`, `Backdrop`, `Drawer`, `AlertConfirmPrompt` — all use
|
|
62
|
-
[`focus-trap.ts`](
|
|
64
|
+
[`focus-trap.ts`](../../../src/lib/actions/focus-trap.ts), scroll-lock, and an Escape-key stack. These
|
|
63
65
|
are testable in browser mode (open → focus trapped → Escape closes → backdrop click closes →
|
|
64
66
|
`returnFocus`). The stack/queue _logic_ is already unit-tested (`AlertConfirmPromptStack`,
|
|
65
67
|
`NotificationsStack`); browser tests add the DOM-interaction layer. Higher effort, real value —
|
|
@@ -6,6 +6,8 @@ Claims verified against the repo at commit cc9958b. Planning artifact; no code w
|
|
|
6
6
|
|
|
7
7
|
# CI — GitHub Actions
|
|
8
8
|
|
|
9
|
+
> **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
|
|
10
|
+
|
|
9
11
|
> The repo is on GitHub (`github.com/marianmeres/stuic`) and publishes to npm, so CI pays off: it
|
|
10
12
|
> runs the tests automatically on every push/PR in a clean machine, catching regressions **before**
|
|
11
13
|
> a broken release reaches npm. The plan: **one ~30-line workflow** that installs Chromium and runs
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Implementation Progress — Component Testing
|
|
2
2
|
|
|
3
|
+
> **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
|
|
4
|
+
|
|
3
5
|
Living tracker for acting on [`00-overview-and-roadmap.md`](./00-overview-and-roadmap.md).
|
|
4
6
|
A fresh conversation should read this file first, then the relevant `NN-*.md` section.
|
|
5
7
|
|
|
@@ -17,7 +19,7 @@ Branch: `feat/component-testing`
|
|
|
17
19
|
| --- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ------ | --------- |
|
|
18
20
|
| 1 | Upgrade vitest 3→4; confirm 9 existing node suites still green | [01](./01-framework-setup.md) Step 1 | ✅ | `71e47e2` |
|
|
19
21
|
| 2 | Browser harness: add deps, `projects` config split, `playwright install chromium`, fix test scripts, Separator smoke test | [01](./01-framework-setup.md) Steps 2–5 | ✅ | `980b323` |
|
|
20
|
-
| 3 | Reconcile [`docs/testing.md`](
|
|
22
|
+
| 3 | Reconcile [`docs/testing.md`](../../testing.md) — add the browser-behavior layer | [02](../../testing-components.md) | ✅ | `977c431` |
|
|
21
23
|
| 4 | **Button** — flagship; establish assertion patterns | [03](./03-component-coverage-roadmap.md) #1 | ✅ | `9485e97` |
|
|
22
24
|
| 5 | **Pill** — intent/active/dismissible event | [03](./03-component-coverage-roadmap.md) #2 | ✅ | `2992faf` |
|
|
23
25
|
| 6 | **Switch** — checked binding, toggle, disabled | [03](./03-component-coverage-roadmap.md) #3 | ✅ | `6aa1771` |
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Component Testing — archived plan
|
|
2
|
+
|
|
3
|
+
> **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](../README.md) for why.
|
|
4
|
+
|
|
5
|
+
> **Archived.** This was the planning set that introduced real-browser component tests to STUIC
|
|
6
|
+
> (Vitest 4 Browser Mode + `vitest-browser-svelte` + Playwright/Chromium), produced 2026-06-08
|
|
7
|
+
> against the codebase at commit `cc9958b`. The plan was executed: the harness, the project split,
|
|
8
|
+
> CI, and the component suite all shipped, and `PROGRESS.md` is closed out apart from two
|
|
9
|
+
> deliberately deferred items.
|
|
10
|
+
>
|
|
11
|
+
> **For how to write a component test today, read
|
|
12
|
+
> [`docs/testing-components.md`](../../testing-components.md)** — the conventions doc was promoted
|
|
13
|
+
> out of this set and is the live reference. Everything below is provenance: why the stack was
|
|
14
|
+
> chosen, what the tiers were, and how the sprint ran.
|
|
15
|
+
|
|
16
|
+
## Documents
|
|
17
|
+
|
|
18
|
+
| # | Doc | Scope | Headline |
|
|
19
|
+
| --- | ---------------------------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------------- |
|
|
20
|
+
| 00 | [overview-and-roadmap](./00-overview-and-roadmap.md) | synthesis + roadmap | The stack is the right default; the vitest 3→4 upgrade was the gating prerequisite. |
|
|
21
|
+
| 01 | [framework-setup](./01-framework-setup.md) | infra | Upgrade vitest 4, add a `projects` split (node `server` + browser `client`), route by filename. |
|
|
22
|
+
| 03 | [component-coverage-roadmap](./03-component-coverage-roadmap.md) | inventory + tiers | 74 components split easy / medium / hard; easy tier first, one commit each. |
|
|
23
|
+
| 04 | [hard-cases-and-e2e](./04-hard-cases-and-e2e.md) | hard cases | Most "hard" components are fine in browser mode; only drag/Milkdown need standalone E2E. |
|
|
24
|
+
| 05 | [ci](./05-ci.md) | CI | One ~30-line GitHub Actions workflow that installs Chromium and runs `pnpm test`. |
|
|
25
|
+
| | [PROGRESS.md](./PROGRESS.md) | tracker | Execution log — what was built, in which commit. |
|
|
26
|
+
|
|
27
|
+
Doc 02 (test conventions) is **not** here; it lives at
|
|
28
|
+
[`docs/testing-components.md`](../../testing-components.md).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Upgrading STUIC
|
|
2
2
|
|
|
3
|
+
> **ARCHIVED — historical, not current guidance.** See [`docs/_archive/README.md`](./README.md) for why.
|
|
4
|
+
|
|
3
5
|
Notes for coding agents (and humans) maintaining a project that consumes `@marianmeres/stuic`. This doc describes the deltas introduced on top of **v3.66.1** — grouped by what a consumer actually cares about, not by commit.
|
|
4
6
|
|
|
5
7
|
## TL;DR
|
package/docs/architecture.md
CHANGED
|
@@ -36,19 +36,24 @@ Layer 4: Internal Vars (--_bg, --_text, --_border)
|
|
|
36
36
|
|
|
37
37
|
```
|
|
38
38
|
src/lib/
|
|
39
|
-
├── components/ #
|
|
39
|
+
├── components/ # 75 component directories
|
|
40
40
|
│ └── {Name}/
|
|
41
41
|
│ ├── {Name}.svelte # Main component
|
|
42
42
|
│ ├── index.ts # Exports
|
|
43
43
|
│ ├── index.css # CSS tokens (if styled)
|
|
44
44
|
│ └── README.md # Documentation
|
|
45
45
|
│
|
|
46
|
-
├── actions/ #
|
|
46
|
+
├── actions/ # 16 Svelte actions
|
|
47
47
|
│ ├── *.svelte.ts # Reactive actions
|
|
48
48
|
│ ├── *.ts # Traditional actions
|
|
49
49
|
│ └── index.ts # Barrel export
|
|
50
50
|
│
|
|
51
|
-
├──
|
|
51
|
+
├── attachments/ # {@attach} DOM helpers (preferred over new actions)
|
|
52
|
+
│ ├── auto-height.ts
|
|
53
|
+
│ ├── long-press.ts
|
|
54
|
+
│ └── index.ts # Barrel export
|
|
55
|
+
│
|
|
56
|
+
├── utils/ # 55 utility modules (48 re-exported from the barrel)
|
|
52
57
|
│ ├── *.svelte.ts # Reactive utilities
|
|
53
58
|
│ ├── *.ts # Pure functions
|
|
54
59
|
│ └── index.ts # Barrel export
|
|
@@ -58,6 +63,8 @@ src/lib/
|
|
|
58
63
|
├── css/ # CSS-only presets (classes + tokens, no JS)
|
|
59
64
|
│ └── frame.css # Ratio-locked frame (letterbox)
|
|
60
65
|
│
|
|
66
|
+
├── types.ts # Shared public types (DataAttributes, TranslateFn, …)
|
|
67
|
+
├── mcp.ts # MCP tool definitions (@marianmeres/mcp-server discovery)
|
|
61
68
|
├── index.css # CENTRALIZED CSS imports
|
|
62
69
|
└── index.ts # Main barrel export
|
|
63
70
|
```
|
|
@@ -87,7 +94,7 @@ src/lib/
|
|
|
87
94
|
**DO NOT** use `import './index.css'` inside component `.svelte` files.
|
|
88
95
|
|
|
89
96
|
**Exception — subpath-export components.** A component whose peer dependencies are
|
|
90
|
-
declared _optional_ (`MarkdownEditor`, `CommentInput`) is kept off the root barrel and
|
|
97
|
+
declared _optional_ (`MarkdownEditor`, `CommentInput`, `TrendChart`) is kept off the root barrel and
|
|
91
98
|
shipped behind its own subpath export, so those peers never enter the entry graph of
|
|
92
99
|
consumers who don't use it. Such a component imports its own `index.css` locally and is
|
|
93
100
|
**not** `@import`-ed into `src/lib/index.css` — centralizing it would ship the styles to
|
|
@@ -145,7 +152,7 @@ Props → Component → Data Attributes → CSS Selectors
|
|
|
145
152
|
| ------------------------------------------ | --------------------------------------------------------------------------- |
|
|
146
153
|
| `src/lib/index.css` | CSS entry point (import this) |
|
|
147
154
|
| `src/lib/index.ts` | JS entry point (barrel export) |
|
|
148
|
-
| `@marianmeres/design-tokens/css/stone.css` | Default theme (
|
|
155
|
+
| `@marianmeres/design-tokens/css/stone.css` | Default theme (54 themes available) |
|
|
149
156
|
| `src/lib/utils/tw-merge.ts` | Tailwind class merging |
|
|
150
157
|
| `src/lib/utils/design-tokens.ts` | Theme types (`ThemeSchema`, `ColorPair`, etc.) and CSS generation functions |
|
|
151
158
|
|
|
@@ -173,14 +180,15 @@ Theme CSS files are provided by the `@marianmeres/design-tokens` package (a regu
|
|
|
173
180
|
"./phone-validation": Phone validation helpers
|
|
174
181
|
"./markdown-editor": MarkdownEditor — optional peer deps, local CSS
|
|
175
182
|
"./comment-input": CommentInput — optional peer deps, local CSS
|
|
183
|
+
"./trend-chart": TrendChart — optional peer deps, local CSS
|
|
176
184
|
```
|
|
177
185
|
|
|
178
|
-
The last
|
|
179
|
-
`@codemirror
|
|
180
|
-
peers gets a hard bundler error (rolldown reports every
|
|
181
|
-
unresolved stub as `MISSING_EXPORT`) if the root barrel can reach
|
|
182
|
-
the editor backends sit behind `await import()`, because the
|
|
183
|
-
literals that a bundler must still resolve at build time.
|
|
186
|
+
The last three are deliberately **off** the main entry: they reach `@milkdown/*`,
|
|
187
|
+
`@codemirror/*` and `@marianmeres/trend-chart`, which are optional peers. A consumer
|
|
188
|
+
that has not installed those peers gets a hard bundler error (rolldown reports every
|
|
189
|
+
named import from the unresolved stub as `MISSING_EXPORT`) if the root barrel can reach
|
|
190
|
+
them — even though the editor backends sit behind `await import()`, because the
|
|
191
|
+
specifiers are static literals that a bundler must still resolve at build time.
|
|
184
192
|
|
|
185
193
|
---
|
|
186
194
|
|
package/docs/domains/actions.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
16 Svelte actions (directives) for reusable DOM behavior.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
| `fileDropzone` | Drag-and-drop file handling | `file-dropzone.svelte.ts` |
|
|
19
19
|
| `highlightDragover` | Visual feedback on drag-over | `highlight-dragover.svelte.ts` |
|
|
20
20
|
| `resizableWidth` | Draggable width resizing | `resizable-width.svelte.ts` |
|
|
21
|
+
| `draggable` | Pointer-delta drag handle (consumer owns the movement) | `draggable.svelte.ts` |
|
|
21
22
|
| `trim` | Auto-trim whitespace from input | `trim.svelte.ts` |
|
|
22
23
|
| `typeahead` | Advanced autocomplete behavior | `typeahead.svelte.ts` |
|
|
23
24
|
| `onSubmitValidityCheck` | Form submit validation | `on-submit-validity-check.svelte.ts` |
|
|
@@ -120,8 +121,8 @@ Per the HTML spec, `<input type="hidden">` is _barred from constraint
|
|
|
120
121
|
validation_ — `validity.valueMissing` stays `false` regardless of the
|
|
121
122
|
`required` attribute, and native browser submit blocking is skipped. Several
|
|
122
123
|
STUIC field components (`FieldPhoneNumber`, `FieldCountry`, `FieldObject`,
|
|
123
|
-
`FieldAssets`, `FieldInputLocalized`, `FieldKeyValues`, `FieldLikeButton
|
|
124
|
-
use a hidden input to participate in `FormData`, so they each enforce
|
|
124
|
+
`FieldAssets`, `FieldInputLocalized`, `FieldKeyValues`, `FieldLikeButton`,
|
|
125
|
+
`FieldDate`, `FieldDateRange`) use a hidden input to participate in `FormData`, so they each enforce
|
|
125
126
|
`required` themselves inside their `customValidator`:
|
|
126
127
|
|
|
127
128
|
```ts
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
75 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
|
|
6
6
|
|
|
7
7
|
## Component Categories
|
|
8
8
|
|
|
@@ -58,22 +58,24 @@
|
|
|
58
58
|
|
|
59
59
|
### Form
|
|
60
60
|
|
|
61
|
-
| Component | Purpose
|
|
62
|
-
| --------------------------------------------- |
|
|
63
|
-
| Input (FieldInput, FieldSelect, etc.) | Form fields
|
|
64
|
-
| FieldMoney | Money input storing integer minor units (e.g. cents)
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
61
|
+
| Component | Purpose |
|
|
62
|
+
| --------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
63
|
+
| Input (FieldInput, FieldSelect, etc.) | Form fields |
|
|
64
|
+
| FieldMoney | Money input storing integer minor units (e.g. cents) |
|
|
65
|
+
| Calendar | Month grid for single-date / range picking: keyboard grid, min/max, dropdown caption, i18n |
|
|
66
|
+
| FieldDate, FieldDateRange | Date / date-range fields around Calendar: trigger + dialog or embedded; ISO hidden inputs |
|
|
67
|
+
| FieldPhoneNumber | International phone input with country picker |
|
|
68
|
+
| FieldObject | Dual-mode JSON object editor (pretty-print/raw) |
|
|
69
|
+
| CronInput | Cron expression editor with presets and validation |
|
|
70
|
+
| Fieldset | Field grouping with legend |
|
|
71
|
+
| FieldKeyValues | Key-value pair editor |
|
|
72
|
+
| FieldsBuilder | Field-definition list editor ("what properties does a thing have?") |
|
|
73
|
+
| FieldAssets | File/asset management |
|
|
74
|
+
| LoginForm, LoginFormModal | Standalone login form with optional modal variant |
|
|
75
|
+
| RegisterForm | Standalone registration form with declarative extra fields |
|
|
76
|
+
| LoginOrRegisterForm, LoginOrRegisterFormModal | Composite login/register/verify form (3 modes, shared social-logins) |
|
|
77
|
+
| EmailVerifyForm | Post-registration email-verify form (OtpInput + resend cooldown) |
|
|
78
|
+
| OtpInput | Generic N-slot one-time-code input (numeric/alphanumeric, paste-distribute) |
|
|
77
79
|
|
|
78
80
|
### Display
|
|
79
81
|
|
|
@@ -150,7 +152,7 @@ Use `validate={false}` to bypass stuic's validation entirely.
|
|
|
150
152
|
|
|
151
153
|
> **Why default-on?** Hidden-input field components (`FieldPhoneNumber`,
|
|
152
154
|
> `FieldCountry`, `FieldObject`, `FieldAssets`, `FieldInputLocalized`,
|
|
153
|
-
> `FieldKeyValues`, `FieldLikeButton`) _must_ be default-on because hidden
|
|
155
|
+
> `FieldKeyValues`, `FieldLikeButton`, `FieldDate`, `FieldDateRange`) _must_ be default-on because hidden
|
|
154
156
|
> inputs are excluded from native browser constraint validation — without the
|
|
155
157
|
> stuic action enforcing `required` in a `customValidator`, the attribute is a
|
|
156
158
|
> silent no-op. Plain-input field components were harmonized to the same
|
|
@@ -161,7 +163,8 @@ Use `validate={false}` to bypass stuic's validation entirely.
|
|
|
161
163
|
Available on `FieldInput`, `FieldMoney`, `FieldTextarea`, `FieldCheckbox`,
|
|
162
164
|
`FieldSelect`, `FieldFile`, `FieldObject`, `FieldAssets`, `FieldInputLocalized`,
|
|
163
165
|
`FieldKeyValues`, `FieldPhoneNumber`, `FieldCountry`, `FieldLikeButton`,
|
|
164
|
-
`FieldRadios`, `FieldSwitch`, `FieldOptions`,
|
|
166
|
+
`FieldRadios`, `FieldSwitch`, `FieldOptions`, `FieldDate`, `FieldDateRange`, and
|
|
167
|
+
`Switch`:
|
|
165
168
|
|
|
166
169
|
| Method | Returns | Purpose |
|
|
167
170
|
| ----------------------- | ------------------------------- | ------------------------------------------------------------- |
|
package/docs/domains/utils.md
CHANGED
package/docs/maybe-todo.md
CHANGED
|
@@ -24,12 +24,12 @@ Checked first, to avoid false positives:
|
|
|
24
24
|
|
|
25
25
|
## Tier 1 — staples nearly every comparable library ships
|
|
26
26
|
|
|
27
|
-
1.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
1. ~~**Date picker / Calendar**~~ — ✅ shipped (see `src/lib/components/Calendar/` and
|
|
28
|
+
`FieldDate` / `FieldDateRange` in `Input/`): `Calendar` is the month grid (single /
|
|
29
|
+
range, APG keyboard grid, min/max/blackouts, dropdown caption, multiple months, week
|
|
30
|
+
numbers, `renderDay`, Intl names + `t` texts with Slovak bundled), built on
|
|
31
|
+
`@marianmeres/calendar-utils`; the fields wrap it as trigger + dialog or embedded, with
|
|
32
|
+
ISO `YYYY-MM-DD` hidden inputs and the usual validate API.
|
|
33
33
|
2. ~~**Breadcrumbs**~~ — ✅ shipped (see `src/lib/components/Breadcrumbs/`):
|
|
34
34
|
APG nav/ol trail, collapsible long trails, schema.org `BreadcrumbList` JSON-LD
|
|
35
35
|
helpers (`breadcrumbsJsonLd` / `breadcrumbsJsonLdScript` + inline `jsonLd` prop).
|
package/docs/tasks.md
CHANGED
|
@@ -149,7 +149,9 @@ For consumers creating their own theme outside the library.
|
|
|
149
149
|
```ts
|
|
150
150
|
import type { ThemeSchema } from "@marianmeres/stuic";
|
|
151
151
|
import { generateThemeCss } from "@marianmeres/stuic";
|
|
152
|
-
|
|
152
|
+
// Built-in theme schemas live in design-tokens, not in stuic. stuic depends on it,
|
|
153
|
+
// but add it to your own package.json if you import from it directly.
|
|
154
|
+
import { stone } from "@marianmeres/design-tokens/themes";
|
|
153
155
|
import { writeFileSync } from "node:fs";
|
|
154
156
|
|
|
155
157
|
const myTheme: ThemeSchema = {
|
|
@@ -166,7 +168,9 @@ const myTheme: ThemeSchema = {
|
|
|
166
168
|
dark: stone.dark,
|
|
167
169
|
};
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
// generateThemeCss(schema, prefix, options?) — the "stuic-" prefix is what the
|
|
172
|
+
// library's own CSS reads (--stuic-color-primary, …), so it is not optional.
|
|
173
|
+
writeFileSync("src/theme.css", generateThemeCss(myTheme, "stuic-"));
|
|
170
174
|
```
|
|
171
175
|
|
|
172
176
|
### Checklist
|
|
@@ -179,20 +183,26 @@ writeFileSync("src/theme.css", generateThemeCss(myTheme));
|
|
|
179
183
|
|
|
180
184
|
## Test Changes
|
|
181
185
|
|
|
186
|
+
The repo uses **pnpm**.
|
|
187
|
+
|
|
182
188
|
### Steps
|
|
183
189
|
|
|
184
|
-
1. Run `
|
|
185
|
-
2. Run `
|
|
186
|
-
3. Run `
|
|
187
|
-
4.
|
|
188
|
-
5.
|
|
189
|
-
6. Test
|
|
190
|
-
7. Test
|
|
190
|
+
1. Run `pnpm run build` - Check for errors
|
|
191
|
+
2. Run `pnpm run check` - TypeScript validation (`svelte-check`)
|
|
192
|
+
3. Run `pnpm run lint` - ESLint + Prettier
|
|
193
|
+
4. Run `pnpm test` - Node + browser suites (see [Testing](./testing.md))
|
|
194
|
+
5. Run `pnpm run dev` - Start dev server
|
|
195
|
+
6. Test in browser (http://localhost:8886)
|
|
196
|
+
7. Test light mode
|
|
197
|
+
8. Test dark mode (add `class="dark"` to `<html>`)
|
|
198
|
+
9. Test that `--stuic-color-primary` changes cascade
|
|
191
199
|
|
|
192
200
|
### Checklist
|
|
193
201
|
|
|
194
202
|
- [ ] No build errors
|
|
195
203
|
- [ ] No TypeScript errors
|
|
204
|
+
- [ ] Lint clean
|
|
205
|
+
- [ ] Tests green
|
|
196
206
|
- [ ] Component renders correctly
|
|
197
207
|
- [ ] Light mode works
|
|
198
208
|
- [ ] Dark mode works
|
|
@@ -1,45 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
GENERATED ANALYSIS — @marianmeres/stuic real-browser component testing
|
|
3
|
-
Produced 2026-06-08 by multi-agent research → adversarial verify → synthesize.
|
|
4
|
-
Claims verified against the codebase at commit cc9958b and the live
|
|
5
|
-
vitest-browser-svelte docs. Planning artifact; no code was changed.
|
|
6
|
-
-->
|
|
1
|
+
# Component Test Conventions
|
|
7
2
|
|
|
8
|
-
|
|
3
|
+
How to write a STUIC browser component test, and — just as important — **what is worth asserting**.
|
|
9
4
|
|
|
10
|
-
>
|
|
11
|
-
> asserting**. The headline: test _behavior the build can't see_ (events fire, bindings update,
|
|
5
|
+
> The headline: test _behavior the build can't see_ (events fire, bindings update,
|
|
12
6
|
> aria/disabled/active states, computed layout), not "does it render" (already gated by
|
|
13
7
|
> `svelte-check` + `publint` + the build). Use `render()` → locators → `expect.element`. In Svelte 5
|
|
14
8
|
> events are props, so you assert them with spies; snippet children come from `createRawSnippet`.
|
|
15
9
|
|
|
16
|
-
|
|
10
|
+
Companion to [`testing.md`](./testing.md), which covers the suite as a whole (both layers, what we
|
|
11
|
+
test and what we deliberately don't). This document is the how-to for the browser layer only.
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
component rendering ("50+ components × prop combinations = slow suite with tiny yield... Rendering is
|
|
20
|
-
already gated by svelte-check + publint + the build") and treats interactive/visual behavior as
|
|
21
|
-
out of scope.
|
|
13
|
+
## The line this layer does and doesn't cross
|
|
22
14
|
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
"Does it render / compile / export" is still low-yield and still covered by `svelte-check` +
|
|
16
|
+
`publint` + the build. We do **not** write tests for that.
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
traps, viewport-clamped anchor positioning (cf. the recent `9d8c974` annotation-clamp fix) — was
|
|
30
|
-
**previously impossible** (node/server build, no DOM, no `$effect`). Browser mode makes it possible,
|
|
31
|
-
and _this_ is the high-yield target.
|
|
32
|
-
|
|
33
|
-
**Task in the roadmap:** update `docs/testing.md` to add this browser-test layer so the docs aren't
|
|
34
|
-
self-contradictory — promote "interactive behavior" from ❌ to ✅-when-it's-a-real-contract, and point
|
|
35
|
-
to this directory. (See [`PROGRESS.md`](./PROGRESS.md), sprint task.)
|
|
18
|
+
"Does it _behave_" — click handlers, two-way `bind:`, `aria-*`/`disabled`/`active` state, focus
|
|
19
|
+
traps, viewport-clamped anchor positioning — was impossible before browser mode (node/server build,
|
|
20
|
+
no DOM, no `$effect`). That is the high-yield target, and the whole reason this layer exists.
|
|
36
21
|
|
|
37
22
|
## File naming & location
|
|
38
23
|
|
|
39
24
|
- One test per component, **co-located** next to the `.svelte` file (matches the existing co-located
|
|
40
25
|
style, e.g. `Input/phone-validation.test.ts`).
|
|
41
26
|
- Name it `ComponentName.svelte.test.ts` — the `.svelte.test.ts` suffix is what routes it into the
|
|
42
|
-
browser `client` project (see [01](./01-framework-setup.md)). A plain `*.test.ts` next to a
|
|
27
|
+
browser `client` project (see [`_archive/component-testing/01-framework-setup.md`](./_archive/component-testing/01-framework-setup.md)). A plain `*.test.ts` next to a
|
|
43
28
|
component stays in the fast node project (correct for extracted pure logic like `_internal/*.ts`).
|
|
44
29
|
|
|
45
30
|
## The canonical test
|
|
@@ -142,8 +127,22 @@ Rely on `expect.element` retries and awaited locator actions — **never fixed `
|
|
|
142
127
|
reactivity resolves through the retry loop; only assertions on _external_ universal state living in a
|
|
143
128
|
`*.svelte.ts` module may need `flushSync()` from `svelte`.
|
|
144
129
|
|
|
145
|
-
##
|
|
130
|
+
## Shared helpers
|
|
131
|
+
|
|
132
|
+
There is deliberately **no shared test-util module**. The one recurring helper — a `text()` snippet
|
|
133
|
+
factory for `children` — is three lines, so each test file declares its own copy rather than
|
|
134
|
+
importing across the tree:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
const text = (s: string) =>
|
|
138
|
+
createRawSnippet(() => ({ render: () => `<span>${s}</span>` }));
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Revisit only if a genuinely non-trivial helper shows up.
|
|
142
|
+
|
|
143
|
+
## Where this came from
|
|
146
144
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
The plan that produced this layer (roadmap, framework setup, coverage tiers, CI, and the completed
|
|
146
|
+
progress tracker) is archived under
|
|
147
|
+
[`_archive/component-testing/`](./_archive/component-testing/). It is history, not guidance — this
|
|
148
|
+
file is the live convention.
|
package/docs/testing.md
CHANGED
|
@@ -16,7 +16,7 @@ Tests are for what those tools can't see. There are **two layers**, split by fil
|
|
|
16
16
|
- **`*.test.ts` — node, fast.** Pure deterministic logic where a regression silently corrupts data.
|
|
17
17
|
- **`*.svelte.test.ts` — real browser (Chromium).** Component _behavior_ the build can't see: events firing, two-way `bind:`, `aria`/`disabled`/`active` state, focus traps, computed layout/positioning.
|
|
18
18
|
|
|
19
|
-
We still explicitly don't try to test everything. The browser layer targets **behavior contracts**, not "does it render" — see [`
|
|
19
|
+
We still explicitly don't try to test everything. The browser layer targets **behavior contracts**, not "does it render" — see [`testing-components.md`](./testing-components.md) for the how-to and the "what to assert" checklist.
|
|
20
20
|
|
|
21
21
|
## What we test
|
|
22
22
|
|
|
@@ -79,8 +79,8 @@ const t: TranslateFn = (k) => k;
|
|
|
79
79
|
For **component** tests (`*.svelte.test.ts`), the patterns differ — `render()` from
|
|
80
80
|
`vitest-browser-svelte`, locators, and the retry-able `expect.element`; events are props (assert with
|
|
81
81
|
spies); snippet children come from `createRawSnippet`. See
|
|
82
|
-
[`
|
|
83
|
-
|
|
82
|
+
[`testing-components.md`](./testing-components.md) for the full how-to and the "what to assert"
|
|
83
|
+
checklist.
|
|
84
84
|
|
|
85
85
|
## When in doubt
|
|
86
86
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/stuic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.172.0",
|
|
4
4
|
"packageManager": "pnpm@11.5.0",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|
|
@@ -183,6 +183,7 @@
|
|
|
183
183
|
"vitest-browser-svelte": "^2.2.1"
|
|
184
184
|
},
|
|
185
185
|
"dependencies": {
|
|
186
|
+
"@marianmeres/calendar-utils": "^2.0.0",
|
|
186
187
|
"@marianmeres/clog": "^3.21.0",
|
|
187
188
|
"@marianmeres/countries": "^1.1.0",
|
|
188
189
|
"@marianmeres/cron-parser": "^1.0.1",
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
GENERATED ANALYSIS — @marianmeres/stuic real-browser component testing
|
|
3
|
-
Produced 2026-06-08 by multi-agent research → adversarial verify → synthesize.
|
|
4
|
-
Claims verified against the codebase at commit cc9958b and the live Vitest 4 /
|
|
5
|
-
vitest-browser-svelte docs. Planning artifact; no code was changed.
|
|
6
|
-
-->
|
|
7
|
-
|
|
8
|
-
# Component Testing — @marianmeres/stuic
|
|
9
|
-
|
|
10
|
-
This directory holds the plan for introducing **real-browser component tests** to STUIC
|
|
11
|
-
(Vitest 4 Browser Mode + `vitest-browser-svelte` + Playwright/Chromium). It was produced
|
|
12
|
-
2026-06-08 from a research pass over the codebase and the current Svelte/Vitest ecosystem.
|
|
13
|
-
It is a **planning artifact — no code has been changed**; every claim is verified against the
|
|
14
|
-
repo at commit `cc9958b` or against the live docs cited in each section.
|
|
15
|
-
|
|
16
|
-
**Start here:** [`00-overview-and-roadmap.md`](./00-overview-and-roadmap.md). Then track and
|
|
17
|
-
resume execution from [`PROGRESS.md`](./PROGRESS.md).
|
|
18
|
-
|
|
19
|
-
## Documents
|
|
20
|
-
|
|
21
|
-
| # | Doc | Scope | Headline |
|
|
22
|
-
| --- | ---------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
23
|
-
| 00 | [overview-and-roadmap](./00-overview-and-roadmap.md) | synthesis + roadmap | The stack is the right default; vitest 3→4 upgrade is the gating prerequisite. |
|
|
24
|
-
| 01 | [framework-setup](./01-framework-setup.md) | infra | Upgrade vitest 4, add a `projects` split (node `server` + browser `client`), route by filename. |
|
|
25
|
-
| 02 | [test-conventions](./02-test-conventions.md) | how-to | `render()` + locators + `expect.element`; events are props (spies); snippets via `createRawSnippet`. |
|
|
26
|
-
| 03 | [component-coverage-roadmap](./03-component-coverage-roadmap.md) | what to cover | 74 components tiered; warm up on Button/Pill/Switch, one commit per component. |
|
|
27
|
-
| 04 | [hard-cases-and-e2e](./04-hard-cases-and-e2e.md) | the hard 30 | Portals/focus-traps/anchor-positioning: one "hard proof" now; standalone Playwright E2E deferred. |
|
|
28
|
-
| 05 | [ci](./05-ci.md) | automation | One ~30-line GitHub Actions workflow; install Chromium, run `pnpm test`. |
|
|
29
|
-
|
|
30
|
-
## How it was produced
|
|
31
|
-
|
|
32
|
-
Five parallel research agents (test-infra audit, full component inventory, multistep-format
|
|
33
|
-
extraction, two independent web-research angles on the stack) → synthesis → live-docs
|
|
34
|
-
verification of the exact Vitest 4 config syntax → this plan.
|
|
35
|
-
|
|
36
|
-
> Nothing here is decided beyond the four clarifying answers recorded in
|
|
37
|
-
> [`PROGRESS.md`](./PROGRESS.md) → Decisions log. Each doc's "Open questions / decisions needed"
|
|
38
|
-
> lists what still needs a call.
|