@astryxdesign/core 0.7.3-canary.fba04ed → 0.7.3

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/dist/Button/Button.d.ts.map +1 -1
  3. package/dist/Button/Button.js +15 -2
  4. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  5. package/dist/Chat/ChatComposer.js +25 -2
  6. package/dist/Chat/ChatSystemMessage.d.ts +4 -0
  7. package/dist/Chat/ChatSystemMessage.d.ts.map +1 -1
  8. package/dist/Chat/ChatSystemMessage.js +4 -2
  9. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +9 -5
  10. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  11. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +2 -0
  12. package/package.json +4 -4
  13. package/src/Button/Button.test.tsx +13 -2
  14. package/src/Button/Button.tsx +23 -2
  15. package/src/Button/__tests__/Button.a11y.states.ts +4 -3
  16. package/src/Carousel/Carousel.spec.md +146 -0
  17. package/src/Chat/ChatComposer.spec.md +26 -26
  18. package/src/Chat/ChatComposer.test.tsx +58 -2
  19. package/src/Chat/ChatComposer.tsx +25 -1
  20. package/src/Chat/ChatSystemMessage.doc.mjs +1 -1
  21. package/src/Chat/ChatSystemMessage.test.tsx +25 -3
  22. package/src/Chat/ChatSystemMessage.tsx +6 -4
  23. package/src/Chat/__tests__/ChatSystemMessage.a11y.chromium.spec.ts +8 -9
  24. package/src/CheckboxList/__tests__/CheckboxList.a11y.chromium.spec.ts +7 -6
  25. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +62 -7
  26. package/src/DropdownMenu/DropdownMenuRadioGroup.spec.md +190 -0
  27. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +10 -4
  28. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +93 -0
  29. package/src/DropdownMenu/__tests__/DropdownMenuRadioGroupAudit.a11y.chromium.spec.ts +327 -0
  30. package/src/FieldStatus/FieldStatus.spec.md +4 -4
  31. package/src/FieldStatus/__tests__/StatusMessage.a11y.renders.tsx +0 -18
  32. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +3 -18
  33. package/src/FieldStatus/__tests__/StatusMessage.a11y.test.tsx +1 -2
  34. package/src/Markdown/Markdown.helpers.perf.test.ts +25 -19
  35. package/src/Table/plugins/columnResize/useTableColumnResize.spec.md +11 -11
  36. package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +4 -3
@@ -130,9 +130,9 @@ intentional decision.
130
130
  region, including on first mount and when message or type changes, without
131
131
  moving focus. Inside a mounted live container, `spec:AST-086` FR4 applies.
132
132
  - **AR5 — Assistive-technology output.** Claims about whether, when, or how the
133
- message is spoken or presented in braille MUST be verified through the real-AT
134
- evidence process in `spec:AST-009`; DOM and accessibility-tree checks prove
135
- only programmatic exposure and routing.
133
+ message is spoken or presented in braille are recommended real-AT follow-up
134
+ under `spec:AST-009`, not a merge or release requirement; DOM and
135
+ accessibility-tree checks prove programmatic exposure and routing.
136
136
  - **AR2 — Visible description.** The rendered message box MUST remain available
137
137
  to assistive technology for `aria-describedby` association and MUST NOT become
138
138
  its own newly mounted live region.
@@ -182,7 +182,7 @@ change either presentation.
182
182
  | FR5, FR6 | `FieldStatus.test.tsx` forwarding and update suites | Initial render and rerender | Breaking the released root or update behavior fails compatibility assertions. | `audit:FieldStatus/behavior` |
183
183
  | AR1 | `StatusMessage.a11y.test.tsx` and `StatusMessage.a11y.chromium.spec.ts` | Error, warning, and success; post-mount message replacement | Removing the persistent channel, changing its role/urgency, or dropping a post-mount update fails the shared status-message contract. | `audit:FieldStatus/accessibility` |
184
184
  | AR1 | `FieldStatus.test.tsx` channel-routing suite | First non-empty mount; same-instance message and type changes | Breaking FieldStatus's first-use hook call or severity rerouting fails component-specific assertions. | `audit:FieldStatus/accessibility` |
