@astryxdesign/core 0.7.1-canary.b32d73f → 0.7.1-canary.c4df922
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/Dialog/Dialog.d.ts +1 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +16 -5
- package/dist/Dialog/DialogHeader.d.ts +1 -1
- package/dist/Dialog/DialogHeader.d.ts.map +1 -1
- package/dist/Dialog/DialogHeader.js +7 -7
- package/package.json +3 -3
- package/src/CodeBlock/CodeBlock.spec.md +12 -12
- package/src/Dialog/Dialog.test.tsx +27 -1
- package/src/Dialog/Dialog.tsx +16 -5
- package/src/Dialog/DialogHeader.test.tsx +27 -2
- package/src/Dialog/DialogHeader.tsx +7 -8
package/dist/Dialog/Dialog.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file Dialog.tsx
|
|
3
3
|
* @input Uses React, DialogHTMLAttributes, ReactNode, container (Layout), DialogContext, layerTextReset,
|
|
4
|
-
* layerStructureReset, modalOutlet, interactionModality
|
|
4
|
+
* layerStructureReset, modalOutlet, interactionModality, chainEventHandlers
|
|
5
5
|
* @output Exports Dialog component, DialogProps, DialogVariant, DialogPurpose types
|
|
6
6
|
* @position Core implementation; consumed by index.ts, tested by Dialog.test.tsx
|
|
7
7
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/Dialog/Dialog.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA2B5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/Dialog/Dialog.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA2B5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAMhD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,SAAS,CAAC;AAyB9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzD,4EAA4E;AAC5E,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAe,SAAQ,mBAAmB;IACzD,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAuBD,2FAA2F;AAC3F,eAAO,MAAM,uCAAuC;;;;;;;;;;;CAW1C,CAAC;AAoJX;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,cAAc;;;;;EAUpE;AAED,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAC/D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;OAOG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAE3C;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC;IAEpC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;IAEtB;;;OAGG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EACN,QAAgB,EAChB,YAAY,EACZ,KAAW,EACX,SAAmB,EACnB,QAAQ,EACR,OAAoB,EACpB,OAAgB,EAChB,OAAO,EACP,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,WAAW,sCA2Ub;yBA3Ve,MAAM"}
|
package/dist/Dialog/Dialog.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Dialog.tsx
|
|
7
7
|
* @input Uses React, DialogHTMLAttributes, ReactNode, container (Layout), DialogContext, layerTextReset,
|
|
8
|
-
* layerStructureReset, modalOutlet, interactionModality
|
|
8
|
+
* layerStructureReset, modalOutlet, interactionModality, chainEventHandlers
|
|
9
9
|
* @output Exports Dialog component, DialogProps, DialogVariant, DialogPurpose types
|
|
10
10
|
* @position Core implementation; consumed by index.ts, tested by Dialog.test.tsx
|
|
11
11
|
*
|
|
@@ -41,6 +41,7 @@ import { colorVars, radiusVars, durationVars, easeVars, shadowVars, spacingVars
|
|
|
41
41
|
import { container } from "../Layout/container.stylex.js";
|
|
42
42
|
import { paddingStyles, containerPaddingInlineVarStyles, containerPaddingBlockStartVarStyles, containerPaddingBlockEndVarStyles, spacingStepToToken, overlayPaddingReset } from "../Layout/padding.stylex.js";
|
|
43
43
|
import { mergeProps } from "../utils/index.js";
|
|
44
|
+
import { chainEventHandlers } from "../utils/chainEventHandlers.js";
|
|
44
45
|
import { devWarn } from "../utils/devWarning.js";
|
|
45
46
|
import { DialogContext } from "./DialogContext.js";
|
|
46
47
|
import { themeProps } from "../utils/themeProps.js";
|
|
@@ -489,12 +490,22 @@ export function Dialog({
|
|
|
489
490
|
})
|
|
490
491
|
});
|
|
491
492
|
|
|
492
|
-
// Filter out native open to prevent InvalidStateError when accidentally passed
|
|
493
|
+
// Filter out native open to prevent InvalidStateError when accidentally passed.
|
|
494
|
+
// Keep the consumer click handler separate so the modal path can compose it
|
|
495
|
+
// with Dialog-owned behavior without changing the inline preview path.
|
|
493
496
|
const hasPosition = position != null && !isFullscreen;
|
|
497
|
+
const {
|
|
498
|
+
onClick: onClickProp,
|
|
499
|
+
...propsWithoutClick
|
|
500
|
+
} = props;
|
|
494
501
|
const {
|
|
495
502
|
open: _open,
|
|
496
503
|
...safeProps
|
|
497
|
-
} =
|
|
504
|
+
} = propsWithoutClick;
|
|
505
|
+
const inlineSafeProps = {
|
|
506
|
+
...safeProps,
|
|
507
|
+
onClick: onClickProp
|
|
508
|
+
};
|
|
498
509
|
|
|
499
510
|
// --- Inline rendering path (for documentation previews) ---
|
|
500
511
|
if (isInline) {
|
|
@@ -502,7 +513,7 @@ export function Dialog({
|
|
|
502
513
|
return null;
|
|
503
514
|
}
|
|
504
515
|
return /*#__PURE__*/_jsx("div", {
|
|
505
|
-
...
|
|
516
|
+
...inlineSafeProps,
|
|
506
517
|
...mergeProps(themeProps('dialog', {
|
|
507
518
|
variant
|
|
508
519
|
}), stylex.props(layerTextReset.reset, layerStructureReset.reset, styles.inlineWrapper, overlayPaddingReset.reset, standardSizing && dynamicStyles.sizing(standardSizing.width, standardSizing.maxWidth, standardSizing.maxHeight), isFullscreen && styles.fullscreen, xstyle), className, style),
|
|
@@ -526,7 +537,7 @@ export function Dialog({
|
|
|
526
537
|
const o = resolveDialogPositionOffsets(position);
|
|
527
538
|
return dynamicStyles.position(o.top, o.insetInlineStart, o.insetInlineEnd, o.bottom);
|
|
528
539
|
})(), isFullscreen && styles.fullscreen, isFullscreen && isOpen && styles.fullscreenOpen, xstyle), className, style),
|
|
529
|
-
onClick: handleClick,
|
|
540
|
+
onClick: chainEventHandlers(handleClick, onClickProp),
|
|
530
541
|
onCancel: handleCancel,
|
|
531
542
|
"aria-modal": "true"
|
|
532
543
|
// The default aria-labelledby (from a DialogHeader title) is set
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file DialogHeader.tsx
|
|
3
|
-
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, DialogContext, mergeProps, themeProps
|
|
3
|
+
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, DialogContext, isRenderable, mergeProps, themeProps
|
|
4
4
|
* @output Exports DialogHeader component and DialogHeaderProps
|
|
5
5
|
* @position Dialog header component; used with Dialog and Layout
|
|
6
6
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DialogHeader.d.ts","sourceRoot":"","sources":["../../src/Dialog/DialogHeader.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAQxD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA2C5C,MAAM,WAAW,iBAAkB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAClE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;;;;;OASG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAE5C;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IAEzB;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IAEvB;;;;;OAKG;IACH,0BAA0B,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IAExD;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,0BAA0B,EAC1B,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,iBAAiB,+
|
|
1
|
+
{"version":3,"file":"DialogHeader.d.ts","sourceRoot":"","sources":["../../src/Dialog/DialogHeader.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAQxD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AA2C5C,MAAM,WAAW,iBAAkB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAClE,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;;;;;;;;OASG;IACH,KAAK,EAAE,SAAS,CAAC;IAEjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAO,CAAC;IAE5C;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IAEzB;;OAEG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IAEvB;;;;;OAKG;IACH,0BAA0B,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;IAExD;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,0BAA0B,EAC1B,UAAU,EACV,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,IAAI,EACR,EAAE,iBAAiB,+BA0GnB;yBAvHe,YAAY"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file DialogHeader.tsx
|
|
7
|
-
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, DialogContext, mergeProps, themeProps
|
|
7
|
+
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, DialogContext, isRenderable, mergeProps, themeProps
|
|
8
8
|
* @output Exports DialogHeader component and DialogHeaderProps
|
|
9
9
|
* @position Dialog header component; used with Dialog and Layout
|
|
10
10
|
*
|
|
@@ -24,7 +24,7 @@ import { Button } from "../Button/index.js";
|
|
|
24
24
|
import { Icon } from "../Icon/index.js";
|
|
25
25
|
import { Heading } from "../Heading/Heading.js";
|
|
26
26
|
import { Text } from "../Text/Text.js";
|
|
27
|
-
import { mergeProps } from "../utils/index.js";
|
|
27
|
+
import { isRenderable, mergeProps } from "../utils/index.js";
|
|
28
28
|
import { themeProps } from "../utils/themeProps.js";
|
|
29
29
|
import { useDialogContext } from "./DialogContext.js";
|
|
30
30
|
import { useTranslator } from "../i18n/index.js";
|
|
@@ -76,9 +76,9 @@ export function DialogHeader({
|
|
|
76
76
|
const dialogContext = useDialogContext();
|
|
77
77
|
const shouldAutoFocus = dialogContext?.isInline !== true;
|
|
78
78
|
const titleId = dialogContext?.titleId;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
79
|
+
const hasSubtitle = isRenderable(subtitle);
|
|
80
|
+
const hasStartContent = isRenderable(startContent);
|
|
81
|
+
const hasEndContent = isRenderable(endContent);
|
|
82
82
|
const shouldCompensateEndBlock = endContentEdgeCompensation == null ? onOpenChange != null : endContentEdgeCompensation === 'block' || endContentEdgeCompensation === 'all';
|
|
83
83
|
const shouldCompensateEndInline = endContentEdgeCompensation == null ? onOpenChange != null : endContentEdgeCompensation === 'inline' || endContentEdgeCompensation === 'all';
|
|
84
84
|
|
|
@@ -103,7 +103,7 @@ export function DialogHeader({
|
|
|
103
103
|
...mergeProps(themeProps('dialog-header'), {
|
|
104
104
|
className: "x78zum5 x1cy8zhl x1qughib xjcht0a"
|
|
105
105
|
}),
|
|
106
|
-
children: [
|
|
106
|
+
children: [hasStartContent && /*#__PURE__*/_jsx("div", {
|
|
107
107
|
...mergeProps(themeProps('dialog-header-start-content'), {
|
|
108
108
|
className: "x78zum5 x6s0dn4 x1txdalj x2lah0s"
|
|
109
109
|
}),
|
|
@@ -125,7 +125,7 @@ export function DialogHeader({
|
|
|
125
125
|
color: "secondary",
|
|
126
126
|
children: subtitle
|
|
127
127
|
})]
|
|
128
|
-
}), (
|
|
128
|
+
}), (hasEndContent || onOpenChange != null) && /*#__PURE__*/_jsxs("div", {
|
|
129
129
|
...mergeProps(themeProps('dialog-header-end-content'), {
|
|
130
130
|
0: {
|
|
131
131
|
className: "x78zum5 x6s0dn4 x1txdalj x2lah0s"
|
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.c4df922",
|
|
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.c4df922",
|
|
791
|
+
"@astryxdesign/cli": "0.7.1-canary.c4df922",
|
|
792
792
|
"@babel/cli": "^7.29.7",
|
|
793
793
|
"@babel/core": "^7.29.7",
|
|
794
794
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -45,7 +45,7 @@ system_specs: [spec:AST-025]
|
|
|
45
45
|
| Behavior | CodeBlock renders a read-only multi-line code surface, applies syntax highlighting when available, preserves the source string for copy, and exposes optional line, header, copy, theme, and disclosure behavior without editing the content. |
|
|
46
46
|
| End-user impact | Readers can inspect and copy code, understand optional line emphasis, and operate long or collapsible examples with keyboard and assistive technology. |
|
|
47
47
|
| Builder impact | Builders choose the released display axes without an additional migration or opt-in; the diff layer is one more opt-in axis, and five defaulted syntax-theme slots mean no syntax theme has to change for it. |
|
|
48
|
-
| Compatibility/readiness | The released `CodeBlock` API and defaults remain stable; FR9 (the diff layer) is current and
|
|
48
|
+
| Compatibility/readiness | The released `CodeBlock` API and defaults remain stable; FR9 (the diff layer) is current and shipped; Markdown's default path for `diff` fences follows it (`component:Markdown` FR29). |
|
|
49
49
|
| Review checks | Reject lost code text, copy of a transformed string, an unnamed copy or disclosure control, stale disclosure state/wiring, dropped root props/ref, a theme target off its painter, or viewport focusability that ignores effective overflow. |
|
|
50
50
|
| Governing rules | `architecture:public-component-api/INV1,INV5–INV9`; `architecture:component-theming-surface/INV3–INV7,INV12`; `architecture:component-test-sufficiency/INV1–INV7,INV10–INV11`; `spec:AST-025/FR12–FR13`; `architecture:theme-tokens` (syntax vocabulary and the diff slot defaults). |
|
|
51
51
|
|
|
@@ -93,17 +93,17 @@ Consumer migration instructions belong in consumer docs and release notes.
|
|
|
93
93
|
|
|
94
94
|
## Behavioral and layout contract
|
|
95
95
|
|
|
96
|
-
| ID | Candidate invariant | Basis | Draft review state
|
|
97
|
-
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
98
|
-
| FR1 | CodeBlock MUST display the caller's code string as read-only code. Built-in or custom tokenization MAY change syntax paint but MUST NOT change displayed or copied source text. | released types, source, tests, consumer docs; established public interface | verify
|
|
99
|
-
| FR2 | A successful copy MUST write the exact `code` string, invoke `onCopy` once, expose localized copied feedback, and preserve the copy control's discoverable purpose. A failed write MUST NOT report success. | shipped source/tests; `architecture:component-test-sufficiency/INV2–INV6`; WCAG name/status obligations | verify
|
|
100
|
-
| FR3 | A title or visible non-plaintext language label MUST create the visible header. Hiding the language label MUST NOT remove copy behavior. Headerless copy remains available when enabled. | shipped source/tests/docs; established public interface | verify
|
|
101
|
-
| FR4 | Line numbers, highlighted lines, wrapping, width, container presentation, size, and maximum height MUST affect only their documented presentation axes. Numeric `maxHeight={0}` MUST remain a valid zero-pixel bound rather than being discarded. | released public type/docs; `architecture:public-component-api/INV3,INV9`; objective JavaScript nullish semantics | verified by focused regression
|
|
102
|
-
| FR5 | Collapse MUST become available only when enabled, a visible header exists, and the line threshold is met. The control MUST expose expanded state and a resolvable controlled region; collapsed content remains mounted but inert, and losing the control expands the region before hiding the control. | shipped docs/source/tests; WAI-ARIA disclosure pattern and HTML `inert` semantics | verify
|
|
103
|
-
| FR6 | The root and documented header/title/copy targets MUST remain on their visible painters, reflect the documented visual axes, and continue emitting the approved deprecated aliases until their authorized removal window. | `architecture:component-theming-surface/INV3–INV7,INV12`; source/docs/tests | verify
|
|
104
|
-
| FR7 | Supported DOM, data, ARIA, class, style, and `xstyle` inputs MUST compose on the documented root, and the public ref MUST reach that root. | `architecture:public-component-api/INV5,INV6,INV8`; shipped source | verify
|
|
105
|
-
| FR8 | The code viewport MUST enter sequential keyboard navigation only while at least one applicable axis effectively overflows; losing overflow MUST NOT move current focus. It retains an appropriate role and accessible name while focusable. | `spec:AST-025/FR12–FR13` | settled by current authority
|
|
106
|
-
| FR9 | With the diff layer on (`isDiff`), CodeBlock MUST classify each line by its first character — `+` added, `-` removed, `@@` header, anything else context — except that a `--- ` line (three minus signs and a space, the unified-diff file header form) directly followed by a `+++ ` line and then an `@@` line is a file header (both lines), wherever it occurs; every other `---`/`+++` line, `---i;` included, is removed or added. CodeBlock MUST set the glyph column aside and tokenize the REST of every line (context, added, and removed alike) with the base `language`'s syntax highlighting. Added, removed, and header lines MUST paint as whole rows — the line-number gutter cell included — from the syntax-theme slots `--color-syntax-added`, `--color-syntax-removed`, and `--color-syntax-diff-header`, whose defaults `architecture:theme-tokens` owns; header rows take the syntax theme's `punctuation` colour as text (`--color-text-secondary` in the default theme) and never change paint; context rows keep ordinary paint. On an added or removed row, the line number and the glyph take the row's marker slot — `--color-syntax-added-marker` / `--color-syntax-removed-marker` — so the margin reads the change in colour; every run of code text, painted by a token or not, keeps its syntax-theme colour. In the Astryx default syntax theme the marker slots default to the theme's tinted-text tokens and the header paint to the muted background (`architecture:theme-tokens` owns the values); a syntax theme that does not set them inherits its own colours — the marker slots and the header text its line-number colour (its `punctuation` slot), the header paint none at all (the header row sits on the theme's own syntax background) — so number, glyph, and header text stay legible on that theme's background in every page mode, and the glyph's shape carries the meaning where its colour does not. The glyph (`+`, `-`, the leading space of a context line, or nothing for a context or header line without one — the marker column is then empty and the whole line is code) MUST render in its own fixed-width marker column — secondary colour on context rows and the row's marker slot on added and removed rows, separated from the code by a token gap (`--spacing-2`, a proportion themes may vary like the rest), excluded from selection for layout yet present in the copied text — so the code column starts at the same inline offset on every line and the content's own indentation is never shifted; the gutter order is line number, marker, code, mirrored in RTL with the code's direction unchanged. The header (language label, copy, disclosure) and the viewport's name stay as for any other code: with the layer over plaintext the visible language label and the viewport name are `diff`, and over a language they name both (`tsx diff`). Markdown's fence mapping onto the layer is `component:Markdown` FR29. Row paint MUST compose with line numbers without moving text; on a highlighted added, removed, or header line the highlight REPLACES the row paint rather than stacking on it, so text keeps its single-paint contrast; and row paint MUST span the full line width — the scrolled overflow of a long line included, in wrapped and unwrapped mode — not only the visible viewport. Contrast floor: in the Astryx default syntax theme, every default token colour and the row's marker slot (line number and glyph) on a painted row MUST measure ≥ 4.5:1 against the default tints composited over the code block's own `--color-syntax-background` on the default card surface. In a syntax theme that overrides a diff slot, a token that already measures under 4.5:1 on that theme's own syntax background MUST NOT drop further on the slot, and every other token MUST clear 4.5:1 on it. Two known shortfalls, tracked separately: the shipped syntax presets do not yet meet the floor on the default tints with the layer on, and shipped page themes whose own text tokens fall under it on the tints keep that shortfall, tracked for a fix in those themes, until each sets its tokens or the diff slots. | this record DEC-1 | current; owner-approved 2026-10-12
|
|
96
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
97
|
+
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
|
|
98
|
+
| FR1 | CodeBlock MUST display the caller's code string as read-only code. Built-in or custom tokenization MAY change syntax paint but MUST NOT change displayed or copied source text. | released types, source, tests, consumer docs; established public interface | verify |
|
|
99
|
+
| FR2 | A successful copy MUST write the exact `code` string, invoke `onCopy` once, expose localized copied feedback, and preserve the copy control's discoverable purpose. A failed write MUST NOT report success. | shipped source/tests; `architecture:component-test-sufficiency/INV2–INV6`; WCAG name/status obligations | verify |
|
|
100
|
+
| FR3 | A title or visible non-plaintext language label MUST create the visible header. Hiding the language label MUST NOT remove copy behavior. Headerless copy remains available when enabled. | shipped source/tests/docs; established public interface | verify |
|
|
101
|
+
| FR4 | Line numbers, highlighted lines, wrapping, width, container presentation, size, and maximum height MUST affect only their documented presentation axes. Numeric `maxHeight={0}` MUST remain a valid zero-pixel bound rather than being discarded. | released public type/docs; `architecture:public-component-api/INV3,INV9`; objective JavaScript nullish semantics | verified by focused regression |
|
|
102
|
+
| FR5 | Collapse MUST become available only when enabled, a visible header exists, and the line threshold is met. The control MUST expose expanded state and a resolvable controlled region; collapsed content remains mounted but inert, and losing the control expands the region before hiding the control. | shipped docs/source/tests; WAI-ARIA disclosure pattern and HTML `inert` semantics | verify |
|
|
103
|
+
| FR6 | The root and documented header/title/copy targets MUST remain on their visible painters, reflect the documented visual axes, and continue emitting the approved deprecated aliases until their authorized removal window. | `architecture:component-theming-surface/INV3–INV7,INV12`; source/docs/tests | verify |
|
|
104
|
+
| FR7 | Supported DOM, data, ARIA, class, style, and `xstyle` inputs MUST compose on the documented root, and the public ref MUST reach that root. | `architecture:public-component-api/INV5,INV6,INV8`; shipped source | verify |
|
|
105
|
+
| FR8 | The code viewport MUST enter sequential keyboard navigation only while at least one applicable axis effectively overflows; losing overflow MUST NOT move current focus. It retains an appropriate role and accessible name while focusable. | `spec:AST-025/FR12–FR13` | settled by current authority |
|
|
106
|
+
| FR9 | With the diff layer on (`isDiff`), CodeBlock MUST classify each line by its first character — `+` added, `-` removed, `@@` header, anything else context — except that a `--- ` line (three minus signs and a space, the unified-diff file header form) directly followed by a `+++ ` line and then an `@@` line is a file header (both lines), wherever it occurs; every other `---`/`+++` line, `---i;` included, is removed or added. CodeBlock MUST set the glyph column aside and tokenize the REST of every line (context, added, and removed alike) with the base `language`'s syntax highlighting. Added, removed, and header lines MUST paint as whole rows — the line-number gutter cell included — from the syntax-theme slots `--color-syntax-added`, `--color-syntax-removed`, and `--color-syntax-diff-header`, whose defaults `architecture:theme-tokens` owns; header rows take the syntax theme's `punctuation` colour as text (`--color-text-secondary` in the default theme) and never change paint; context rows keep ordinary paint. On an added or removed row, the line number and the glyph take the row's marker slot — `--color-syntax-added-marker` / `--color-syntax-removed-marker` — so the margin reads the change in colour; every run of code text, painted by a token or not, keeps its syntax-theme colour. In the Astryx default syntax theme the marker slots default to the theme's tinted-text tokens and the header paint to the muted background (`architecture:theme-tokens` owns the values); a syntax theme that does not set them inherits its own colours — the marker slots and the header text its line-number colour (its `punctuation` slot), the header paint none at all (the header row sits on the theme's own syntax background) — so number, glyph, and header text stay legible on that theme's background in every page mode, and the glyph's shape carries the meaning where its colour does not. The glyph (`+`, `-`, the leading space of a context line, or nothing for a context or header line without one — the marker column is then empty and the whole line is code) MUST render in its own fixed-width marker column — secondary colour on context rows and the row's marker slot on added and removed rows, separated from the code by a token gap (`--spacing-2`, a proportion themes may vary like the rest), excluded from selection for layout yet present in the copied text — so the code column starts at the same inline offset on every line and the content's own indentation is never shifted; the gutter order is line number, marker, code, mirrored in RTL with the code's direction unchanged. The header (language label, copy, disclosure) and the viewport's name stay as for any other code: with the layer over plaintext the visible language label and the viewport name are `diff`, and over a language they name both (`tsx diff`). Markdown's fence mapping onto the layer is `component:Markdown` FR29. Row paint MUST compose with line numbers without moving text; on a highlighted added, removed, or header line the highlight REPLACES the row paint rather than stacking on it, so text keeps its single-paint contrast; and row paint MUST span the full line width — the scrolled overflow of a long line included, in wrapped and unwrapped mode — not only the visible viewport. Contrast floor: in the Astryx default syntax theme, every default token colour and the row's marker slot (line number and glyph) on a painted row MUST measure ≥ 4.5:1 against the default tints composited over the code block's own `--color-syntax-background` on the default card surface. In a syntax theme that overrides a diff slot, a token that already measures under 4.5:1 on that theme's own syntax background MUST NOT drop further on the slot, and every other token MUST clear 4.5:1 on it. Two known shortfalls, tracked separately: the shipped syntax presets do not yet meet the floor on the default tints with the layer on, and shipped page themes whose own text tokens fall under it on the tints keep that shortfall, tracked for a fix in those themes, until each sets its tokens or the diff slots. | this record DEC-1 | current; owner-approved 2026-10-12 |
|
|
107
107
|
|
|
108
108
|
### Allowed variation
|
|
109
109
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* SYNC: When Dialog.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {useState} from 'react';
|
|
12
|
+
import {useState, type SyntheticEvent} from 'react';
|
|
13
13
|
import {readFileSync} from 'node:fs';
|
|
14
14
|
import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
|
|
15
15
|
import type {MockInstance} from 'vitest';
|
|
@@ -1030,6 +1030,32 @@ describe('Dialog', () => {
|
|
|
1030
1030
|
expect(screen.getByTestId('custom-dialog')).toBeInTheDocument();
|
|
1031
1031
|
});
|
|
1032
1032
|
|
|
1033
|
+
it('runs backdrop dismissal before a consumer onClick', () => {
|
|
1034
|
+
const callOrder: string[] = [];
|
|
1035
|
+
const onClick = vi.fn((event: SyntheticEvent) => {
|
|
1036
|
+
event.preventDefault();
|
|
1037
|
+
callOrder.push('consumer');
|
|
1038
|
+
});
|
|
1039
|
+
const onOpenChange = vi.fn(() => {
|
|
1040
|
+
callOrder.push('dialog');
|
|
1041
|
+
});
|
|
1042
|
+
render(
|
|
1043
|
+
<Dialog
|
|
1044
|
+
isOpen={true}
|
|
1045
|
+
onOpenChange={onOpenChange}
|
|
1046
|
+
onClick={onClick}
|
|
1047
|
+
data-testid="click-dialog">
|
|
1048
|
+
Content
|
|
1049
|
+
</Dialog>,
|
|
1050
|
+
);
|
|
1051
|
+
|
|
1052
|
+
fireEvent.click(screen.getByTestId('click-dialog'));
|
|
1053
|
+
|
|
1054
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
1055
|
+
expect(onOpenChange).toHaveBeenCalledWith(false);
|
|
1056
|
+
expect(callOrder).toEqual(['dialog', 'consumer']);
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1033
1059
|
it('does not forward native open prop to dialog element', () => {
|
|
1034
1060
|
render(
|
|
1035
1061
|
<Dialog
|
package/src/Dialog/Dialog.tsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* @file Dialog.tsx
|
|
7
7
|
* @input Uses React, DialogHTMLAttributes, ReactNode, container (Layout), DialogContext, layerTextReset,
|
|
8
|
-
* layerStructureReset, modalOutlet, interactionModality
|
|
8
|
+
* layerStructureReset, modalOutlet, interactionModality, chainEventHandlers
|
|
9
9
|
* @output Exports Dialog component, DialogProps, DialogVariant, DialogPurpose types
|
|
10
10
|
* @position Core implementation; consumed by index.ts, tested by Dialog.test.tsx
|
|
11
11
|
*
|
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
} from '../Layout/padding.stylex';
|
|
66
66
|
import type {SpacingStep} from '../utils/types';
|
|
67
67
|
import {mergeProps} from '../utils';
|
|
68
|
+
import {chainEventHandlers} from '../utils/chainEventHandlers';
|
|
68
69
|
import {devWarn} from '../utils/devWarning';
|
|
69
70
|
import {DialogContext} from './DialogContext';
|
|
70
71
|
import {themeProps} from '../utils/themeProps';
|
|
@@ -679,9 +680,19 @@ export function Dialog({
|
|
|
679
680
|
</div>
|
|
680
681
|
);
|
|
681
682
|
|
|
682
|
-
// Filter out native open to prevent InvalidStateError when accidentally passed
|
|
683
|
+
// Filter out native open to prevent InvalidStateError when accidentally passed.
|
|
684
|
+
// Keep the consumer click handler separate so the modal path can compose it
|
|
685
|
+
// with Dialog-owned behavior without changing the inline preview path.
|
|
683
686
|
const hasPosition = position != null && !isFullscreen;
|
|
684
|
-
const {
|
|
687
|
+
const {onClick: onClickProp, ...propsWithoutClick} = props;
|
|
688
|
+
const {open: _open, ...safeProps} = propsWithoutClick as Record<
|
|
689
|
+
string,
|
|
690
|
+
unknown
|
|
691
|
+
>;
|
|
692
|
+
const inlineSafeProps: Record<string, unknown> = {
|
|
693
|
+
...safeProps,
|
|
694
|
+
onClick: onClickProp,
|
|
695
|
+
};
|
|
685
696
|
|
|
686
697
|
// --- Inline rendering path (for documentation previews) ---
|
|
687
698
|
if (isInline) {
|
|
@@ -691,7 +702,7 @@ export function Dialog({
|
|
|
691
702
|
|
|
692
703
|
return (
|
|
693
704
|
<div
|
|
694
|
-
{...
|
|
705
|
+
{...inlineSafeProps}
|
|
695
706
|
{...mergeProps(
|
|
696
707
|
themeProps('dialog', {variant}),
|
|
697
708
|
stylex.props(
|
|
@@ -760,7 +771,7 @@ export function Dialog({
|
|
|
760
771
|
className,
|
|
761
772
|
style,
|
|
762
773
|
)}
|
|
763
|
-
onClick={handleClick}
|
|
774
|
+
onClick={chainEventHandlers(handleClick, onClickProp)}
|
|
764
775
|
onCancel={handleCancel}
|
|
765
776
|
aria-modal="true"
|
|
766
777
|
// The default aria-labelledby (from a DialogHeader title) is set
|
|
@@ -260,6 +260,19 @@ describe('DialogHeader', () => {
|
|
|
260
260
|
expect(closeIcon).toHaveClass('astryx-icon');
|
|
261
261
|
});
|
|
262
262
|
|
|
263
|
+
it('renders numeric zero in both content slots', () => {
|
|
264
|
+
const {container} = render(
|
|
265
|
+
<DialogHeader title="Title" startContent={0} endContent={0} />,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
expect(
|
|
269
|
+
container.querySelector('.astryx-dialog-header-start-content'),
|
|
270
|
+
).toHaveTextContent(/^0$/);
|
|
271
|
+
expect(
|
|
272
|
+
container.querySelector('.astryx-dialog-header-end-content'),
|
|
273
|
+
).toHaveTextContent(/^0$/);
|
|
274
|
+
});
|
|
275
|
+
|
|
263
276
|
it('exposes the start-content target when startContent renders', () => {
|
|
264
277
|
const {container} = render(
|
|
265
278
|
<DialogHeader
|
|
@@ -292,8 +305,20 @@ describe('DialogHeader', () => {
|
|
|
292
305
|
).toHaveLength(1);
|
|
293
306
|
});
|
|
294
307
|
|
|
295
|
-
it(
|
|
296
|
-
|
|
308
|
+
it.each([
|
|
309
|
+
['empty string', ''],
|
|
310
|
+
['false', false],
|
|
311
|
+
['true', true],
|
|
312
|
+
['null', null],
|
|
313
|
+
['undefined', undefined],
|
|
314
|
+
])('omits both content-slot targets for %s', (_, content) => {
|
|
315
|
+
const {container} = render(
|
|
316
|
+
<DialogHeader
|
|
317
|
+
title="Title"
|
|
318
|
+
startContent={content}
|
|
319
|
+
endContent={content}
|
|
320
|
+
/>,
|
|
321
|
+
);
|
|
297
322
|
|
|
298
323
|
expect(
|
|
299
324
|
container.querySelector('.astryx-dialog-header-start-content'),
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file DialogHeader.tsx
|
|
7
|
-
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, DialogContext, mergeProps, themeProps
|
|
7
|
+
* @input Uses React, StyleX, LayoutHeader, Button, Icon, Heading, Text, DialogContext, isRenderable, mergeProps, themeProps
|
|
8
8
|
* @output Exports DialogHeader component and DialogHeaderProps
|
|
9
9
|
* @position Dialog header component; used with Dialog and Layout
|
|
10
10
|
*
|
|
@@ -25,7 +25,7 @@ import {Icon} from '../Icon';
|
|
|
25
25
|
import {Heading} from '../Heading/Heading';
|
|
26
26
|
import {Text} from '../Text/Text';
|
|
27
27
|
import type {BaseProps} from '../BaseProps';
|
|
28
|
-
import {mergeProps} from '../utils';
|
|
28
|
+
import {isRenderable, mergeProps} from '../utils';
|
|
29
29
|
import {themeProps} from '../utils/themeProps';
|
|
30
30
|
import {useDialogContext} from './DialogContext';
|
|
31
31
|
import {useTranslator} from '../i18n';
|
|
@@ -164,10 +164,9 @@ export function DialogHeader({
|
|
|
164
164
|
const dialogContext = useDialogContext();
|
|
165
165
|
const shouldAutoFocus = dialogContext?.isInline !== true;
|
|
166
166
|
const titleId = dialogContext?.titleId;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
subtitle != null && typeof subtitle !== 'boolean' && subtitle !== '';
|
|
167
|
+
const hasSubtitle = isRenderable(subtitle);
|
|
168
|
+
const hasStartContent = isRenderable(startContent);
|
|
169
|
+
const hasEndContent = isRenderable(endContent);
|
|
171
170
|
const shouldCompensateEndBlock =
|
|
172
171
|
endContentEdgeCompensation == null
|
|
173
172
|
? onOpenChange != null
|
|
@@ -203,7 +202,7 @@ export function DialogHeader({
|
|
|
203
202
|
themeProps('dialog-header'),
|
|
204
203
|
stylex.props(styles.container),
|
|
205
204
|
)}>
|
|
206
|
-
{
|
|
205
|
+
{hasStartContent && (
|
|
207
206
|
<div
|
|
208
207
|
{...mergeProps(
|
|
209
208
|
themeProps('dialog-header-start-content'),
|
|
@@ -231,7 +230,7 @@ export function DialogHeader({
|
|
|
231
230
|
</Text>
|
|
232
231
|
)}
|
|
233
232
|
</div>
|
|
234
|
-
{(
|
|
233
|
+
{(hasEndContent || onOpenChange != null) && (
|
|
235
234
|
<div
|
|
236
235
|
{...mergeProps(
|
|
237
236
|
themeProps('dialog-header-end-content'),
|