@astryxdesign/core 0.7.1-canary.3235ec9 → 0.7.1-canary.366aafa
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/dist/Divider/Divider.d.ts +1 -0
- package/dist/Divider/Divider.d.ts.map +1 -1
- package/dist/Divider/Divider.js +5 -4
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +7 -5
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +12 -5
- package/package.json +3 -3
- package/src/Chat/ChatComposerInput.test.tsx +4 -0
- package/src/Chat/ChatSystemMessage.spec.md +36 -37
- package/src/Chat/ChatSystemMessage.test.tsx +4 -4
- package/src/Divider/Divider.doc.mjs +5 -3
- package/src/Divider/Divider.spec.md +140 -54
- package/src/Divider/Divider.test.tsx +15 -0
- package/src/Divider/Divider.tsx +6 -4
- package/src/DropdownMenu/DropdownMenu.spec.md +34 -17
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +38 -2
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +32 -18
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +146 -1
|
@@ -27,6 +27,7 @@ export interface DividerProps extends BaseProps<HTMLDivElement> {
|
|
|
27
27
|
orientation?: 'horizontal' | 'vertical';
|
|
28
28
|
/**
|
|
29
29
|
* Optional label to display centered on the divider.
|
|
30
|
+
* Numeric zero is preserved; null, booleans, and empty strings render no label.
|
|
30
31
|
* Rendered with small, secondary text styling.
|
|
31
32
|
*/
|
|
32
33
|
label?: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../../src/Divider/Divider.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAQ,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAW5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,YAAa,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC7D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IAExC
|
|
1
|
+
{"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../../src/Divider/Divider.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAQ,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAW5C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,SAAS,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,YAAa,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC7D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IAExC;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IAEzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAiED;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,EACtB,WAA0B,EAC1B,KAAK,EACL,OAAkB,EAClB,WAAmB,EACnB,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EACjC,GAAG,KAAK,EACT,EAAE,YAAY,+BA2Dd;yBAvEe,OAAO"}
|
package/dist/Divider/Divider.js
CHANGED
|
@@ -18,7 +18,7 @@ import { useId } from 'react';
|
|
|
18
18
|
import * as stylex from '@stylexjs/stylex';
|
|
19
19
|
import "../theme/tokens.stylex.js";
|
|
20
20
|
import { colorVars, spacingVars, typeScaleVars, borderVars } from "../theme/tokens.stylex.js";
|
|
21
|
-
import { mergeProps } from "../utils/index.js";
|
|
21
|
+
import { isRenderable, mergeProps } from "../utils/index.js";
|
|
22
22
|
import { themeProps } from "../utils/themeProps.js";
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -102,13 +102,14 @@ export function Divider({
|
|
|
102
102
|
...props
|
|
103
103
|
}) {
|
|
104
104
|
const isHorizontal = orientation === 'horizontal';
|
|
105
|
+
const hasLabel = isRenderable(label);
|
|
105
106
|
const labelId = useId();
|
|
106
107
|
|
|
107
108
|
// A separator does not derive its accessible name from its content, so the
|
|
108
109
|
// rendered label must be referenced explicitly via aria-labelledby to be
|
|
109
110
|
// exposed as the separator's name (WCAG 1.3.1). An explicit aria-label or
|
|
110
111
|
// aria-labelledby from the consumer takes precedence.
|
|
111
|
-
const resolvedLabelledBy = ariaLabelledBy ?? (
|
|
112
|
+
const resolvedLabelledBy = ariaLabelledBy ?? (hasLabel && ariaLabel == null ? labelId : undefined);
|
|
112
113
|
return /*#__PURE__*/_jsxs("div", {
|
|
113
114
|
ref: ref,
|
|
114
115
|
...props,
|
|
@@ -122,7 +123,7 @@ export function Divider({
|
|
|
122
123
|
}), stylex.props(isHorizontal ? baseStyles.horizontal : baseStyles.vertical, isFullBleed && (isHorizontal ? fullBleedStyles.horizontal : fullBleedStyles.vertical), xstyle), className, style),
|
|
123
124
|
children: [/*#__PURE__*/_jsx("div", {
|
|
124
125
|
...stylex.props(isHorizontal ? lineStyles.horizontalLine : lineStyles.verticalLine, lineStyles[variant])
|
|
125
|
-
}),
|
|
126
|
+
}), hasLabel && /*#__PURE__*/_jsx("div", {
|
|
126
127
|
id: labelId,
|
|
127
128
|
...{
|
|
128
129
|
0: {
|
|
@@ -133,7 +134,7 @@ export function Divider({
|
|
|
133
134
|
}
|
|
134
135
|
}[!!!isHorizontal << 0],
|
|
135
136
|
children: label
|
|
136
|
-
}),
|
|
137
|
+
}), hasLabel && /*#__PURE__*/_jsx("div", {
|
|
137
138
|
...stylex.props(isHorizontal ? lineStyles.horizontalLine : lineStyles.verticalLine, lineStyles[variant])
|
|
138
139
|
})]
|
|
139
140
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file DropdownMenuCheckboxItem.tsx
|
|
3
|
-
* @input React, stylex, Item +
|
|
3
|
+
* @input React, stylex, Item + Indicator registry + DropdownMenu context + tokens from core
|
|
4
4
|
* @output DropdownMenuCheckboxItem — a standalone checkable menu item.
|
|
5
5
|
* @position Sub-component; used inside DropdownMenu.
|
|
6
6
|
*
|
|
@@ -47,9 +47,8 @@ export interface DropdownMenuCheckboxItemProps extends Omit<BaseProps, 'onChange
|
|
|
47
47
|
*/
|
|
48
48
|
onChange?: (checked: boolean) => void;
|
|
49
49
|
/**
|
|
50
|
-
* Whether the item is disabled. Disabled items
|
|
51
|
-
*
|
|
52
|
-
* technology, but activation is blocked.
|
|
50
|
+
* Whether the item is disabled. Disabled items expose `aria-disabled`, block
|
|
51
|
+
* activation, and are currently skipped by DropdownMenu arrow-key navigation.
|
|
53
52
|
* @default false
|
|
54
53
|
*/
|
|
55
54
|
isDisabled?: boolean;
|
|
@@ -73,6 +72,9 @@ export interface DropdownMenuCheckboxItemProps extends Omit<BaseProps, 'onChange
|
|
|
73
72
|
*
|
|
74
73
|
* Must be used inside a DropdownMenu. Toggles an independent boolean; for a
|
|
75
74
|
* one-of-N choice use DropdownMenuRadioGroup + DropdownMenuRadioItem instead.
|
|
75
|
+
* Consumer click and pointer-move handlers compose after the row's own
|
|
76
|
+
* activation and hover-focus behavior; disabled activation stops before the
|
|
77
|
+
* consumer click handler.
|
|
76
78
|
*
|
|
77
79
|
* @example
|
|
78
80
|
* ```
|
|
@@ -86,7 +88,7 @@ export interface DropdownMenuCheckboxItemProps extends Omit<BaseProps, 'onChange
|
|
|
86
88
|
* </DropdownMenu>
|
|
87
89
|
* ```
|
|
88
90
|
*/
|
|
89
|
-
export declare function DropdownMenuCheckboxItem({ label, description, icon, value, onChange, isDisabled, hasCloseOnSelect, endContent, xstyle, className, style, ...rest }: DropdownMenuCheckboxItemProps): import("react").JSX.Element;
|
|
91
|
+
export declare function DropdownMenuCheckboxItem({ label, description, icon, value, onChange, isDisabled, hasCloseOnSelect, endContent, xstyle, className, style, onClick, onPointerMove, ...rest }: DropdownMenuCheckboxItemProps): import("react").JSX.Element;
|
|
90
92
|
export declare namespace DropdownMenuCheckboxItem {
|
|
91
93
|
var displayName: string;
|
|
92
94
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenuCheckboxItem.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuCheckboxItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"DropdownMenuCheckboxItem.d.ts","sourceRoot":"","sources":["../../src/DropdownMenu/DropdownMenuCheckboxItem.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAIL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAiB,KAAK,QAAQ,EAAC,MAAM,SAAS,CAAC;AAOtD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA4C5C,MAAM,WAAW,6BAA8B,SAAQ,IAAI,CACzD,SAAS,EACT,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,CAClD;IACC;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sFAAsF;IACtF,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,KAAK,EACL,WAAW,EACX,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,UAAkB,EAClB,gBAAwB,EACxB,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,OAAO,EACP,aAAa,EACb,GAAG,IAAI,EACR,EAAE,6BAA6B,+BA+D/B;yBA9Ee,wBAAwB"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file DropdownMenuCheckboxItem.tsx
|
|
7
|
-
* @input React, stylex, Item +
|
|
7
|
+
* @input React, stylex, Item + Indicator registry + DropdownMenu context + tokens from core
|
|
8
8
|
* @output DropdownMenuCheckboxItem — a standalone checkable menu item.
|
|
9
9
|
* @position Sub-component; used inside DropdownMenu.
|
|
10
10
|
*
|
|
@@ -34,7 +34,7 @@ import { useDropdownMenuContext } from "./DropdownMenuContext.js";
|
|
|
34
34
|
import { focusMenuItemOnHover } from "./menuItemHover.js";
|
|
35
35
|
import "../theme/tokens.stylex.js";
|
|
36
36
|
import { colorVars, spacingVars } from "../theme/tokens.stylex.js";
|
|
37
|
-
import { mergeProps, themeProps } from "../utils/index.js";
|
|
37
|
+
import { composeEventHandlers, mergeProps, themeProps } from "../utils/index.js";
|
|
38
38
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
39
39
|
const styles = {
|
|
40
40
|
root: {
|
|
@@ -67,6 +67,9 @@ const styles = {
|
|
|
67
67
|
*
|
|
68
68
|
* Must be used inside a DropdownMenu. Toggles an independent boolean; for a
|
|
69
69
|
* one-of-N choice use DropdownMenuRadioGroup + DropdownMenuRadioItem instead.
|
|
70
|
+
* Consumer click and pointer-move handlers compose after the row's own
|
|
71
|
+
* activation and hover-focus behavior; disabled activation stops before the
|
|
72
|
+
* consumer click handler.
|
|
70
73
|
*
|
|
71
74
|
* @example
|
|
72
75
|
* ```
|
|
@@ -92,28 +95,32 @@ export function DropdownMenuCheckboxItem({
|
|
|
92
95
|
xstyle,
|
|
93
96
|
className,
|
|
94
97
|
style,
|
|
98
|
+
onClick,
|
|
99
|
+
onPointerMove,
|
|
95
100
|
...rest
|
|
96
101
|
}) {
|
|
97
102
|
const ctx = useDropdownMenuContext();
|
|
98
103
|
const menuSize = ctx?.menuSize ?? 'md';
|
|
99
104
|
const controlSize = menuSize === 'sm' ? 'sm' : 'md';
|
|
100
105
|
const CheckboxControl = useIndicator('checkbox');
|
|
101
|
-
const handleClick = useCallback(
|
|
106
|
+
const handleClick = useCallback(event => {
|
|
102
107
|
if (isDisabled) {
|
|
108
|
+
event.preventDefault();
|
|
103
109
|
return;
|
|
104
110
|
}
|
|
105
111
|
onChange?.(!value);
|
|
106
112
|
if (hasCloseOnSelect) {
|
|
107
113
|
ctx?.closeMenu();
|
|
108
114
|
}
|
|
109
|
-
|
|
115
|
+
onClick?.(event);
|
|
116
|
+
}, [isDisabled, onChange, value, hasCloseOnSelect, ctx, onClick]);
|
|
110
117
|
const handlePointerMove = useCallback(e => focusMenuItemOnHover(e, isDisabled), [isDisabled]);
|
|
111
118
|
return /*#__PURE__*/_jsx(Item, {
|
|
112
119
|
...rest,
|
|
113
120
|
role: "menuitemcheckbox",
|
|
114
121
|
"aria-checked": value,
|
|
115
122
|
tabIndex: isDisabled ? undefined : -1,
|
|
116
|
-
onPointerMove: handlePointerMove,
|
|
123
|
+
onPointerMove: composeEventHandlers(handlePointerMove, onPointerMove),
|
|
117
124
|
marker:
|
|
118
125
|
/*#__PURE__*/
|
|
119
126
|
// No wrapper, and no menu-specific theme target: the shared
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.7.1-canary.
|
|
3
|
+
"version": "0.7.1-canary.366aafa",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -787,8 +787,8 @@
|
|
|
787
787
|
"react-dom": ">=19.0.0"
|
|
788
788
|
},
|
|
789
789
|
"devDependencies": {
|
|
790
|
-
"@astryxdesign/a11y-spec": "0.7.1-canary.
|
|
791
|
-
"@astryxdesign/cli": "0.7.1-canary.
|
|
790
|
+
"@astryxdesign/a11y-spec": "0.7.1-canary.366aafa",
|
|
791
|
+
"@astryxdesign/cli": "0.7.1-canary.366aafa",
|
|
792
792
|
"@babel/cli": "^7.29.7",
|
|
793
793
|
"@babel/core": "^7.29.7",
|
|
794
794
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -1556,6 +1556,10 @@ describe('ChatComposerInput', () => {
|
|
|
1556
1556
|
await waitFor(() => {
|
|
1557
1557
|
expect(editable).toHaveAttribute('aria-expanded', 'false');
|
|
1558
1558
|
});
|
|
1559
|
+
// The hidden menu's results commit outside act, so the popover's Escape
|
|
1560
|
+
// registration is released by a passive effect that can still be pending
|
|
1561
|
+
// when the attribute changes. Flush it before pressing Escape.
|
|
1562
|
+
await act(async () => {});
|
|
1559
1563
|
|
|
1560
1564
|
// Nothing cancels the key: the closed menu holds no Escape handling,
|
|
1561
1565
|
// so a surrounding dialog or drawer still receives it.
|
|
@@ -80,7 +80,7 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
80
80
|
- Sender identity, avatar, bubble, transcript ordering, or scrolling.
|
|
81
81
|
- Divider rule paint, label placement, separator role, or separator naming, which are delegated to `component:Divider`.
|
|
82
82
|
- Long Divider-label overflow and forced-colors rule paint, which remain shared `component:Divider` advisories rather than ChatSystemMessage-owned defects.
|
|
83
|
-
- Divider's
|
|
83
|
+
- Divider's skipped-label rendering. Empty strings and empty Fragments leave the separator without a visible label or accessible name; numeric zero renders one visible `0` and names the separator `0`. These partitions remain owned by `component:Divider`.
|
|
84
84
|
- The semantic or directional meaning of caller-provided content or icon artwork.
|
|
85
85
|
- Announcement timing or spoken output; claims at that layer require `spec:AST-009` evidence.
|
|
86
86
|
|
|
@@ -99,14 +99,14 @@ observable public surface without deciding the unresolved divider/icon contract.
|
|
|
99
99
|
|
|
100
100
|
## Behavioral and layout contract
|
|
101
101
|
|
|
102
|
-
| ID | Candidate invariant
|
|
103
|
-
| --- |
|
|
104
|
-
| FR1 | The default branch MUST render the required content in a centered, noninteractive status row, allow long content to wrap within the available inline space, and preserve optional icon geometry.
|
|
105
|
-
| FR2 |
|
|
106
|
-
| FR3 | The divider branch currently ignores `icon`; this draft MUST NOT convert that observation into a stable restriction, a requirement to render the icon, or a warning policy.
|
|
107
|
-
| FR4 | Supported root DOM, data, ARIA, className, style, xstyle, and ref inputs MUST reach or compose on the root while component-owned `role="status"` and reflected `variant` remain intact.
|
|
108
|
-
| FR5 | The component MUST use semantic color, spacing, and typography tokens and logical layout; the `variant` state remains reflected on the single `chat-system-message` target.
|
|
109
|
-
| FR6 | The component remains render-only: props determine output without Effects, state mirrors, observers, listeners, timers, or owned async resources.
|
|
102
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
103
|
+
| --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
104
|
+
| FR1 | The default branch MUST render the required content in a centered, noninteractive status row, allow long content to wrap within the available inline space, and preserve optional icon geometry. | Current source, released docs, tests, and examples | Verified current behavior |
|
|
105
|
+
| FR2 | The divider branch MUST render renderable content through Divider as a labelled horizontal separator inside the component's status root. Skipped content follows Divider's delegated label contract. | Current source, released docs, Divider implementation, tests, and examples | Verified current behavior; delegated Divider partitions |
|
|
106
|
+
| FR3 | The divider branch currently ignores `icon`; this draft MUST NOT convert that observation into a stable restriction, a requirement to render the icon, or a warning policy. | `architecture:public-component-api/INV3`, `architecture:public-component-api/INV5`, and `spec:AST-002/FR15` | Human API decision required; see OQ1 |
|
|
107
|
+
| FR4 | Supported root DOM, data, ARIA, className, style, xstyle, and ref inputs MUST reach or compose on the root while component-owned `role="status"` and reflected `variant` remain intact. | Current source plus `architecture:public-component-api/INV5-INV8` | Verified current behavior and existing focused evidence |
|
|
108
|
+
| FR5 | The component MUST use semantic color, spacing, and typography tokens and logical layout; the `variant` state remains reflected on the single `chat-system-message` target. | Current source, `architecture:theme-tokens`, and theming architecture | Verified current behavior |
|
|
109
|
+
| FR6 | The component remains render-only: props determine output without Effects, state mirrors, observers, listeners, timers, or owned async resources. | Current source and `architecture:react-component-runtime/INV1-INV8` | Verified current behavior |
|
|
110
110
|
|
|
111
111
|
### Allowed variation
|
|
112
112
|
|
|
@@ -116,9 +116,8 @@ observable public surface without deciding the unresolved divider/icon contract.
|
|
|
116
116
|
content unchanged: empty string and empty Fragment content remain empty, while
|
|
117
117
|
numeric zero renders one `0`. In the divider variant, the status root and separator
|
|
118
118
|
remain; empty string and empty Fragment content produce no visible label or
|
|
119
|
-
accessible name, while numeric zero
|
|
120
|
-
|
|
121
|
-
to `component:Divider`.
|
|
119
|
+
accessible name, while numeric zero renders one visible `0` and names the
|
|
120
|
+
separator `0`. Divider owns those label partitions.
|
|
122
121
|
- **AV2 - Caller styling.** Supported root styling inputs may extend the component
|
|
123
122
|
without replacing owned status semantics or variant reflection.
|
|
124
123
|
- **AV3 - Delegated Divider paint.** Divider owns its rule and label paint and may
|
|
@@ -126,15 +125,15 @@ observable public surface without deciding the unresolved divider/icon contract.
|
|
|
126
125
|
|
|
127
126
|
### Representative states
|
|
128
127
|
|
|
129
|
-
| State | Required invariant
|
|
130
|
-
| --------------------- |
|
|
131
|
-
| Default | Centered status content is present.
|
|
132
|
-
| Default with icon | Optional icon content and visible content render together.
|
|
133
|
-
| Default long content | Content wraps without crossing either inline edge or creating page overflow.
|
|
134
|
-
| Divider | A labelled horizontal separator presents the supplied content.
|
|
135
|
-
| Divider with icon | Current output is measured without deciding the ignored-input contract.
|
|
136
|
-
| Empty ReactNode | The default variant keeps `role="status"` and renders content unchanged (empty or one `0`). In the divider variant, the status root and separator remain; empty content has no label/name, while numeric zero
|
|
137
|
-
| Narrow/coarse pointer | The noninteractive row remains visible without horizontal page overflow.
|
|
128
|
+
| State | Required invariant | Allowed variation |
|
|
129
|
+
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
|
|
130
|
+
| Default | Centered status content is present. | Content, theme, and root styling. |
|
|
131
|
+
| Default with icon | Optional icon content and visible content render together. | Caller-provided artwork and content. |
|
|
132
|
+
| Default long content | Content wraps without crossing either inline edge or creating page overflow. | Theme and localized content. |
|
|
133
|
+
| Divider | A labelled horizontal separator presents the supplied content. | Label content, theme, and root styling. |
|
|
134
|
+
| Divider with icon | Current output is measured without deciding the ignored-input contract. | No behavior change until OQ1 is resolved. |
|
|
135
|
+
| Empty ReactNode | The default variant keeps `role="status"` and renders content unchanged (empty or one `0`). In the divider variant, the status root and separator remain; empty content has no label/name, while numeric zero renders and names one `0`. | Empty string, numeric zero, empty Fragment. |
|
|
136
|
+
| Narrow/coarse pointer | The noninteractive row remains visible without horizontal page overflow. | Theme and supported content. |
|
|
138
137
|
|
|
139
138
|
### Transformation and precedence order
|
|
140
139
|
|
|
@@ -156,10 +155,10 @@ observable public surface without deciding the unresolved divider/icon contract.
|
|
|
156
155
|
- **AR1 - Status exposure.** The root currently exposes `role="status"` for both
|
|
157
156
|
variants. Browser evidence may prove role exposure; spoken announcement timing is
|
|
158
157
|
outside this draft and requires `spec:AST-009` evidence.
|
|
159
|
-
- **AR2 - Divider relationship.**
|
|
158
|
+
- **AR2 - Divider relationship.** Renderable content in the divider branch delegates a
|
|
160
159
|
horizontal `separator` whose labelled target contains the visible label; focused DOM
|
|
161
|
-
tests verify the computed accessible name.
|
|
162
|
-
|
|
160
|
+
tests verify the computed accessible name. Skipped content follows Divider's
|
|
161
|
+
delegated label contract.
|
|
163
162
|
- **AR3 - Caller content.** Visible content remains the self-contained message.
|
|
164
163
|
Caller-provided icon content does not replace that text.
|
|
165
164
|
- **AR4 - Noninteractive surface.** The component introduces no focus target,
|
|
@@ -180,8 +179,8 @@ icon artwork, live-region timing, or divider/icon behavior.
|
|
|
180
179
|
## Family and system relationships
|
|
181
180
|
|
|
182
181
|
- `component:Divider` owns the delegated separator structure, role, name,
|
|
183
|
-
paint, long-label overflow, and
|
|
184
|
-
|
|
182
|
+
paint, long-label overflow, and label partitions. Numeric zero renders as one
|
|
183
|
+
visible label and names the separator `0`; skipped content remains unlabelled.
|
|
185
184
|
- `architecture:public-component-api` owns released exports, BaseProps reachability,
|
|
186
185
|
styling composition, and the ref contract.
|
|
187
186
|
- `architecture:component-theming-surface` owns the target/anatomy mapping and
|
|
@@ -197,17 +196,17 @@ icon artwork, live-region timing, or divider/icon behavior.
|
|
|
197
196
|
|
|
198
197
|
## Verification map
|
|
199
198
|
|
|
200
|
-
| Contract | Verification | Representative states | Mutation or failure expectation
|
|
201
|
-
| ------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
202
|
-
| FR1, AR1, AR3 | `ChatSystemMessage.test.tsx`, scoped axe, and exact-head Chromium receipts | Default, default with icon, long content, nested live log, neutral light/dark | Missing content, status role, icon content, wrapping, or browser-visible subject fails focused or browser evidence.
|
|
203
|
-
| FR2, AR2 | `ChatSystemMessage.test.tsx`, Divider tests, scoped axe, and exact-head Chromium receipts |
|
|
204
|
-
| FR3 | Closed audit inventory and exact-head observation of the divider/icon partition | Divider with supplied icon | The audit must expose, not silently omit, the unresolved branch; implementation remains unchanged until owner decision.
|
|
205
|
-
| FR4 | Mutation-sensitive root-passthrough tests, source inspection, type checks, and target reflection | Default and divider roots | Removing ref, className, style, xstyle, ARIA forwarding, or owned role precedence fails focused tests.
|
|
206
|
-
| FR5 | `themingTargets.test.ts`, source inspection, generated build, and exact-head computed paint | Both variants and color modes | Target/state metadata drift, raw styling, physical layout, or unresolved mode paint fails checks or browser evidence.
|
|
207
|
-
| FR6 | Source inspection, strict lint, and component test suite | Every render | Adding hidden state or an owned external resource triggers runtime review and corresponding evidence.
|
|
208
|
-
| RTL relation | Source-hashed verified-N/A record | Both variants and caller-owned icon content | Directional component-owned content, physical positioning, ordering, scroll/drag, overlay, or horizontal keyboard behavior invalidates the record.
|
|
209
|
-
| Browser matrix | `ChatSystemMessage.a11y.chromium.spec.ts` exact-head 10-sensor receipts | Default, icon, wrapped long content, divider, unresolved divider/icon, nested live log, 320px/coarse pointer, light/dark, D7 pairs | Stale build, wrong story, incorrect mode, missing semantics, default overflow, failed contrast, blank crop, or page error fails closed.
|
|
210
|
-
| Documentation and surface | Consumer docs, published declarations, block metadata, explicit story ownership, exports, and `check:knowledge` | Props, `./Chat` subpath, four blocks, draft record | Missing or stale public docs, export, owner route, target map, or knowledge shape fails validation or review.
|
|
199
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
200
|
+
| ------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
201
|
+
| FR1, AR1, AR3 | `ChatSystemMessage.test.tsx`, scoped axe, and exact-head Chromium receipts | Default, default with icon, long content, nested live log, neutral light/dark | Missing content, status role, icon content, wrapping, or browser-visible subject fails focused or browser evidence. | `audit:ChatSystemMessage/accessibility` |
|
|
202
|
+
| FR2, AR2 | `ChatSystemMessage.test.tsx`, Divider tests, scoped axe, and exact-head Chromium receipts | Labelled divider content plus empty string, numeric zero, and empty Fragment observations | Lost separator role, label association, orientation, content, or paint fails focused or browser evidence; skipped partitions stay delegated to Divider. | `audit:ChatSystemMessage/behavior` |
|
|
203
|
+
| FR3 | Closed audit inventory and exact-head observation of the divider/icon partition | Divider with supplied icon | The audit must expose, not silently omit, the unresolved branch; implementation remains unchanged until owner decision. | `audit:ChatSystemMessage/public-api` |
|
|
204
|
+
| FR4 | Mutation-sensitive root-passthrough tests, source inspection, type checks, and target reflection | Default and divider roots | Removing ref, className, style, xstyle, ARIA forwarding, or owned role precedence fails focused tests. | `audit:ChatSystemMessage/public-api` |
|
|
205
|
+
| FR5 | `themingTargets.test.ts`, source inspection, generated build, and exact-head computed paint | Both variants and color modes | Target/state metadata drift, raw styling, physical layout, or unresolved mode paint fails checks or browser evidence. | `audit:ChatSystemMessage/theming` |
|
|
206
|
+
| FR6 | Source inspection, strict lint, and component test suite | Every render | Adding hidden state or an owned external resource triggers runtime review and corresponding evidence. | `audit:ChatSystemMessage/code-health` |
|
|
207
|
+
| RTL relation | Source-hashed verified-N/A record | Both variants and caller-owned icon content | Directional component-owned content, physical positioning, ordering, scroll/drag, overlay, or horizontal keyboard behavior invalidates the record. | `audit:ChatSystemMessage/i18n-rtl` |
|
|
208
|
+
| Browser matrix | `ChatSystemMessage.a11y.chromium.spec.ts` exact-head 10-sensor receipts | Default, icon, wrapped long content, divider, unresolved divider/icon, nested live log, 320px/coarse pointer, light/dark, D7 pairs | Stale build, wrong story, incorrect mode, missing semantics, default overflow, failed contrast, blank crop, or page error fails closed. | `audit:ChatSystemMessage/visual` |
|
|
209
|
+
| Documentation and surface | Consumer docs, published declarations, block metadata, explicit story ownership, exports, and `check:knowledge` | Props, `./Chat` subpath, four blocks, draft record | Missing or stale public docs, export, owner route, target map, or knowledge shape fails validation or review. | `audit:ChatSystemMessage/docs` |
|
|
211
210
|
|
|
212
211
|
## Decision log
|
|
213
212
|
|
|
@@ -152,7 +152,7 @@ describe('ChatSystemMessage', () => {
|
|
|
152
152
|
expect(root).toContainElement(screen.getByRole('separator'));
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
-
it('
|
|
155
|
+
it('keeps the numeric-zero label visible and names the shared divider', () => {
|
|
156
156
|
render(
|
|
157
157
|
<ChatSystemMessage variant="divider" data-testid="divider-zero">
|
|
158
158
|
{0}
|
|
@@ -160,8 +160,8 @@ describe('ChatSystemMessage', () => {
|
|
|
160
160
|
);
|
|
161
161
|
const root = screen.getByTestId('divider-zero');
|
|
162
162
|
const separator = screen.getByRole('separator');
|
|
163
|
-
expect(root).toHaveTextContent(
|
|
164
|
-
expect(separator).
|
|
165
|
-
expect(separator).
|
|
163
|
+
expect(root).toHaveTextContent(/^0$/);
|
|
164
|
+
expect(separator).toHaveAccessibleName('0');
|
|
165
|
+
expect(separator).toHaveAttribute('aria-labelledby');
|
|
166
166
|
});
|
|
167
167
|
});
|
|
@@ -40,7 +40,8 @@ export const docs = {
|
|
|
40
40
|
{
|
|
41
41
|
name: 'label',
|
|
42
42
|
type: 'ReactNode',
|
|
43
|
-
description:
|
|
43
|
+
description:
|
|
44
|
+
'Optional label centered on the divider. Numeric zero is preserved; null, booleans, and empty strings render no label.',
|
|
44
45
|
},
|
|
45
46
|
{
|
|
46
47
|
name: 'variant',
|
|
@@ -92,7 +93,8 @@ export const docsZh = {
|
|
|
92
93
|
{
|
|
93
94
|
name: 'label',
|
|
94
95
|
type: 'ReactNode',
|
|
95
|
-
description:
|
|
96
|
+
description:
|
|
97
|
+
'居中显示在分隔线上的可选标签。数字 0 会保留;null、布尔值和空字符串不渲染标签。',
|
|
96
98
|
},
|
|
97
99
|
{
|
|
98
100
|
name: 'variant',
|
|
@@ -143,7 +145,7 @@ export const docsDense = {
|
|
|
143
145
|
},
|
|
144
146
|
propDescriptions: {
|
|
145
147
|
orientation: 'divider orientation',
|
|
146
|
-
label: 'optional centered label
|
|
148
|
+
label: 'optional centered label; preserves 0; null/boolean/"" omit it',
|
|
147
149
|
variant: 'visual weight of divider line',
|
|
148
150
|
isFullBleed: 'extend to container edges w/ negative margins',
|
|
149
151
|
xstyle: 'StyleX styles for layout; must be stylex.create() value',
|