@marianmeres/stuic 3.117.0 → 3.118.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.
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ // Conventions escape hatch (docs/component-testing/02-test-conventions.md):
3
+ // AssetsPreview is imperative-only — it opens a ModalDialog via a ref method
4
+ // open(index?). A *.svelte.test.ts file can't hold a bind:this + a trigger, so
5
+ // this fixture exposes an opener button that calls open(openIndex); all other
6
+ // AssetsPreview props are forwarded through ...rest.
7
+ import type { ComponentProps } from "svelte";
8
+ import AssetsPreview from "./AssetsPreview.svelte";
9
+
10
+ let ref = $state<AssetsPreview>();
11
+ let {
12
+ openIndex,
13
+ ...rest
14
+ }: { openIndex?: number } & ComponentProps<typeof AssetsPreview> = $props();
15
+ </script>
16
+
17
+ <button data-testid="opener" onclick={() => ref?.open(openIndex)}>open</button>
18
+
19
+ <AssetsPreview bind:this={ref} {...rest} />
@@ -0,0 +1,4 @@
1
+ import AssetsPreview from "./AssetsPreview.svelte";
2
+ declare const AssetsPreview: any;
3
+ type AssetsPreview = ReturnType<typeof AssetsPreview>;
4
+ export default AssetsPreview;
@@ -36,7 +36,7 @@ Branch: `feat/component-testing`
36
36
  | Rank | Task | Source | Status |
37
37
  | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | ------ |
38
38
  | 14 | Rest of Tier 1 (Separator already smoke-tested · H, KbdShortcut, ButtonGroupRadio, ListItemButton, Card, TabbedMenu, IconSwap, Collapsible) | [03](./03-component-coverage-roadmap.md) #10–17 | ✅ |
39
- | 15 | Tier 2 — `FieldInput` first, then the Field\* family + OtpInput, Nav, etc. | [03](./03-component-coverage-roadmap.md) | 🚧 |
39
+ | 15 | Tier 2 — `FieldInput` first, then the Field\* family + OtpInput, Nav, etc. | [03](./03-component-coverage-roadmap.md) | |
40
40
  | 16 | Portals/focus-traps in browser mode (Modal, ModalDialog, Backdrop, Drawer, AlertConfirmPrompt) | [04](./04-hard-cases-and-e2e.md) | ✅ |
41
41
  | 17 | Anchor-positioned menus (DropdownMenu, CommandMenu, UserAvatarMenu) + extract search logic to `_internal` | [04](./04-hard-cases-and-e2e.md) | ✅ |
42
42
  | 18 | Standalone Playwright E2E layer (drag: Tree/FieldOptions/FieldFile; Milkdown; Checkout/auth flows) | [04](./04-hard-cases-and-e2e.md) | ⏭️ |
@@ -77,16 +77,22 @@ Other Tier 2:
77
77
  - [x] ImageCycler — single-image static contract: role=img, aria-label, data-fit, bg, snippets (5 tests)
78
78
  - [x] PricingTable — list/tiers, billing toggle switches prices, data flags, CTA callback (11 tests)
79
79
  - [x] SlidingPanels — fixture-driven imperative `show()`; post-transition panel destroy (3 tests)