185
- | AR5 | Pending/unverified real-AT evidence governed by `spec:AST-009` | First mount; message replacement; type/urgency change | Spoken and braille output remain unverified until a durable named AT/browser receipt exists. | `audit:FieldStatus/accessibility` |
185
+ | AR5 | Recommended real-AT follow-up under `spec:AST-009` | First mount; message replacement; type/urgency change | Spoken and braille output stay unverified until a real-AT receipt exists; this does not block merge or release. | `audit:FieldStatus/accessibility` |
186
186
  | AR2 | `FieldStatus.test.tsx` | Attached/detached visible message and empty message | The visible description becomes a second live region or is hidden from assistive technology. | `audit:FieldStatus/accessibility` |
187
187
  | Theming anatomy map | `scripts/check-knowledge.mjs` | Consumer anatomy and current targets | Missing, extra, duplicated, prefixed, or stale mappings fail repository validation. | `audit:FieldStatus/theming` |
188
188
 
@@ -10,7 +10,6 @@
10
10
  */
11
11
 
12
12
  import {useEffect, useState, type ReactElement, type ReactNode} from 'react';
13
- import {ChatSystemMessage} from '../../Chat/ChatSystemMessage';
14
13
  import {ProgressBar} from '../../ProgressBar/ProgressBar';
15
14
  import {Spinner} from '../../Spinner/Spinner';
16
15
  import {Toast} from '../../Toast/Toast';
@@ -198,22 +197,6 @@ function SpinnerHarness({visibleLabel}: {visibleLabel: boolean}) {
198
197
  );
199
198
  }
200
199
 
