@adia-ai/web-modules 0.6.16 → 0.6.18
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
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-modules
|
|
2
2
|
|
|
3
|
+
## [0.6.18] — 2026-05-21
|
|
4
|
+
|
|
5
|
+
### Added — `admin-sidebar` resize-handle diagnostic (FB-17)
|
|
6
|
+
|
|
7
|
+
- **`<admin-sidebar resizable>` now warns when no `[data-resize]` child is
|
|
8
|
+
present.** `#setupResizeHandle()` previously returned silently when the
|
|
9
|
+
author-supplied drag-handle element was missing, leaving `resizable` a no-op
|
|
10
|
+
with zero diagnostic signal. It now emits a one-shot `console.warn`
|
|
11
|
+
(WeakSet-guarded) naming the fix. Warning-only; no functional change.
|
|
12
|
+
(~22 LOC `admin-sidebar.js`.)
|
|
13
|
+
|
|
14
|
+
### Docs
|
|
15
|
+
|
|
16
|
+
- **Live `<admin-sidebar>` variant gallery + collapse/expand demo** added to `admin-shell.html` and `admin-shell.examples.html` — covers the resizable / collapsible / variant axes in one browsable surface.
|
|
17
|
+
|
|
18
|
+
### Note
|
|
19
|
+
|
|
20
|
+
- The headline v0.6.18 work shipped in `@adia-ai/web-components` — `stat-ui` + `table-ui` `loading` props (FB-12 P2) and `text-ui` `size` / `color` / `weight` / `text-align` overlay attributes (FB-10). See `packages/web-components/CHANGELOG.md#0618--2026-05-21` for details.
|
|
21
|
+
|
|
22
|
+
## [0.6.17] — 2026-05-21
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- **Collapsed sidebar — text labels overflow for vanilla HTML consumers.** When `<admin-sidebar>` collapses to its ≤96px icon-rail state, the existing `@container sidebar (max-width: 96px)` rules in `admin-shell.collapsed.css` target only the AdiaUI primitives (`<nav-ui>`, `<nav-item-ui>`, `<select-ui>`, `<button-ui>`). Consumers that use vanilla HTML inside the sidebar — `<button class="nav-item">Label</button>` with native text nodes, or `<span slot="heading">Brand</span>` for the app wordmark — were not covered, and their text content overflowed the 48px rail via `overflow: visible`. Symptom: "Claims UI" wordmark wrapped to two lines, and nav labels (Dashboard/Claims/Users/Settings) rendered visibly past the rail edge into the content area. Reported by claims-ui-v3 cold-start v0.6.16 screenshot.
|
|
26
|
+
|
|
27
|
+
Fix adds a forgiving fallback block to `admin-shell.collapsed.css`:
|
|
28
|
+
- Clips overflow on the sidebar itself + chrome containers (`admin-topbar`, `admin-statusbar`, `section`, `section-ui`, `nav`) so escaping text descendants stay inside the rail edge.
|
|
29
|
+
- Hides `[slot="heading"]` children — authors who want a brand mark visible when collapsed should put an `<icon-ui>` or logo image in `[slot="leading"]` instead.
|
|
30
|
+
- For vanilla `<button class="nav-item">`, `<a class="nav-item">`, and `[data-nav]` — applies `justify-content: center`, `overflow: hidden`, `text-indent: -9999px`, `white-space: nowrap` to clip text labels while preserving icons. Restores `text-indent: 0` on child `<icon-ui>` so the icon itself stays visible.
|
|
31
|
+
|
|
32
|
+
Using `<nav-item-ui>` / `<nav-ui>` remains the canonical, more capable path — popover tooltips on hover, badge slots, separator drawing all work via the primitives. The fallback is a safety net for consumers who don't (yet) wire the AdiaUI primitive stack inside the sidebar. Files: `packages/web-modules/shell/admin-shell/css/admin-shell.collapsed.css` (+60 LOC).
|
|
33
|
+
|
|
34
|
+
### Verification
|
|
35
|
+
- `components.mjs --verify` clean (142 files up-to-date).
|
|
36
|
+
- `verify:traits` 56/56 clean.
|
|
37
|
+
- `check:lockstep` — all 9 at 0.6.17, ranges at ^0.6.0.
|
|
38
|
+
- `eval:diff --engine zettel` cov=49% / avg=90 (baseline-identical).
|
|
39
|
+
- Browser-verified at `http://localhost:5175/` (claims-ui-v3, after copying the updated CSS to local `node_modules`): toggling the sidebar to collapsed state hides `[slot="heading"]` (display=none), applies `text-indent: -9999px` + `overflow: hidden` to vanilla nav buttons, and preserves icon visibility via `text-indent: 0` on `<icon-ui>` descendants. Vision pass confirms no overflow, no "Claims UI" wrapping, icons stacked vertically in the rail.
|
|
40
|
+
|
|
3
41
|
## [0.6.16] — 2026-05-21
|
|
4
42
|
|
|
5
43
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-modules",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.18",
|
|
4
4
|
"description": "AdiaUI composite custom elements \u2014 shell, chat, editor, runtime clusters built from @adia-ai/web-components primitives. Subpath exports per cluster.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname, resolve } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const bespokeCSS = readFileSync(resolve(__dirname, 'css/admin-shell.bespoke.css'), 'utf8');
|
|
8
|
+
const collapsedCSS = readFileSync(resolve(__dirname, 'css/admin-shell.collapsed.css'), 'utf8');
|
|
9
|
+
|
|
10
|
+
// ── Helpers ────────────────────────────────────────────────────────
|
|
11
|
+
function mount(html) {
|
|
12
|
+
document.body.innerHTML = html;
|
|
13
|
+
return document.body.firstElementChild;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
document.body.innerHTML = '';
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// ── Regression guards ─────────────────────────────────────────────
|
|
21
|
+
//
|
|
22
|
+
// These tests document substrate selector decisions that have been
|
|
23
|
+
// regressed and re-fixed at least once each. They run against the raw
|
|
24
|
+
// CSS file content + DOM `element.matches()` rather than computed style,
|
|
25
|
+
// because happy-dom doesn't fully resolve external stylesheet cascade.
|
|
26
|
+
// The trade-off: we don't assert that the rule WAS applied, but we DO
|
|
27
|
+
// assert that the rule's selector REACHES the element it's meant to
|
|
28
|
+
// style — which is the part that broke in v0.6.16.
|
|
29
|
+
|
|
30
|
+
describe('admin-shell.bespoke.css — selector reachability', () => {
|
|
31
|
+
it('v0.6.16 fix: admin-page rule uses descendant combinator (not >)', () => {
|
|
32
|
+
// The previous selector `admin-scroll > admin-page` broke when a
|
|
33
|
+
// router (e.g. <router-ui>) sat between <admin-scroll> and
|
|
34
|
+
// <admin-page>. The fix uses :is() + descendant combinator so any
|
|
35
|
+
// depth of intermediate wrapping still matches.
|
|
36
|
+
expect(bespokeCSS).toMatch(/:is\(admin-scroll,\s*admin-content\)\s+admin-page\s*\{/);
|
|
37
|
+
// Negative assertion: the broken direct-child form should not be
|
|
38
|
+
// present any more (catches accidental reverts).
|
|
39
|
+
expect(bespokeCSS).not.toMatch(/^admin-scroll\s*>\s*admin-page\s*,/m);
|
|
40
|
+
expect(bespokeCSS).not.toMatch(/^admin-content\s*>\s*admin-page\s*\{/m);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('admin-page rule reaches admin-page inside <router-ui>', () => {
|
|
44
|
+
// Synthesize the docs-site DOM shape that hit the v0.6.16 bug.
|
|
45
|
+
// The selector under test must match <admin-page> via descendant
|
|
46
|
+
// combinator even when <router-ui> sits between.
|
|
47
|
+
mount(`
|
|
48
|
+
<admin-shell>
|
|
49
|
+
<admin-content>
|
|
50
|
+
<admin-scroll>
|
|
51
|
+
<router-ui id="router">
|
|
52
|
+
<admin-page id="page"></admin-page>
|
|
53
|
+
</router-ui>
|
|
54
|
+
</admin-scroll>
|
|
55
|
+
</admin-content>
|
|
56
|
+
</admin-shell>
|
|
57
|
+
`);
|
|
58
|
+
const page = document.getElementById('page');
|
|
59
|
+
expect(page).toBeTruthy();
|
|
60
|
+
// Direct-child match should fail (router-ui sits between)
|
|
61
|
+
expect(page.matches('admin-scroll > admin-page')).toBe(false);
|
|
62
|
+
expect(page.matches('admin-content > admin-page')).toBe(false);
|
|
63
|
+
// Descendant match must succeed (this is what v0.6.17 introduced)
|
|
64
|
+
expect(page.matches(':is(admin-scroll, admin-content) admin-page')).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('admin-page rule still reaches admin-page as direct child', () => {
|
|
68
|
+
// Canonical playground/example layout — no router wrapper.
|
|
69
|
+
// The descendant combinator must still match this case.
|
|
70
|
+
mount(`
|
|
71
|
+
<admin-shell>
|
|
72
|
+
<admin-content>
|
|
73
|
+
<admin-scroll>
|
|
74
|
+
<admin-page id="page"></admin-page>
|
|
75
|
+
</admin-scroll>
|
|
76
|
+
</admin-content>
|
|
77
|
+
</admin-shell>
|
|
78
|
+
`);
|
|
79
|
+
const page = document.getElementById('page');
|
|
80
|
+
expect(page.matches(':is(admin-scroll, admin-content) admin-page')).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('admin-shell.collapsed.css — vanilla HTML fallback (v0.6.17)', () => {
|
|
85
|
+
it('contains the vanilla-HTML fallback block', () => {
|
|
86
|
+
// v0.6.17 added rules covering <button class="nav-item"> etc. so
|
|
87
|
+
// text labels don't overflow the 48px rail when the consumer doesn't
|
|
88
|
+
// use AdiaUI <nav-item-ui> primitives.
|
|
89
|
+
expect(collapsedCSS).toMatch(/Vanilla-HTML fallback/);
|
|
90
|
+
expect(collapsedCSS).toMatch(/\[slot="heading"\]\s*\{[\s\S]*?display:\s*none/);
|
|
91
|
+
expect(collapsedCSS).toMatch(/button\.nav-item/);
|
|
92
|
+
expect(collapsedCSS).toMatch(/text-indent:\s*-9999px/);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('preserves icon visibility inside vanilla nav buttons', () => {
|
|
96
|
+
// text-indent:-9999px on the parent must be undone on the icon
|
|
97
|
+
// child, otherwise the icon disappears with the text. Both rules
|
|
98
|
+
// must be present in the file.
|
|
99
|
+
expect(collapsedCSS).toMatch(/button\.nav-item\s*>\s*icon-ui[\s\S]*?text-indent:\s*0/);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('fallback rules sit inside the @container sidebar query', () => {
|
|
103
|
+
// The vanilla-HTML fallback must be scoped to the collapsed state
|
|
104
|
+
// (max-width: 96px container query). Outside that scope, the
|
|
105
|
+
// text-indent: -9999px clip would also break the expanded state.
|
|
106
|
+
const containerMatch = collapsedCSS.match(/@container\s+sidebar\s*\(max-width:\s*96px\)\s*\{([\s\S]+)\}/);
|
|
107
|
+
expect(containerMatch).toBeTruthy();
|
|
108
|
+
const inside = containerMatch[1];
|
|
109
|
+
expect(inside).toMatch(/Vanilla-HTML fallback/);
|
|
110
|
+
expect(inside).toMatch(/text-indent:\s*-9999px/);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -83,4 +83,64 @@
|
|
|
83
83
|
button-ui [slot="trailing"] {
|
|
84
84
|
display: none;
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
/* ─── Vanilla-HTML fallback ───
|
|
88
|
+
The primitive-targeted rules above (nav-ui, nav-item-ui, button-ui) cover
|
|
89
|
+
the canonical AdiaUI composition. Consumers using vanilla HTML inside the
|
|
90
|
+
sidebar (e.g. <button class="nav-item">Label</button> with native text
|
|
91
|
+
nodes, or <span slot="heading">Brand</span>) need a forgiving fallback —
|
|
92
|
+
without it, text labels overflow the 48px collapsed bbox via
|
|
93
|
+
`overflow: visible` and spill into the content area, producing the
|
|
94
|
+
"Claims UI wraps to two lines + nav labels visible past the rail" bug
|
|
95
|
+
reported by claims-ui-v3 cold-start v0.6.16 §sidebar-overflow.
|
|
96
|
+
|
|
97
|
+
Strategy: clip the sidebar itself + hide common text-bearing slot
|
|
98
|
+
children. Icons (<icon-ui>) and avatars (<img>, <avatar-ui>) stay
|
|
99
|
+
visible. This is a safety net — using <nav-item-ui> / <nav-ui> remains
|
|
100
|
+
the canonical, more capable path (popover tooltips, badges, etc.). */
|
|
101
|
+
|
|
102
|
+
/* Clip the sidebar's chrome containers — text descendants that escape
|
|
103
|
+
their parent's width are now invisible past the rail edge. */
|
|
104
|
+
:scope,
|
|
105
|
+
:is(admin-topbar, admin-statusbar),
|
|
106
|
+
:is(section, section-ui, nav) {
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Hide [slot="heading"] (typically the app brand label in the sidebar's
|
|
111
|
+
<admin-topbar slot="header">) — keeps the rail clean in collapsed mode.
|
|
112
|
+
Authors who want a brand mark visible when collapsed should put an
|
|
113
|
+
<icon-ui> or logo image in [slot="leading"] instead. */
|
|
114
|
+
[slot="heading"] {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Vanilla nav buttons: any <button class="nav-item"> or [data-nav] item
|
|
119
|
+
not built from nav-item-ui. Hide non-icon children so only the icon
|
|
120
|
+
remains visible. Detection: a child of the sidebar that is NOT
|
|
121
|
+
itself an icon/avatar primitive and is NOT an element wrapping such
|
|
122
|
+
a primitive — i.e., bare text/spans/labels. */
|
|
123
|
+
button.nav-item,
|
|
124
|
+
a.nav-item,
|
|
125
|
+
[data-nav] {
|
|
126
|
+
/* Center the icon; hide any text by clipping (we can't safely
|
|
127
|
+
:not(:has(icon-ui)) every child without false-positives, so we
|
|
128
|
+
clip the box and rely on flex centering the icon). */
|
|
129
|
+
justify-content: center;
|
|
130
|
+
overflow: hidden;
|
|
131
|
+
text-indent: -9999px; /* push label text off-screen */
|
|
132
|
+
white-space: nowrap;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* But: keep the icon inside the button visible. text-indent: -9999px
|
|
136
|
+
would push the icon too — so restore positioning for the icon child
|
|
137
|
+
and any element that wraps it. */
|
|
138
|
+
button.nav-item > icon-ui,
|
|
139
|
+
a.nav-item > icon-ui,
|
|
140
|
+
[data-nav] > icon-ui,
|
|
141
|
+
button.nav-item > :has(> icon-ui),
|
|
142
|
+
a.nav-item > :has(> icon-ui),
|
|
143
|
+
[data-nav] > :has(> icon-ui) {
|
|
144
|
+
text-indent: 0;
|
|
145
|
+
}
|
|
86
146
|
}
|
|
@@ -54,6 +54,10 @@ class AdminSidebar extends UIElement {
|
|
|
54
54
|
|
|
55
55
|
static template = () => null;
|
|
56
56
|
|
|
57
|
+
// FEEDBACK-17: elements already warned about a missing [data-resize]
|
|
58
|
+
// handle — one-shot per element so re-renders stay quiet. GC-friendly.
|
|
59
|
+
static #warnedNoHandle = new WeakSet();
|
|
60
|
+
|
|
57
61
|
// The width the sidebar had before being collapsed — used for restore.
|
|
58
62
|
// Map keyed by sidebar name allows multiple sidebars on one host.
|
|
59
63
|
#previousExpandedWidth = '';
|
|
@@ -149,7 +153,23 @@ class AdminSidebar extends UIElement {
|
|
|
149
153
|
|
|
150
154
|
#setupResizeHandle() {
|
|
151
155
|
const handle = this.querySelector(':scope > [data-resize]');
|
|
152
|
-
if (!handle)
|
|
156
|
+
if (!handle) {
|
|
157
|
+
// FEEDBACK-17: `resizable` opts in to drag-resize but relies on an
|
|
158
|
+
// author-supplied `[data-resize]` child. A silent return left authors
|
|
159
|
+
// with a no-op `resizable` attribute and zero diagnostic signal.
|
|
160
|
+
if (!AdminSidebar.#warnedNoHandle.has(this)) {
|
|
161
|
+
AdminSidebar.#warnedNoHandle.add(this);
|
|
162
|
+
// eslint-disable-next-line no-console
|
|
163
|
+
console.warn(
|
|
164
|
+
'[admin-sidebar] `resizable` is set but no `[data-resize]` child ' +
|
|
165
|
+
'was found. Add `<div data-resize></div>` as a direct child to ' +
|
|
166
|
+
'enable drag-to-resize. (Unlike `collapsible`, `resizable` requires ' +
|
|
167
|
+
'this author-supplied handle.)',
|
|
168
|
+
this,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
153
173
|
|
|
154
174
|
const slot = this.getAttribute('slot');
|
|
155
175
|
const isLeading = slot === 'leading';
|