@astryxdesign/lab 0.4.7-canary.94c2d8b → 0.4.7-canary.98ea5ba

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.
@@ -42,6 +42,9 @@ export interface CodeEditorProps extends Omit<BaseProps<HTMLDivElement>, 'onChan
42
42
  * The escape is advertised to assistive technology via a visually hidden
43
43
  * aria-describedby hint (see `escapeHint`).
44
44
  *
45
+ * The editor carries an explicit tabindex="0" (including when read-only) so
46
+ * the scrollable container passes axe's scrollable-region-focusable check.
47
+ *
45
48
  * @example
46
49
  * ```
47
50
  * const [code, setCode] = useState('');
@@ -1 +1 @@
1
- {"version":3,"file":"CodeEditor.d.ts","sourceRoot":"","sources":["../../src/CodeEditor/CodeEditor.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAkBlD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,8BAA8B,CAAC;AAuF5D,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,CACX;IACC,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,+BAA+B;IAC/B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,uBAAuB;IACvB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;IAC5D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,QAAsB,EACtB,cAAsB,EACtB,UAAkB,EAClB,WAAW,EACX,SAAS,EACT,IAAW,EACX,SAAS,EAAE,eAAe,EAC1B,KAAK,EACL,UAAqE,EACrE,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,eAAe,+BAwUjB;yBAzVe,UAAU"}
1
+ {"version":3,"file":"CodeEditor.d.ts","sourceRoot":"","sources":["../../src/CodeEditor/CodeEditor.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAkBlD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,8BAA8B,CAAC;AAuF5D,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,CACX;IACC,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,qBAAqB;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,+BAA+B;IAC/B,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,uBAAuB;IACvB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;IAC5D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,QAAsB,EACtB,cAAsB,EACtB,UAAkB,EAClB,WAAW,EACX,SAAS,EACT,IAAW,EACX,SAAS,EAAE,eAAe,EAC1B,KAAK,EACL,UAAqE,EACrE,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,GAAG,KAAK,EACT,EAAE,eAAe,+BA8UjB;yBA/Ve,UAAU"}
@@ -170,6 +170,9 @@ let editorInstanceCounter = 0;
170
170
  * The escape is advertised to assistive technology via a visually hidden
171
171
  * aria-describedby hint (see `escapeHint`).
172
172
  *
173
+ * The editor carries an explicit tabindex="0" (including when read-only) so
174
+ * the scrollable container passes axe's scrollable-region-focusable check.
175
+ *
173
176
  * @example
174
177
  * ```
175
178
  * const [code, setCode] = useState('');