80
- - [ ] Nav (expand/collapse) ⏭️ _postponed: 856-line component; needs a focused session_
81
- - [ ] ThemePreview ⏭️ _postponed: largely visual/presentational; low behavioral yield_
82
- - [ ] AppShell / AppShellSimple ⏭️ _postponed: layout-heavy, low behavioral yield_
83
- - [ ] AssetsPreview ⏭️ _postponed: heavy; pure-logic already covered by assets-preview-utils tests_
84
- - [ ] Notifications⏭️ _postponed: borderline Tier 3 (portal + timers); has a node test already_
85
-
86
- **Task 15 status:** core done 16 components (FieldInput + 6 Field family + 3 complex Field +
87
- OtpInput, TypeaheadInput, ColorScheme, ImageCycler, PricingTable, SlidingPanels). 5 postponed
88
- (Nav, ThemePreview, AppShell/AppShellSimple, AssetsPreview, Notifications) heaviest / lowest
89
- behavioral yield; revisit in a focused follow-up. Moving on to backlog #16/#17.
80
+ - [x] Nav — section title (interactive vs not), group expand/collapse + onGroupToggle, leaf href/onClick/
81
+ disabled variants, parent-with-children nesting, activeId/isActive auto-expand, isCollapsed (12 tests)
82
+ - [x] ThemePreview root class, default header vs header/sidebar/footer snippet overrides, showLabels/
83
+ showInputs/compact toggles, embedded sidebar Nav (8 tests)
84
+ - [x] AppShellslot-driven data-shell regions (conditional vs always-on), children in page-main,
85
+ pageFlexGrow → flex class (7 tests)
86
+ - [x] AppShellSimplemain always renders; header/rail/aside conditional; headerStyle applied (6 tests)
87
+ - [x] AssetsPreview inline gallery exercises AssetsPreviewContent (name/zoom-state/dots/arrow nav) +
88
+ modal fixture for imperative open()/Close; hermetic 1×1-PNG data URIs (11 tests)
89
+ - [x] Notifications reactive stack render: role=alert + data-type per intent, dedup count badge,
90
+ close-button removal, noXButton, multiple/empty stacks (8 tests)
91
+
92
+ **Task 15 status:** ✅ DONE — 22 components. Core 16 (FieldInput + 6 Field family + 3 complex Field +
93
+ OtpInput, TypeaheadInput, ColorScheme, ImageCycler, PricingTable, SlidingPanels) plus the final 6
94
+ (Nav, ThemePreview, AppShell, AppShellSimple, AssetsPreview inline+modal, Notifications). The whole
95
+ non-deferred backlog (#14–#17, #19) is now complete; only #18/#20 remain deferred.
90
96
 
91
97
  ### Task 16 — Portals / focus-traps
92
98
 
@@ -107,6 +113,26 @@ behavioral yield; revisit in a focused follow-up. Moving on to backlog #16/#17.
107
113
 
108
114
  ## Decisions log
109
115
 
116
+ - **2026-06-09** — **Task 15 closed — the final 5 postponed components done.** Nav, ThemePreview,
117
+ AppShell, AppShellSimple, AssetsPreview (inline + modal), Notifications — drafted + adversarially
118
+ reviewed in a parallel workflow, then verified against real Chromium + the full-suite gate before
119
+ per-component commits. **484 tests** green; `pnpm check`/`lint`/`test` all clean. The entire
120
+ non-deferred backlog (#14–#17, #19) is now complete; only #18 (standalone E2E) and #20 (visual
121
+ regression) remain deferred. New gotchas learned: (a) **un-caught image preload rejects on dead
122
+ URLs** — `AssetsPreview`/`AssetsPreviewInline` call `preloadImgs(...)` without `.catch`, so a
123
+ network/`example.com` URL surfaces as an unhandled rejection (and isn't hermetic for CI). Fix: pass
124
+ `AssetPreview` objects with a real 1×1-PNG **data-URI** `url` + explicit `name`/`type:"image"` so the
125
+ preload resolves offline. (b) **The AssetsPreviewContent slide has a ~300ms async tail** (`await
126
+ waitForNextRepaint()` + `sleep(300)`); if the component unmounts mid-slide, the continuation reads
127
+ `$state` on a torn-down scope → **`track_reactivity_loss` unhandled rejection**. Fix: after navigating,
128
+ poll until the slide settles (outgoing panel gone → a single `<img>`) so it finishes while mounted.
129
+ (c) Re-confirmed the `page.elementLocator` staleness rule — it snapshots an element by its current
130
+ text, so a name label that changes during navigation must be read live via `expect.poll(() =>
131
+ el.textContent)`. (d) `AssetsPreview.open(index)` settles back to index 0 (its open-effect reads then
132
+ nulls `_openIdx` and re-runs) — a component quirk; the modal test asserts open()/Close only and leaves
133
+ index-precise nav to the inline test. (e) **Nav persists expand state in localStorage** (persistState
134
+ defaults true) → pass `persistState={false}` + `localStorage.clear()` in beforeEach; and never `.click()`
135
+ a Nav `<a href>` leaf (it navigates the page) — assert its attributes instead.
110
136
  - **2026-06-08** — **Backlog #15/#16/#17 done (this session).** Tier-2 core (16 components), portals/
111
137
  focus-traps (#16: focus-trap action proof + Backdrop/Modal/Drawer/ModalDialog/AlertConfirmPrompt), and
112
138
  anchor menus (#17: DropdownMenu/CommandMenu/UserAvatarMenu) — all browser-mode, drafted+adversarially-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.117.0",
3
+ "version": "3.118.0",
4
4
  "packageManager": "pnpm@11.5.0",
5
5
  "scripts": {
6
6
  "dev": "vite dev",