@adia-ai/web-components 0.8.18 → 0.8.19
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 +13 -0
- package/components/select/select.a2ui.json +6 -1
- package/components/select/select.class.js +34 -7
- package/components/select/select.d.ts +2 -0
- package/components/select/select.test.js +28 -0
- package/components/select/select.yaml +16 -1
- package/dist/host.min.css +1 -1
- package/dist/host.sheet.js +1 -1
- package/dist/theme-provider.min.js +1 -1
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +16 -16
- package/dist/web-components.sheet.js +1 -1
- package/package.json +1 -1
- package/styles/colors/semantics/core.css +10 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog — @adia-ai/web-components
|
|
2
2
|
|
|
3
|
+
## [0.8.19] — 2026-07-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`select-ui[summary-label]` — multi-select trigger as one text line, not chips** (gh#442, ruled from the adiav2 admin dashboard filter bar). With the attribute set, a single selection shows its own label and N > 1 shows "Label (N)" ("Organizations (2)"); none shows the placeholder. The popover's checkbox rows, Backspace-removes-last, the comma-separated form value, `[select-all]`, and `[clearable]` semantics are unchanged, and chip mode stays the default when the attribute is absent (`[data-multi-chips]` is simply not set in summary mode, so the trigger keeps the plain single-line layout). Motivation: in a narrow filter-bar trigger, three selected organizations wrapped into a vertically ballooning chip stack; `max-chips` still renders chips and could not express the ruling. Entry re-homed from the root CHANGELOG (package-local change; PR #445 filed it there mid-flight while #452's re-home was landing).
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- **Row hover no longer goes near-black — `--a-bg-hover`/`--a-bg-active` (and the `--a-canvas-*` twins) move off the fill ladder onto the container alpha ladder** (`styles/colors/semantics/core.css`; operator directive 2026-07-27). The roles pointed at `neutral-hover`/`neutral-active` — the FILLED-BUTTON stops, opaque 650/700 — so all ten row/option/cell consumers (list, tree, select, command, calendar-grid, calendar-picker, date-range-picker, tag, agent-artifact) tinted toward black on hover, while the adjacent `--a-bg-selected` note already prescribed "hover = container-low". Now: hover = `container-low` (10%), active = `container` (20%, the selected/tonal tier). The fill ladder itself is untouched — filled buttons keep 600/650/700.
|
|
10
|
+
- **The unit suite can no longer touch real network** (`test-setup.js`) — happy-dom's `fetch` does real I/O against its default `http://localhost:3000` base URL, so test outcomes depended on which dev servers happened to be running (the v0.8.17 cut lost a gate run to an ECONNREFUSED flake in `onboarding-checklist.test.js`). A fast-rejecting guard now replaces `globalThis.fetch`; tests exercising fetch paths keep installing their own per-file mocks, which override it.
|
|
11
|
+
|
|
12
|
+
### Maintenance
|
|
13
|
+
- **`components/` touched in this release window** (6 file(s), e.g. `select/select.a2ui.json`) — carried by the entries above.
|
|
14
|
+
- **`dist/` bundles rebuilt** in this cut's window (6 file(s)) — regenerated from the source changes described above, not independent edits.
|
|
15
|
+
|
|
3
16
|
## [0.8.18] — 2026-07-27
|
|
4
17
|
|
|
5
18
|
### Maintenance
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://adiaui.dev/a2ui/v0_9/components/Select.json",
|
|
4
4
|
"title": "Select",
|
|
5
|
-
"description": "Single- and multi-select dropdown primitive — the canonical AdiaUI\nselect control. Form-bearing via UIFormElement: [name], [value],\n[required], [disabled], fires `change`. Options via native\n<option> / <optgroup> children, programmatic `.options` array, or\nJSON [data-options] (hydrated by <editor-shell>'s wireSelects).\nUse for single-select with > 4 options; for ≤ 4 options use\n<segmented-ui> or <radio-ui>. Multi-select via [multiple]: trigger\nrenders <tag-ui> chips per selection, popover shows checkbox-style\noption list, Backspace removes the last chip, [select-all] adds\nbulk controls, [max-chips] caps the visible chip count with a\n\"+N more\" pill
|
|
5
|
+
"description": "Single- and multi-select dropdown primitive — the canonical AdiaUI\nselect control. Form-bearing via UIFormElement: [name], [value],\n[required], [disabled], fires `change`. Options via native\n<option> / <optgroup> children, programmatic `.options` array, or\nJSON [data-options] (hydrated by <editor-shell>'s wireSelects).\nUse for single-select with > 4 options; for ≤ 4 options use\n<segmented-ui> or <radio-ui>. Multi-select via [multiple]: trigger\nrenders <tag-ui> chips per selection, popover shows checkbox-style\noption list, Backspace removes the last chip, [select-all] adds\nbulk controls, [max-chips] caps the visible chip count with a\n\"+N more\" pill, and [summary-label] swaps the chip trigger for one\ntext line — the single selection's label, or \"Label (N)\" for more.\nForm value stays a comma-separated string under\n[name] for native <form> compatibility (parse with .split(',')).\n",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"allOf": [
|
|
8
8
|
{
|
|
@@ -150,6 +150,11 @@
|
|
|
150
150
|
],
|
|
151
151
|
"default": "md"
|
|
152
152
|
},
|
|
153
|
+
"summary-label": {
|
|
154
|
+
"description": "Multi-select only (gh#442). Replaces the chip trigger with one\ntext line: a single selection shows its own label; N > 1 shows\n\"<summary-label> (N)\" — e.g. \"Organizations (2)\"; none shows the\nplaceholder. Popover checkbox rows, Backspace-removes-last, the\ncomma-separated form value, [select-all], and [clearable] are\nunchanged. Absent (default) = tag-ui chip trigger. Prefer this\nform for narrow triggers (filter bars) where wrapping chips\nwould balloon the control.\n",
|
|
155
|
+
"type": "string",
|
|
156
|
+
"default": null
|
|
157
|
+
},
|
|
153
158
|
"value": {
|
|
154
159
|
"description": "Currently selected option value. With declarative <option> children, a child carrying the native `selected` attribute sets the initial value when this attribute is absent (native <select> parity).",
|
|
155
160
|
"type": "string",
|
|
@@ -60,6 +60,10 @@ export class UISelect extends UIFormElement {
|
|
|
60
60
|
// These are no-ops without [multiple] but reflect so [maxChips]
|
|
61
61
|
// selectors etc. work in CSS even when authored declaratively.
|
|
62
62
|
maxChips: { type: Number, default: 0, reflect: true, attribute: 'max-chips' },
|
|
63
|
+
// gh#442: summary trigger for [multiple] — when set, the trigger shows
|
|
64
|
+
// one text line (a single selection's own label, or "Label (N)" for
|
|
65
|
+
// N > 1) instead of tag-ui chips. Absent = chip mode, unchanged.
|
|
66
|
+
summaryLabel: { type: String, default: '', reflect: true, attribute: 'summary-label' },
|
|
63
67
|
min: { type: Number, default: 0, reflect: true },
|
|
64
68
|
max: { type: Number, default: 0, reflect: true },
|
|
65
69
|
selectAll: { type: Boolean, default: false, reflect: true, attribute: 'select-all' },
|
|
@@ -404,7 +408,9 @@ export class UISelect extends UIFormElement {
|
|
|
404
408
|
// optional clear-x + optional "+N more" pill alongside the usual
|
|
405
409
|
// leading / display / caret. We toggle host attribute [data-multi-chips]
|
|
406
410
|
// so CSS can switch layout from single-line text to flex-wrap chip row.
|
|
407
|
-
|
|
411
|
+
// gh#442 — [summary-label] keeps the single-line text layout: the
|
|
412
|
+
// summary renders through [slot="display"], no chip row.
|
|
413
|
+
if (this.multiple && !this.summaryLabel) this.setAttribute('data-multi-chips', '');
|
|
408
414
|
else this.removeAttribute('data-multi-chips');
|
|
409
415
|
|
|
410
416
|
// A11y (FB-88): name a placeholder-only select from its placeholder, like
|
|
@@ -859,17 +865,18 @@ export class UISelect extends UIFormElement {
|
|
|
859
865
|
#stampChips() {
|
|
860
866
|
const slot = this.querySelector('[slot="trigger"] [data-chips]');
|
|
861
867
|
if (!slot) return;
|
|
868
|
+
// gh#442 — summary mode renders no chips; clear any stamped ones so a
|
|
869
|
+
// dynamic switch out of chip mode leaves no stale tag-ui behind.
|
|
870
|
+
if (this.summaryLabel) {
|
|
871
|
+
while (slot.firstChild) slot.removeChild(slot.firstChild);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
862
874
|
const values = this.#values();
|
|
863
875
|
const cap = Number(this.maxChips) || 0;
|
|
864
876
|
const cappedValues = cap > 0 && values.length > cap ? values.slice(0, cap) : values;
|
|
865
877
|
const overflow = cap > 0 && values.length > cap ? values.length - cap : 0;
|
|
866
878
|
// Label lookup from internal options model (handles grouped form too).
|
|
867
|
-
const
|
|
868
|
-
.filter((o) => !o.separator && !o.header && o.value != null);
|
|
869
|
-
const labelOf = (v) => {
|
|
870
|
-
const found = flat.find((o) => String(o.value) === String(v));
|
|
871
|
-
return found ? found.label : String(v);
|
|
872
|
-
};
|
|
879
|
+
const labelOf = (v) => this.#labelOf(v);
|
|
873
880
|
// Rebuild — chips are cheap and reconciliation gets us correct order
|
|
874
881
|
// + correct labels with no per-chip tracking state.
|
|
875
882
|
while (slot.firstChild) slot.removeChild(slot.firstChild);
|
|
@@ -893,8 +900,28 @@ export class UISelect extends UIFormElement {
|
|
|
893
900
|
}
|
|
894
901
|
}
|
|
895
902
|
|
|
903
|
+
/**
|
|
904
|
+
* Label for one value from the internal options model (grouped form
|
|
905
|
+
* included); falls back to the raw value string. Shared by the chip
|
|
906
|
+
* stamper and the gh#442 summary trigger.
|
|
907
|
+
*/
|
|
908
|
+
#labelOf(v) {
|
|
909
|
+
const flat = this.#options.flatMap((o) => o.options || [o])
|
|
910
|
+
.filter((o) => !o.separator && !o.header && o.value != null);
|
|
911
|
+
const found = flat.find((o) => String(o.value) === String(v));
|
|
912
|
+
return found ? found.label : String(v);
|
|
913
|
+
}
|
|
914
|
+
|
|
896
915
|
#displayText() {
|
|
897
916
|
if (this.multiple) {
|
|
917
|
+
// gh#442 — [summary-label]: one selection shows its own label, more
|
|
918
|
+
// show "Label (N)", none shows the placeholder.
|
|
919
|
+
if (this.summaryLabel) {
|
|
920
|
+
const values = this.#values();
|
|
921
|
+
if (values.length === 0) return this.placeholder;
|
|
922
|
+
if (values.length === 1) return this.#labelOf(values[0]);
|
|
923
|
+
return `${this.summaryLabel} (${values.length})`;
|
|
924
|
+
}
|
|
898
925
|
// SPEC-040 — chips render the selection; display element holds the
|
|
899
926
|
// placeholder when empty and is otherwise empty (CSS sees [data-multi-chips]).
|
|
900
927
|
return this.value ? '' : this.placeholder;
|
|
@@ -58,6 +58,8 @@ export class UISelect extends UIFormElement {
|
|
|
58
58
|
divider: boolean;
|
|
59
59
|
/** SPEC-040 — cap visible chips in multi-select trigger. 0 = unlimited. */
|
|
60
60
|
maxChips: number;
|
|
61
|
+
/** gh#442 — multi-select summary trigger: one selection shows its label, N > 1 shows "Label (N)"; empty string = chip mode. */
|
|
62
|
+
summaryLabel: string;
|
|
61
63
|
/** SPEC-040 — minimum required selections in multi-select mode. */
|
|
62
64
|
min: number;
|
|
63
65
|
/** SPEC-040 — maximum allowed selections in multi-select mode. 0 = unlimited. */
|
|
@@ -139,6 +139,34 @@ describe('select-ui', () => {
|
|
|
139
139
|
expect(chipValues).toContain('c');
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
it('multi-select: [summary-label] renders a text summary instead of chips (gh#442)', async () => {
|
|
143
|
+
const s = mount(`
|
|
144
|
+
<select-ui multiple summary-label="Organizations" value="a,c">
|
|
145
|
+
<option value="a">Riverside Health System</option>
|
|
146
|
+
<option value="b">Bay Area Health Group</option>
|
|
147
|
+
<option value="c">Greenfield Medical Group</option>
|
|
148
|
+
</select-ui>
|
|
149
|
+
`);
|
|
150
|
+
await tick();
|
|
151
|
+
expect(s.hasAttribute('data-multi-chips')).toBe(false);
|
|
152
|
+
expect(s.querySelectorAll('[slot="trigger"] [data-chips] tag-ui').length).toBe(0);
|
|
153
|
+
expect(s.querySelector('[slot="display"]').textContent).toBe('Organizations (2)');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('multi-select summary: one selection shows its own label; none shows the placeholder (gh#442)', async () => {
|
|
157
|
+
const s = mount(`
|
|
158
|
+
<select-ui multiple summary-label="Organizations" placeholder="All organizations" value="a">
|
|
159
|
+
<option value="a">Riverside Health System</option>
|
|
160
|
+
<option value="b">Bay Area Health Group</option>
|
|
161
|
+
</select-ui>
|
|
162
|
+
`);
|
|
163
|
+
await tick();
|
|
164
|
+
expect(s.querySelector('[slot="display"]').textContent).toBe('Riverside Health System');
|
|
165
|
+
s.value = '';
|
|
166
|
+
await tick();
|
|
167
|
+
expect(s.querySelector('[slot="display"]').textContent).toBe('All organizations');
|
|
168
|
+
});
|
|
169
|
+
|
|
142
170
|
it('multi-select: aria-multiselectable=true is set on the listbox', async () => {
|
|
143
171
|
const s = mount(`
|
|
144
172
|
<select-ui multiple open>
|
|
@@ -18,7 +18,9 @@ description: |
|
|
|
18
18
|
renders <tag-ui> chips per selection, popover shows checkbox-style
|
|
19
19
|
option list, Backspace removes the last chip, [select-all] adds
|
|
20
20
|
bulk controls, [max-chips] caps the visible chip count with a
|
|
21
|
-
"+N more" pill
|
|
21
|
+
"+N more" pill, and [summary-label] swaps the chip trigger for one
|
|
22
|
+
text line — the single selection's label, or "Label (N)" for more.
|
|
23
|
+
Form value stays a comma-separated string under
|
|
22
24
|
[name] for native <form> compatibility (parse with .split(',')).
|
|
23
25
|
# Per ADR-0027 — primitives that programmatically create other primitives
|
|
24
26
|
# do NOT auto-import them. Consumer (or demo shell) must explicitly import.
|
|
@@ -120,6 +122,19 @@ props:
|
|
|
120
122
|
type: number
|
|
121
123
|
default: 0
|
|
122
124
|
reflect: true
|
|
125
|
+
summary-label:
|
|
126
|
+
description: |
|
|
127
|
+
Multi-select only (gh#442). Replaces the chip trigger with one
|
|
128
|
+
text line: a single selection shows its own label; N > 1 shows
|
|
129
|
+
"<summary-label> (N)" — e.g. "Organizations (2)"; none shows the
|
|
130
|
+
placeholder. Popover checkbox rows, Backspace-removes-last, the
|
|
131
|
+
comma-separated form value, [select-all], and [clearable] are
|
|
132
|
+
unchanged. Absent (default) = tag-ui chip trigger. Prefer this
|
|
133
|
+
form for narrow triggers (filter bars) where wrapping chips
|
|
134
|
+
would balloon the control.
|
|
135
|
+
type: string
|
|
136
|
+
default: null
|
|
137
|
+
reflect: true
|
|
123
138
|
min:
|
|
124
139
|
description: |
|
|
125
140
|
Multi-select only. Minimum required selections. Below this floor
|