201
- function ChatSystemMessageHarness() {
202
- const initial = 'Conversation started';
203
- const [message, setMessage] = useState(initial);
204
- return (
205
- <Frame>
206
- <TransitionButton name="show" onClick={() => setMessage(initial)} />
207
- <TransitionButton
208
- name="replace"
209
- onClick={() => setMessage('A file was shared')}
210
- />
211
- <TransitionButton name="clear" onClick={() => setMessage('')} />
212
- <ChatSystemMessage>{message}</ChatSystemMessage>
213
- </Frame>
214
- );
215
- }
216
-
217
200
  function ProgressHarness({
218
201
  initial,
219
202
  progressValue = 40,
@@ -289,7 +272,6 @@ export const CORE_STATUS_MESSAGE_STATE_RENDERS: Record<
289
272
  <SpinnerHarness visibleLabel={false} />
290
273
  ),
291
274
  'spinner-visible-label-mounted': () => <SpinnerHarness visibleLabel />,
292
- 'chat-system-status-mounted': () => <ChatSystemMessageHarness />,
293
275
  'progress-loading-to-complete': () => <ProgressHarness />,
294
276
  'progress-custom-range': () => (
295
277
  <ProgressHarness
@@ -14,7 +14,6 @@ export type CoreStatusMessageBinding =
14
14
  | 'Toast card'
15
15
  | 'FieldStatus announcement channel'
16
16
  | 'Spinner'
17
- | 'ChatSystemMessage'
18
17
  | 'ProgressBar';
19
18
 
20
19
  export interface CoreStatusMessageBindingDefinition {
@@ -188,20 +187,6 @@ export const CORE_STATUS_MESSAGE_BINDING_STATES = [
188
187
  }),
189
188
  storyId: 'a11y-status-message-pattern--spinner-visible-label-mounted',
190
189
  },
191
- {
192
- id: 'chat-system-status-mounted',
193
- binding: 'ChatSystemMessage',
194
- summary: 'a chat status notice is mounted with its live-region content',
195
- facts: liveFacts({
196
- role: 'status',
197
- politeness: 'polite',
198
- initialMessage: 'Conversation started',
199
- message: 'Conversation started',
200
- replacement: 'A file was shared',
201
- semanticTransitions: ['replace', 'clear'],
202
- }),
203
- storyId: 'a11y-status-message-pattern--chat-system-status-mounted',
204
- },
205
190
  {
206
191
  id: 'progress-loading-to-complete',
207
192
  binding: 'ProgressBar',
@@ -279,10 +264,10 @@ export const CORE_STATUS_MESSAGE_EXCLUSIONS = [
279
264
  'A composing control that marks Spinner aria-hidden owns the surrounding busy semantics; the decorative ring is not another status message.',
280
265
  },
281
266
  {
282
- owner: 'ChatSystemMessage divider',
283
- part: 'date or section separator',
267
+ owner: 'ChatSystemMessage',
268
+ part: 'chat notice or date separator',
284
269
  reason:
285
- 'A divider does not report success, results, waiting, progress, or an error. Its current forced status role is tracked as a separate ownership mismatch rather than adopted here.',
270
+ 'spec:AST-086 FR4: a notice added to a chat log is delivered by the log, so neither the notice row nor the divider carries its own live role; a date or section separator is ordinary transcript content.',
286
271
  },
287
272
  {
288
273
  owner: 'ChatMessageList and streaming logs',
@@ -128,7 +128,6 @@ describe('Core status-message binding inventory', () => {
128
128
  ),
129
129
  ].sort(),
130
130
  ).toEqual([
131
- 'ChatSystemMessage',
132
131
  'FieldStatus announcement channel',
133
132
  'ProgressBar',
134
133
  'Spinner',
@@ -141,7 +140,7 @@ describe('Core status-message binding inventory', () => {
141
140
  expect(CORE_STATUS_MESSAGE_EXCLUSIONS.map(entry => entry.part)).toEqual([
142
141
  'visible validation text',
143
142
  'aria-hidden decorative spinner',
144
- 'date or section separator',
143
+ 'chat notice or date separator',
145
144
  'role=log transcript updates',
146
145
  'composer errors and tool-run states',
147
146
  'busy announcement',
@@ -357,17 +357,17 @@ describe('Markdown helper performance', () => {
357
357
  }),
358
358
  ];
359
359
 
360
- // Timed on the transform stage alone and compared at the FASTEST
361
- // sample of each side: this is a few hundred microseconds against a
362
- // parse of milliseconds, so measuring the whole parse buries it, and
363
- // a minimum is the least-disturbed sample each side achieved.
360
+ // Timed on the transform stage alone: this is a few hundred
361
+ // microseconds against a parse of milliseconds, so measuring the whole
362
+ // parse buries it. Each side is compared at its FASTEST sample, the
363
+ // least-disturbed run it achieved, and the two sides alternate sample by
364
+ // sample so a slow stretch of the machine reaches both instead of one.
364
365
  const root = parseMarkdownAst(source);
365
- const fastestTransform = (
366
+ const transformOnly = (
366
367
  plugins: ReadonlyArray<ReturnType<typeof createMarkdownPlugin>>,
367
- ): number => {
368
+ ) => {
368
369
  const prepared = prepareMarkdownPlugins(plugins);
369
- let fastest = Number.POSITIVE_INFINITY;
370
- for (let sample = 0; sample < 60; sample++) {
370
+ return () => {
371
371
  const start = performance.now();
372
372
  benchmarkSink ^= applyMarkdownTransforms(
373
373
  root,
@@ -376,18 +376,24 @@ describe('Markdown helper performance', () => {
376
376
  true,
377
377
  'block',
378
378
  ).children.length;
379
- fastest = Math.min(fastest, performance.now() - start);
380
- }
381
- void benchmarkSink;
382
- return fastest;
379
+ return performance.now() - start;
380
+ };
383
381
  };
384
-
385
- const single = plugin('scale-single', gaps);
386
- const double = plugin('scale-double', doubled);
387
- // Warm both before either is recorded.
388
- fastestTransform(single);
389
- fastestTransform(double);
390
- const ratio = fastestTransform(double) / fastestTransform(single);
382
+ const single = transformOnly(plugin('scale-single', gaps));
383
+ const double = transformOnly(plugin('scale-double', doubled));
384
+ let fastestSingle = Number.POSITIVE_INFINITY;
385
+ let fastestDouble = Number.POSITIVE_INFINITY;
386
+ for (let sample = 0; sample < 80; sample++) {
387
+ // The first rounds only warm both sides.
388
+ const singleTime = single();
389
+ const doubleTime = double();
390
+ if (sample >= 20) {
391
+ fastestSingle = Math.min(fastestSingle, singleTime);
392
+ fastestDouble = Math.min(fastestDouble, doubleTime);
393
+ }
394
+ }
395
+ void benchmarkSink;
396
+ const ratio = fastestDouble / fastestSingle;
391
397
  console.log(
392
398
  ` ${sections} sections, ${doubled.length} ranges over ${gaps.length}: ${ratio.toFixed(3)}`,
393
399
  );
@@ -227,17 +227,17 @@ without a target of its own.
227
227
 
228
228
  ## Verification map
229
229
 
230
- | Contract | Verification | Representative states | Mutation or failure expectation |
231
- | ------------------ | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
232
- | FR1, FR6 | `useTableColumnResize.test.tsx` boundary-presence cases plus hover-capability and coarse-primary cases | Resizable, opted-out, and generated columns; hover-capable, coarse-primary with keyboard, and coarse-only inputs | A boundary appears on a non-resizable column, disappears from a resizable one, the hover reveal fires where no pointer can hover, or the control is absent where an input can operate it. |
233
- | FR2 | Browser measurement of scroll-region vertical overflow with the plugin enabled | Short and tall tables, wrapped and single-line headers, dense and default | The indicator ends below the last row or the scroll region gains vertical scroll that the same table lacks without resize. |
234
- | FR3-FR5, FR9, ORD1 | New focused tests for resting, header-hovered, pointer-on-boundary, focused, and dragging states | Rest, header hover, hover plus pointer on one boundary, keyboard focus with no pointer in the row, drag | A resting header draws boundaries, header hover misses a boundary, the pointed/focused boundary fails to outrank the hint, or focus alone draws a neighbor. |
235
- | FR10, ORD0 | Scroll-gate test driving the scroll region with a stationary pointer over the header | Pointer resting over a header while the region scrolls and after it settles | Boundaries reveal mid-scroll, an existing hint survives the scroll, or disclosure fails to resume once scrolling stops. |
236
- | FR7 | Snapshot or assertion that header hover changes no other header or row treatment | Header hover over a sortable and a selectable header | Header hover changes background, label, cursor, width, or another control's state. |
237
- | FR8, AR1-AR3, AR6 | Existing keyboard and ARIA suites, a coarse-primary keyboard case, plus assistive-technology spot check | Tab to boundary, arrow resize, screen-reader navigation of the header, coarse-primary device with a keyboard | The boundary leaves tab order on any input that can operate it, loses its name or values, or the hint reaches the accessibility tree. |
238
- | PR1, PR2 | Render-count assertion around header pointer enter/leave and during a scroll; listener and observer inspection | Pointer enters and leaves the header repeatedly; the region scrolls under a resting pointer | Disclosure rerenders the Table, the scroll gate sets React state, more than one scroll listener or any timer is added, or the extent measurement observes what it resizes. |
239
- | Plugin composition | Test mounting the resize and sticky-columns plugins together in both insertion orders | Pinned start column with resize enabled, before and after a drag | A pinned column loses `position: sticky` in either order, or the composition depends on caller insertion order. |
240
- | Record shape | `scripts/check-knowledge.mjs` | Colocated module record, canonical id and filename, parent backlink | A missing, misnamed, mis-parented, or unlinked record passes validation. |
230
+ | Contract | Verification | Representative states | Mutation or failure expectation |
231
+ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
232
+ | FR1, FR6 | `useTableColumnResize.test.tsx` boundary-presence cases plus hover-capability and coarse-primary cases | Resizable, opted-out, and generated columns; hover-capable, coarse-primary with keyboard, and coarse-only inputs | A boundary appears on a non-resizable column, disappears from a resizable one, the hover reveal fires where no pointer can hover, or the control is absent where an input can operate it. |
233
+ | FR2 | Browser measurement of scroll-region vertical overflow with the plugin enabled | Short and tall tables, wrapped and single-line headers, dense and default | The indicator ends below the last row or the scroll region gains vertical scroll that the same table lacks without resize. |
234
+ | FR3-FR5, FR9, ORD1 | New focused tests for resting, header-hovered, pointer-on-boundary, focused, and dragging states | Rest, header hover, hover plus pointer on one boundary, keyboard focus with no pointer in the row, drag | A resting header draws boundaries, header hover misses a boundary, the pointed/focused boundary fails to outrank the hint, or focus alone draws a neighbor. |
235
+ | FR10, ORD0 | Scroll-gate test driving the scroll region with a stationary pointer over the header | Pointer resting over a header while the region scrolls and after it settles | Boundaries reveal mid-scroll, an existing hint survives the scroll, or disclosure fails to resume once scrolling stops. |
236
+ | FR7 | Snapshot or assertion that header hover changes no other header or row treatment | Header hover over a sortable and a selectable header | Header hover changes background, label, cursor, width, or another control's state. |
237
+ | FR8, AR1-AR3, AR6 | Existing keyboard and ARIA suites and a coarse-primary keyboard case; an assistive-technology spot check is recommended follow-up | Tab to boundary, arrow resize, screen-reader navigation of the header, coarse-primary device with a keyboard | The boundary leaves tab order on any input that can operate it, loses its name or values, or the hint reaches the accessibility tree. |
238
+ | PR1, PR2 | Render-count assertion around header pointer enter/leave and during a scroll; listener and observer inspection | Pointer enters and leaves the header repeatedly; the region scrolls under a resting pointer | Disclosure rerenders the Table, the scroll gate sets React state, more than one scroll listener or any timer is added, or the extent measurement observes what it resizes. |
239
+ | Plugin composition | Test mounting the resize and sticky-columns plugins together in both insertion orders | Pinned start column with resize enabled, before and after a drag | A pinned column loses `position: sticky` in either order, or the composition depends on caller insertion order. |
240
+ | Record shape | `scripts/check-knowledge.mjs` | Colocated module record, canonical id and filename, parent backlink | A missing, misnamed, mis-parented, or unlinked record passes validation. |
241
241
 
242
242
  Browser, render-count, and assistive-technology evidence are pending. Passing this
243
243
  specification pull request proves record integrity only.
@@ -214,8 +214,9 @@ add semantic/custom cases to representative Table render and no-op update budget
214
214
  - **AR5 — Supplemental tooltip.** Tooltip may repeat `label` as pointer help, but
215
215
  the indicator's accessible name MUST remain sufficient without opening it. The
216
216
  current indicator is not focusable, so tooltip presence is not keyboard evidence.
217
- - **AR6 — Row context.** Assistive-technology verification MUST confirm the named
218
- status is encountered in its row context without duplicate announcements.
217
+ - **AR6 — Row context.** The named status is exposed in its row context without a
218
+ duplicate name, proved at the accessibility tree. Assistive-technology
219
+ observation of the announcement is recommended follow-up under `spec:AST-009`.
219
220
 
220
221
  ## Design relationships
221
222
 
@@ -259,7 +260,7 @@ own AST-002 review and synchronized consumer anatomy.
259
260
  | FR3, FR4, FR6 | Add public-subpath compile fixtures for the unchanged interface, declaration merging and extension, semantic interface, callback union, missing labels, unknown statuses, and forbidden mixtures. | Stable custom annotations/extensions; all semantic statuses. | `TableRowStatus` changes or stops merging, semantic usage is inaccessible, or supported status mixes with custom fields. |
260
261
  | FR3-FR5 | Extend row-status runtime tests with semantic-looking custom colors, raw/palette dots, explicit custom icons, all semantic statuses, and active-theme substitution. | Custom dot, custom icon, and three semantic outcomes. | Custom color selects an implicit glyph, semantic status bypasses shared resolution, or variant becomes caller/theme input. |
261
262
  | FR7, ORD2-ORD4, PR4 | Runtime tests under development and production modes with repeated mixed objects, rows, and renders. | `status + color`, `status + icon`, and all three fields. | Custom input overrides supported status, forbidden fields affect output, production warns, or development warns more than once per loaded module instance. |
262
- | AR1-AR6 | Existing role/name tests plus browser axe, forced-colors, tooltip modality, and VoiceOver table-navigation evidence. | Semantic glyphs, custom icon, custom dot, `null`, light/dark, and every shipped theme. | Header or indicator loses its name, nested glyph is announced twice, glyph distinctions disappear, tooltip becomes sole meaning, or row context duplicates status. |
263
+ | AR1-AR6 | Existing role/name tests plus browser axe, forced-colors, tooltip modality, and accessibility-tree row-context evidence; VoiceOver table navigation is recommended follow-up. | Semantic glyphs, custom icon, custom dot, `null`, light/dark, and every shipped theme. | Header or indicator loses its name, nested glyph is announced twice, glyph distinctions disappear, tooltip becomes sole meaning, or row context duplicates status. |
263
264
  | PR1-PR3 | Extend `Table.perf.test.tsx` with semantic/custom initial render and no-op update cases. | Stable rerender, mixed rows, and theme substitution. | Resolution adds measurement/resources, creates a parallel registry, or exceeds existing representative Table budgets. |
264
265
  | Structural ownership | `scripts/check-knowledge.mjs`. | Colocated module, canonical filename/id, and parent backlink. | A missing, duplicate, orphaned, mis-parented, misnamed, or undiscovered record passes validation. |
265
266
  | Public hygiene and scope | Prettier, `check:knowledge`, checker tests, `check:repo`, changed-file scan, and public-content scan. | Parent backlink plus one module specification; no Changeset. | Runtime/docs files, private context, invalid record shape, or unrelated changes enter this contract proposal. |