@@ -472,7 +475,14 @@ export function CodeEditor({
472
475
  ref: editorRef
473
476
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
474
477
  ,
475
- contentEditable: isReadOnly ? false : 'plaintext-only',
478
+ contentEditable: isReadOnly ? false : 'plaintext-only'
479
+ // Explicit tabindex: editing hosts have an effective tabIndex of 0
480
+ // already, but axe reads the attribute, and contenteditable is not
481
+ // on its natively-focusable whitelist — without it a maxHeight
482
+ // editor fails scrollable-region-focusable. Also keeps read-only
483
+ // editors keyboard-focusable, like a readonly textarea.
484
+ ,
485
+ tabIndex: 0,
476
486
  role: "textbox",
477
487
  "aria-multiline": "true",
478
488
  "aria-label": label,
@@ -1 +1 @@
1
- {"version":3,"file":"InfoTip.d.ts","sourceRoot":"","sources":["../../src/InfoTip/InfoTip.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAgC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAGpE,OAAO,EAAO,KAAK,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAW5D;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;AAEnC,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,KAA0B,EAC1B,IAAW,GACZ,EAAE,YAAY,GAAG,SAAS,CAiD1B;yBArDe,OAAO"}
1
+ {"version":3,"file":"InfoTip.d.ts","sourceRoot":"","sources":["../../src/InfoTip/InfoTip.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,EAAC,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,OAAO,EAAO,KAAK,QAAQ,EAAC,MAAM,yBAAyB,CAAC;AAW5D;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;AAEnC,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,KAA0B,EAC1B,IAAW,GACZ,EAAE,YAAY,GAAG,SAAS,CAqB1B;yBAzBe,OAAO"}
@@ -13,7 +13,6 @@
13
13
  * - /packages/lab/src/InfoTip/InfoTip.test.tsx (tests for new/changed behavior)
14
14
  * - /packages/lab/src/InfoTip/index.ts (exports if types change)
15
15
  */
16
- import { useCallback, useRef, useState } from 'react';
17
16
  import * as stylex from '@stylexjs/stylex';
18
17
  import { Icon } from '@astryxdesign/core/Icon';
19
18
  import { Tooltip } from '@astryxdesign/core/Tooltip';
@@ -69,26 +68,10 @@ export function InfoTip({
69
68
  label = 'More information',
70
69
  size = 'sm'
71
70
  }) {
72
- // Escape dismissal: `true` force-hides the tooltip (Tooltip isOpen={false});
73
- // `false` returns control to Tooltip's own hover/focus triggers (isOpen
74
- // undefined). Reset when the pointer or focus leaves the trigger so the
75
- // tooltip can re-open on the next hover/focus.
76
- const [isDismissed, setIsDismissed] = useState(false);
77
- const isOpenRef = useRef(false);
78
- const handleOpenChange = useCallback(isOpen => {
79
- isOpenRef.current = isOpen;
80
- }, []);
81
- const handleKeyDown = useCallback(event => {
82
- if (event.key === 'Escape' && isOpenRef.current) {
83
- // Only swallow Escape when it actually dismissed the tooltip, so an
84
- // enclosing dialog still closes on the next press.
85
- event.stopPropagation();
86
- setIsDismissed(true);
87
- }
88
- }, []);
89
- const handleReset = useCallback(() => {
90
- setIsDismissed(false);
91
- }, []);
71
+ // No local Escape handler: the trigger used to stopPropagation the press,
72
+ // which hid the tip but left the browser's close watcher to fire `cancel` on
73
+ // an enclosing Dialog, so one press took both (#5168). Tooltip's own entry in
74
+ // the shared dismissal stack takes it instead.
92
75
  return /*#__PURE__*/_jsx(Tooltip, {
93
76
  content: content
94
77
  // The trigger is a real button, so Tooltip's `auto` touch rule would
@@ -97,14 +80,9 @@ export function InfoTip({
97
80
  // an InfoTip's content is unreachable on a phone.
98
81
  ,
99
82
  touchTrigger: "tap",
100
- isOpen: isDismissed ? false : undefined,
101
- onOpenChange: handleOpenChange,
102
83
  children: /*#__PURE__*/_jsx("button", {
103
84
  type: "button",
104
85
  "aria-label": label,
105
- onKeyDown: handleKeyDown,
106
- onBlur: handleReset,
107
- onMouseLeave: handleReset,
108
86
  ...stylex.props(focusOutlineStyles.focusVisible, styles.trigger),
109
87
  children: /*#__PURE__*/_jsx(Icon, {
110
88
  icon: "info",
@@ -1 +1 @@
1
- {"version":3,"file":"MonthlyView.d.ts","sourceRoot":"","sources":["../../src/Schedule/MonthlyView.tsx"],"names":[],"mappings":"AA8CA,OAAO,KAAK,EAEV,YAAY,EAEb,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,0BAA0B;IACzC,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC1C;AAgSD,wBAAgB,yBAAyB,CAAC,EACxC,YAAgB,GACjB,GAAE,0BAA+B,GAAG,YAAY,CAAC,0BAA0B,CAAC,CAmC5E"}
1
+ {"version":3,"file":"MonthlyView.d.ts","sourceRoot":"","sources":["../../src/Schedule/MonthlyView.tsx"],"names":[],"mappings":"AA8CA,OAAO,KAAK,EAEV,YAAY,EAEb,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,0BAA0B;IACzC,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC1C;AA4SD,wBAAgB,yBAAyB,CAAC,EACxC,YAAgB,GACjB,GAAE,0BAA+B,GAAG,YAAY,CAAC,0BAA0B,CAAC,CAmC5E"}
@@ -45,7 +45,12 @@ function ScheduleMonthlyView(_props) {
45
45
  children: /*#__PURE__*/_jsxs("div", {
46
46
  role: "grid",
47
47
  "aria-label": formatMonthTitle(rangeDate, timezoneID),
48
- "aria-readonly": true,
48
+ "aria-readonly": true
49
+ // The grid scrolls horizontally at narrow viewports and contains no
50
+ // focusable descendants, so it must be focusable itself for keyboard
51
+ // scrolling (axe: scrollable-region-focusable).
52
+ ,
53
+ tabIndex: 0,
49
54
  ...stylex.props(styles.monthGrid),
50
55
  children: [/*#__PURE__*/_jsx("div", {
51
56
  role: "row",
@@ -1 +1 @@
1
- {"version":3,"file":"PaginationPlugin.d.ts","sourceRoot":"","sources":["../../../src/Schedule/plugins/PaginationPlugin.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAEV,cAAc,EACd,sBAAsB,EACvB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CACnC;AA8DD,eAAO,MAAM,+BAA+B,gBACV,CAAC;AAEnC,wBAAgB,2BAA2B,CAAC,EAC1C,QAAkB,GACnB,GAAE,+BAAoC,GAAG,cAAc,CAKvD"}
1
+ {"version":3,"file":"PaginationPlugin.d.ts","sourceRoot":"","sources":["../../../src/Schedule/plugins/PaginationPlugin.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAEV,cAAc,EACd,sBAAsB,EACvB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CACnC;AA4ED,eAAO,MAAM,+BAA+B,gBAAmC,CAAC;AAEhF,wBAAgB,2BAA2B,CAAC,EAC1C,QAAkB,GACnB,GAAE,+BAAoC,GAAG,cAAc,CAEvD"}
@@ -13,6 +13,7 @@ import { Button } from '@astryxdesign/core/Button';
13
13
  import { ButtonGroup } from '@astryxdesign/core/ButtonGroup';
14
14
  import { Icon } from '@astryxdesign/core/Icon';
15
15
  import { IconButton } from '@astryxdesign/core/IconButton';
16
+ import { rtlStyles } from '@astryxdesign/core/utils';
16
17
  import { useScheduleContext } from "../context.js";
17
18
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
18
19
  function SchedulePaginationControls() {
@@ -31,7 +32,8 @@ function SchedulePaginationControls() {
31
32
  icon: /*#__PURE__*/_jsx(Icon, {
32
33
  icon: "chevronLeft",
33
34
  size: "sm",
34
- color: "inherit"
35
+ color: "inherit",
36
+ xstyle: rtlStyles.mirror
35
37
  }),
36
38
  onClick: onPreviousDate
37
39
  }), /*#__PURE__*/_jsx(Button, {
@@ -43,7 +45,8 @@ function SchedulePaginationControls() {
43
45
  icon: /*#__PURE__*/_jsx(Icon, {
44
46
  icon: "chevronRight",
45
47
  size: "sm",
46
- color: "inherit"
48
+ color: "inherit",
49
+ xstyle: rtlStyles.mirror
47
50
  }),
48
51
  onClick: onNextDate
49
52
  })]
@@ -693,7 +693,10 @@ export declare const styles: Readonly<{
693
693
  readonly fontFamily: stylex.StyleXClassNameFor<"fontFamily", "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif">;
694
694
  readonly color: stylex.StyleXClassNameFor<"color", "light-dark(#0A1317, #DFE2E5)">;
695
695
  }>;
696
- readonly listEventPast: Readonly<{
696
+ readonly listEventTitlePast: Readonly<{
697
+ readonly color: stylex.StyleXClassNameFor<"color", "light-dark(#4E606F, #AAAFB5)">;
698
+ }>;
699
+ readonly listEventDotPast: Readonly<{
697
700
  readonly opacity: stylex.StyleXClassNameFor<"opacity", 0.5>;
698
701
  }>;
699
702
  readonly eventBlue: Readonly<{
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/Schedule/shared.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/Schedule/shared.tsx"],"names":[],"mappings":"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"}
@@ -211,16 +211,11 @@ export function ListEventRow({
211
211
  const category = getEventCategory(event, categories);
212
212
  return /*#__PURE__*/_jsxs("div", {
213
213
  ...{
214
- 0: {
215
- className: "xrvj5dj x857yr4 x6s0dn4 x1txdalj xeuugli"
216
- },
217
- 1: {
218
- className: "xrvj5dj x857yr4 x6s0dn4 x1txdalj xeuugli xbyyjgo"
219
- }
220
- }[!!isPast << 0],
214
+ className: "xrvj5dj x857yr4 x6s0dn4 x1txdalj xeuugli"
215
+ },
221
216
  children: [/*#__PURE__*/_jsx("span", {
222
217
  "aria-hidden": true,
223
- ...stylex.props(styles.listEventDot, eventDotColorStyle(category.color))
218
+ ...stylex.props(styles.listEventDot, eventDotColorStyle(category.color), isPast && styles.listEventDotPast)
224
219
  }), /*#__PURE__*/_jsx("span", {
225
220
  ...{
226
221
  className: "x9ynric xv1l7n4 x141an7d x1ltkj2j xuxw1ft"
@@ -228,8 +223,13 @@ export function ListEventRow({
228
223
  children: isDayEvent(event) ? 'All day' : formatEventTimeRange(event, timezoneID)
229
224
  }), /*#__PURE__*/_jsx("span", {
230
225
  ...{
231
- className: "x9ynric x1tgivj0 xeuugli xb3r6kr xlyipyv xuxw1ft xjm74w1 xw6l6zx x1e4wzip"
232
- },
226
+ 0: {
227
+ className: "x9ynric x1tgivj0 xeuugli xb3r6kr xlyipyv xuxw1ft xjm74w1 xw6l6zx x1e4wzip"
228
+ },
229
+ 1: {
230
+ className: "x9ynric xeuugli xb3r6kr xlyipyv xuxw1ft xjm74w1 xw6l6zx x1e4wzip xv1l7n4"
231
+ }
232
+ }[!!isPast << 0],
233
233
  children: event.title
234
234
  })]
235
235
  });
@@ -1064,7 +1064,11 @@ export const styles = {
1064
1064
  k63SB2: "x1e4wzip",
1065
1065
  $$css: true
1066
1066
  },
1067
- listEventPast: {
1067
+ listEventTitlePast: {
1068
+ kMwMTN: "xv1l7n4",
1069
+ $$css: true
1070
+ },
1071
+ listEventDotPast: {
1068
1072
  kSiTet: "xbyyjgo",
1069
1073
  $$css: true
1070
1074
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/lab",
3
- "version": "0.4.7-canary.94c2d8b",
3
+ "version": "0.4.7-canary.98ea5ba",
4
4
  "description": "Experimental Astryx components — published to npm only under the @canary dist-tag for early testing; never released as a stable (latest) version.",
5
5
  "author": "Meta Open Source",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@stylexjs/stylex": ">=0.10.0",
48
- "@astryxdesign/core": "0.4.7-canary.94c2d8b",
48
+ "@astryxdesign/core": "0.4.7-canary.98ea5ba",
49
49
  "react": ">=19.0.0",
50
50
  "react-dom": ">=19.0.0"
51
51
  },
@@ -55,7 +55,7 @@
55
55
  "d3-shape": "^3.2.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@astryxdesign/cli": "0.4.7-canary.94c2d8b",
58
+ "@astryxdesign/cli": "0.4.7-canary.98ea5ba",
59
59
  "@types/d3-array": "^3.2.2",
60
60
  "@types/d3-scale": "^4.0.9",
61
61
  "@types/d3-shape": "^3.1.8",
@@ -188,4 +188,40 @@ describe('CodeEditor', () => {
188
188
 
189
189
  expect(onChange).toHaveBeenCalledWith(' a;\nb;');
190
190
  });
191
+
192
+ it('exposes an explicit tabindex so scrollable editors count as focusable content', () => {
193
+ // contenteditable is not on axe's natively-focusable whitelist, and axe
194
+ // reads the tabindex ATTRIBUTE (not the browser's effective tabIndex),
195
+ // so the scrollable editorContainer only passes scrollable-region-focusable
196
+ // when the <code> editor materializes tabindex="0" explicitly.
197
+ render(
198
+ <CodeEditor
199
+ value={Array.from({length: 30}, (_, i) => `line ${i + 1};`).join('\n')}
200
+ onChange={() => {}}
201
+ language="typescript"
202
+ label="Edit snippet"
203
+ maxHeight={200}
204
+ />,
205
+ );
206
+ expect(screen.getByRole('textbox', {name: 'Edit snippet'})).toHaveAttribute(
207
+ 'tabindex',
208
+ '0',
209
+ );
210
+ });
211
+
212
+ it('keeps read-only editors keyboard-focusable, like a readonly textarea', () => {
213
+ render(
214
+ <CodeEditor
215
+ value="const x = 1;"
216
+ onChange={() => {}}
217
+ language="typescript"
218
+ label="Edit snippet"
219
+ isReadOnly
220
+ />,
221
+ );
222
+ expect(screen.getByRole('textbox', {name: 'Edit snippet'})).toHaveAttribute(
223
+ 'tabindex',
224
+ '0',
225
+ );
226
+ });
191
227
  });
@@ -242,6 +242,9 @@ let editorInstanceCounter = 0;
242
242
  * The escape is advertised to assistive technology via a visually hidden
243
243
  * aria-describedby hint (see `escapeHint`).
244
244
  *
245
+ * The editor carries an explicit tabindex="0" (including when read-only) so
246
+ * the scrollable container passes axe's scrollable-region-focusable check.
247
+ *
245
248
  * @example
246
249
  * ```
247
250
  * const [code, setCode] = useState('');
@@ -575,6 +578,12 @@ export function CodeEditor({
575
578
  ref={editorRef}
576
579
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
577
580
  contentEditable={isReadOnly ? false : ('plaintext-only' as any)}
581
+ // Explicit tabindex: editing hosts have an effective tabIndex of 0
582
+ // already, but axe reads the attribute, and contenteditable is not
583
+ // on its natively-focusable whitelist — without it a maxHeight
584
+ // editor fails scrollable-region-focusable. Also keeps read-only
585
+ // editors keyboard-focusable, like a readonly textarea.
586
+ tabIndex={0}
578
587
  role="textbox"
579
588
  aria-multiline="true"
580
589
  aria-label={label}
@@ -14,7 +14,7 @@
14
14
  * - /packages/lab/src/InfoTip/index.ts (exports if types change)
15
15
  */
16
16
 
17
- import {useCallback, useRef, useState, type ReactNode} from 'react';
17
+ import {type ReactNode} from 'react';
18
18
  import * as stylex from '@stylexjs/stylex';
19
19
 
20
20
  import {Icon, type IconSize} from '@astryxdesign/core/Icon';
@@ -103,33 +103,10 @@ export function InfoTip({
103
103
  label = 'More information',
104
104
  size = 'sm',
105
105
  }: InfoTipProps): ReactNode {
106
- // Escape dismissal: `true` force-hides the tooltip (Tooltip isOpen={false});
107
- // `false` returns control to Tooltip's own hover/focus triggers (isOpen
108
- // undefined). Reset when the pointer or focus leaves the trigger so the
109
- // tooltip can re-open on the next hover/focus.
110
- const [isDismissed, setIsDismissed] = useState(false);
111
- const isOpenRef = useRef(false);
112
-
113
- const handleOpenChange = useCallback((isOpen: boolean) => {
114
- isOpenRef.current = isOpen;
115
- }, []);
116
-
117
- const handleKeyDown = useCallback(
118
- (event: React.KeyboardEvent<HTMLButtonElement>) => {
119
- if (event.key === 'Escape' && isOpenRef.current) {
120
- // Only swallow Escape when it actually dismissed the tooltip, so an
121
- // enclosing dialog still closes on the next press.
122
- event.stopPropagation();
123
- setIsDismissed(true);
124
- }
125
- },
126
- [],
127
- );
128
-
129
- const handleReset = useCallback(() => {
130
- setIsDismissed(false);
131
- }, []);
132
-
106
+ // No local Escape handler: the trigger used to stopPropagation the press,
107
+ // which hid the tip but left the browser's close watcher to fire `cancel` on
108
+ // an enclosing Dialog, so one press took both (#5168). Tooltip's own entry in
109
+ // the shared dismissal stack takes it instead.
133
110
  return (
134
111
  <Tooltip
135
112
  content={content}
@@ -137,15 +114,10 @@ export function InfoTip({
137
114
  // give the tap to the control and suppress the tooltip. Here the tooltip
138
115
  // IS the control's only purpose, so the tap has to open it — otherwise
139
116
  // an InfoTip's content is unreachable on a phone.
140
- touchTrigger="tap"
141
- isOpen={isDismissed ? false : undefined}
142
- onOpenChange={handleOpenChange}>
117
+ touchTrigger="tap">
143
118
  <button
144
119
  type="button"
145
120
  aria-label={label}
146
- onKeyDown={handleKeyDown}
147
- onBlur={handleReset}
148
- onMouseLeave={handleReset}
149
121
  {...stylex.props(focusOutlineStyles.focusVisible, styles.trigger)}>
150
122
  <Icon icon="info" size={size} />
151
123
  </button>
@@ -57,8 +57,16 @@ export interface ScheduleMonthlyViewOptions {
57
57
  function ScheduleMonthlyView(
58
58
  _props: ScheduleViewComponentProps<ScheduleMonthlyViewOptions>,
59
59
  ) {
60
- const {events, categories, date, focusDate, timezoneID, range, isLoading, headingLevel} =
61
- useScheduleContext();
60
+ const {
61
+ events,
62
+ categories,
63
+ date,
64
+ focusDate,
65
+ timezoneID,
66
+ range,
67
+ isLoading,
68
+ headingLevel,
69
+ } = useScheduleContext();
62
70
  const rangeDate = date.toPlainDate();
63
71
  const highlightedDate = focusDate.toPlainDate();
64
72
  const currentTime = useCurrentTime();
@@ -76,6 +84,10 @@ function ScheduleMonthlyView(
76
84
  role="grid"
77
85
  aria-label={formatMonthTitle(rangeDate, timezoneID)}
78
86
  aria-readonly
87
+ // The grid scrolls horizontally at narrow viewports and contains no
88
+ // focusable descendants, so it must be focusable itself for keyboard
89
+ // scrolling (axe: scrollable-region-focusable).
90
+ tabIndex={0}
79
91
  {...stylex.props(styles.monthGrid)}>
80
92
  <div role="row" {...stylex.props(styles.weekHeader)}>
81
93
  {days.slice(0, 7).map((day, index) => (
@@ -114,9 +114,9 @@ describe('Schedule', () => {
114
114
  await waitFor(() => {
115
115
  expect(screen.getByText('Visible sync')).toBeInTheDocument();
116
116
  });
117
- expect(
118
- screen.getAllByRole('heading', {level: 3}).length,
119
- ).toBeGreaterThan(0);
117
+ expect(screen.getAllByRole('heading', {level: 3}).length).toBeGreaterThan(
118
+ 0,
119
+ );
120
120
  });
121
121
 
122
122
  it('renders list day headings at the configured headingLevel (default 3)', async () => {
@@ -133,9 +133,9 @@ describe('Schedule', () => {
133
133
  await waitFor(() => {
134
134
  expect(screen.getByText('Visible sync')).toBeInTheDocument();
135
135
  });
136
- expect(
137
- screen.getAllByRole('heading', {level: 3}).length,
138
- ).toBeGreaterThan(0);
136
+ expect(screen.getAllByRole('heading', {level: 3}).length).toBeGreaterThan(
137
+ 0,
138
+ );
139
139
  });
140
140
 
141
141
  it('loads async events with Instant range boundaries', async () => {
@@ -249,6 +249,27 @@ describe('Schedule', () => {
249
249
  ).toBeInTheDocument();
250
250
  });
251
251
 
252
+ it('makes the scrollable monthly grid keyboard-focusable', () => {
253
+ // The month grid is a horizontal scroll container with no focusable
254
+ // descendants, so it needs tabindex="0" itself for
255
+ // scrollable-region-focusable to pass and for keyboard scrolling.
256
+ render(
257
+ <Schedule
258
+ view={createScheduleMonthlyView()}
259
+ events={events}
260
+ categories={categories}
261
+ date={Date.UTC(2026, 4, 13)}
262
+ focusDate={Date.UTC(2026, 4, 13)}
263
+ timezoneID="UTC"
264
+ />,
265
+ );
266
+
267
+ expect(screen.getByRole('grid', {name: 'May 2026'})).toHaveAttribute(
268
+ 'tabindex',
269
+ '0',
270
+ );
271
+ });
272
+
252
273
  it('exposes time grid views as ARIA grids', () => {
253
274
  render(
254
275
  <Schedule
@@ -14,6 +14,7 @@ import {Button} from '@astryxdesign/core/Button';
14
14
  import {ButtonGroup} from '@astryxdesign/core/ButtonGroup';
15
15
  import {Icon} from '@astryxdesign/core/Icon';
16
16
  import {IconButton} from '@astryxdesign/core/IconButton';
17
+ import {rtlStyles} from '@astryxdesign/core/utils';
17
18
  import {useScheduleContext} from '../context';
18
19
  import type {
19
20
  ScheduleHeaderContent,
@@ -37,13 +38,27 @@ function SchedulePaginationControls() {
37
38
  <ButtonGroup label="Schedule pagination" size="sm">
38
39
  <IconButton
39
40
  label={previousDateLabel}
40
- icon={<Icon icon="chevronLeft" size="sm" color="inherit" />}
41
+ icon={
42
+ <Icon
43
+ icon="chevronLeft"
44
+ size="sm"
45
+ color="inherit"
46
+ xstyle={rtlStyles.mirror}
47
+ />
48
+ }
41
49
  onClick={onPreviousDate}
42
50
  />
43
51
  <Button label="Today" size="sm" onClick={onToday} />
44
52
  <IconButton
45
53
  label={nextDateLabel}
46
- icon={<Icon icon="chevronRight" size="sm" color="inherit" />}
54
+ icon={
55
+ <Icon
56
+ icon="chevronRight"
57
+ size="sm"
58
+ color="inherit"
59
+ xstyle={rtlStyles.mirror}
60
+ />
61
+ }
47
62
  onClick={onNextDate}
48
63
  />
49
64
  </ButtonGroup>
@@ -85,14 +100,10 @@ function createSchedulePaginationPlugin({
85
100
  };
86
101
  }
87
102
 
88
- export const defaultSchedulePaginationPlugin =
89
- createSchedulePaginationPlugin();
103
+ export const defaultSchedulePaginationPlugin = createSchedulePaginationPlugin();
90
104
 
91
105
  export function useSchedulePaginationPlugin({
92
106
  position = 'start',
93
107
  }: SchedulePaginationPluginOptions = {}): SchedulePlugin {
94
- return useMemo(
95
- () => createSchedulePaginationPlugin({position}),
96
- [position],
97
- );
108
+ return useMemo(() => createSchedulePaginationPlugin({position}), [position]);
98
109
  }
@@ -244,12 +244,13 @@ export function ListEventRow({
244
244
  const {categories} = useScheduleContext();
245
245
  const category = getEventCategory(event, categories);
246
246
  return (
247
- <div {...stylex.props(styles.listEventRow, isPast && styles.listEventPast)}>
247
+ <div {...stylex.props(styles.listEventRow)}>
248
248
  <span
249
249
  aria-hidden
250
250
  {...stylex.props(
251
251
  styles.listEventDot,
252
252
  eventDotColorStyle(category.color),
253
+ isPast && styles.listEventDotPast,
253
254
  )}
254
255
  />
255
256
  <span {...stylex.props(styles.listEventTime)}>
@@ -257,7 +258,13 @@ export function ListEventRow({
257
258
  ? 'All day'
258
259
  : formatEventTimeRange(event, timezoneID)}
259
260
  </span>
260
- <span {...stylex.props(styles.listEventTitle)}>{event.title}</span>
261
+ <span
262
+ {...stylex.props(
263
+ styles.listEventTitle,
264
+ isPast && styles.listEventTitlePast,
265
+ )}>
266
+ {event.title}
267
+ </span>
261
268
  </div>
262
269
  );
263
270
  }
@@ -1072,7 +1079,13 @@ export const styles = stylex.create({
1072
1079
  lineHeight: typeScaleVars['--text-body-leading'],
1073
1080
  fontWeight: fontWeightVars['--font-weight-medium'],
1074
1081
  },
1075
- listEventPast: {
1082
+ // A past row is muted with the secondary text token, which the theme
1083
+ // guarantees at AA. Fading the whole row with opacity instead pulled its
1084
+ // text toward the surface behind it — 2.35:1 on the light theme.
1085
+ listEventTitlePast: {
1086
+ color: colorVars['--color-text-secondary'],
1087
+ },
1088
+ listEventDotPast: {
1076
1089
  opacity: 0.5,
1077
1090
  },
1078
1091
  eventBlue: {
@@ -1,51 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- import {describe, it, expect} from 'vitest';
4
-
5
- import {docs as ChatReasoningDocs} from '../ChatReasoning/ChatReasoning.doc.mjs';
6
- import {docs as CodeEditorDocs} from '../CodeEditor/CodeEditor.doc.mjs';
7
- import {docs as LogStreamDocs} from '../LogStream/LogStream.doc.mjs';
8
-
9
- function getProps(docs: Record<string, unknown>): Array<{name: string}> {
10
- return (
11
- (docs.props as Array<{name: string}>) ||
12
- (
13
- docs.components as Array<{
14
- props: Array<{name: string}>;
15
- }>
16
- )?.[0]?.props ||
17
- []
18
- );
19
- }
20
-
21
- describe('Lab API Contract Drift (#4163)', () => {
22
- it('documents ChatReasoning props', () => {
23
- const props = getProps(ChatReasoningDocs).map(p => p.name);
24
- expect(props).toContain('duration');
25
- expect(props).toContain('isStreaming');
26
- expect(props).toContain('isExpanded');
27
- expect(props).toContain('defaultIsExpanded');
28
- expect(props).toContain('onExpandedChange');
29
- });
30
-
31
- it('documents CodeEditor props', () => {
32
- const props = getProps(CodeEditorDocs).map(p => p.name);
33
- expect(props).toContain('language');
34
- expect(props).toContain('hasLineNumbers');
35
- expect(props).toContain('isReadOnly');
36
- expect(props).toContain('placeholder');
37
- expect(props).toContain('maxHeight');
38
- expect(props).toContain('size');
39
- expect(props).toContain('tokenizer');
40
- });
41
-
42
- it('documents LogStream props', () => {
43
- const props = getProps(LogStreamDocs).map(p => p.name);
44
- expect(props).toContain('variant');
45
- expect(props).toContain('isFollowing');
46
- expect(props).toContain('onFollowChange');
47
- expect(props).toContain('maxHeight');
48
- expect(props).toContain('hasTimestamps');
49
- expect(props).toContain('renderEntry');
50
- });
51
- });