@astryxdesign/core 0.7.6-canary.19ae6fc → 0.7.6-canary.25f61ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/Layer/useLayer.d.ts.map +1 -1
  2. package/dist/Layer/useLayer.js +27 -6
  3. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  4. package/dist/PowerSearch/PowerSearch.js +37 -30
  5. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  6. package/dist/Tokenizer/Tokenizer.js +15 -4
  7. package/dist/Tokenizer/TokenizerClearAllMessageContext.d.ts +2 -0
  8. package/dist/Tokenizer/TokenizerClearAllMessageContext.d.ts.map +1 -0
  9. package/dist/Tokenizer/TokenizerClearAllMessageContext.js +17 -0
  10. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  11. package/dist/i18n/generated-locales/en.generated.js +1 -0
  12. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  13. package/dist/i18n/generated-locales/pseudo.generated.js +1 -0
  14. package/locales/en.json +5 -1
  15. package/locales/pseudo.json +3 -0
  16. package/package.json +4 -4
  17. package/src/CodeBlock/__tests__/CodeBlockCopyClearance.a11y.chromium.spec.ts +38 -3
  18. package/src/Layer/useLayer.test.tsx +78 -3
  19. package/src/Layer/useLayer.tsx +27 -6
  20. package/src/PowerSearch/PowerSearch.test.tsx +24 -0
  21. package/src/PowerSearch/PowerSearch.tsx +42 -31
  22. package/src/Tokenizer/Tokenizer.spec.md +13 -1
  23. package/src/Tokenizer/Tokenizer.test.tsx +27 -0
  24. package/src/Tokenizer/Tokenizer.tsx +24 -10
  25. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +8 -0
  26. package/src/Tokenizer/TokenizerClearAllMessageContext.ts +21 -0
  27. package/src/i18n/generated-locales/en.generated.ts +1 -0
  28. package/src/i18n/generated-locales/pseudo.generated.ts +1 -0
  29. package/src/motion/README.md +2 -1
  30. package/src/motion/roles.test.ts +5 -4
@@ -1 +1 @@
1
- {"version":3,"file":"useLayer.d.ts","sourceRoot":"","sources":["../../src/Layer/useLayer.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAQZ,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAsInD;;GAEG;AACH,UAAU,kBAAkB;IAC1B,aAAa,EAAE,KAAK,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;IACtD;;;OAGG;IACH,cAAc,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CACtD;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAClC;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;IACpD;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV;;OAEG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED;;GAEG;AACH,UAAU,gBAAgB;IACxB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IAC3D,IAAI,EAAE,SAAS,CAAC;IAChB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,GAAG,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IAE9B;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE,kBAAkB,KAAK,SAAS,CAAC;CACxE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,GAAG,EAAE,SAAS,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,MAAM,IAAI,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,KAAK,SAAS,CAAC;CACrE;AAED,UAAU,mBAAmB;IAC3B,gBAAgB,EAAE,MAAM,OAAO,CAAC;CACjC;AAED;;;GAGG;AACH,UAAU,2BAA4B,SAAQ,mBAAmB;IAC/D;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,KAAK,0BAA0B,GAAG,kBAAkB,GAAG,mBAAmB,CAAC;AAC3E,KAAK,wBAAwB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;AA8gBvE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,GAAE,cAAwB,EACnC,SAAS,GAAE,cAAyB,GACnC,MAAM,CAiBR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,cAAc,EACzB,SAAS,EAAE,cAAc,EACzB,cAAc,EAAE,OAAO,GACtB,KAAK,CAAC,aAAa,CAQrB;AA4BD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,GACd,kBAAkB,CAoCpB;AAm2BD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CAAC;AAC3E,wBAAgB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAAC;AAWvE;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,2BAA2B,GACnC,0BAA0B,CAAC;AAC9B,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,iBAAiB,GACzB,wBAAwB,CAAC"}
1
+ {"version":3,"file":"useLayer.d.ts","sourceRoot":"","sources":["../../src/Layer/useLayer.tsx"],"names":[],"mappings":"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"}
@@ -328,8 +328,9 @@ function failsAnchor(layer, anchor) {
328
328
  * every show and remount, and while the layer is open whenever its size, its
329
329
  * content's size, its anchor's size or the viewport changes; each of those
330
330
  * reads lands before the next frame paints. A same-size anchor that moves on
331
- * screen, in layout or because something scrolled, is read after paint, from
332
- * the anchor-move observer (FR8). Scroll events are not read: during one the
331
+ * screen, in layout or because something scrolled, is reported after paint by
332
+ * the anchor-move observer and read in the next frame, before it paints (FR8).
333
+ * Scroll events are not read: during one the
333
334
  * anchor has moved but the layer anchored to it not yet, so the read would
334
335
  * compare a moved anchor with a stale layer. Once switched, a layer keeps its
335
336
  * anchor-clear list until it next opens, so it never jumps back over its
@@ -469,9 +470,16 @@ function observeMove(element, onMove) {
469
470
  * range (spec:AST-059 FR4, FR8): the layer, its content or the anchor
470
471
  * resizing (ResizeObserver, before paint), the viewport resizing (before
471
472
  * paint), and the anchor moving on screen without resizing, in layout or by a
472
- * scroll (`observeMove`, after paint). Each calls `keepAnchorClear` for this layer and anchor while
473
- * `isCurrent` says they are still the live pair. Returns the function that
474
- * removes every observer and listener it added.
473
+ * scroll (`observeMove`, reported after paint). Each calls `keepAnchorClear`
474
+ * for this layer and anchor while `isCurrent` says they are still the live
475
+ * pair. A move is read in the next animation frame, not in the task that
476
+ * reports it: placing the layer afresh there, right after the frame that
477
+ * scrolled its anchor, lets Chromium lay it out for one more frame against
478
+ * the anchor's position before the scroll, over the anchor, so the second
479
+ * painted frame would still cover it. Read in the next frame, before it
480
+ * paints, the fresh placement uses the moved anchor. Reports in one frame
481
+ * share one read. Returns the function that removes every observer and
482
+ * listener it added.
475
483
  */
476
484
  function observeAnchorClearance(layer, anchor, isCurrent) {
477
485
  const view = layer.ownerDocument.defaultView;
@@ -492,11 +500,24 @@ function observeAnchorClearance(layer, anchor, isCurrent) {
492
500
  }
493
501
  observer.observe(anchor);
494
502
  }
495
- const stopObservingMove = observeMove(anchor, reread);
503
+ let moveFrame = null;
504
+ const stopObservingMove = observeMove(anchor, () => {
505
+ if (moveFrame !== null) {
506
+ return;
507
+ }
508
+ moveFrame = view.requestAnimationFrame(() => {
509
+ moveFrame = null;
510
+ reread();
511
+ });
512
+ });
496
513
  view.addEventListener('resize', reread);
497
514
  return () => {
498
515
  observer?.disconnect();
499
516
  stopObservingMove();
517
+ if (moveFrame !== null) {
518
+ view.cancelAnimationFrame(moveFrame);
519
+ moveFrame = null;
520
+ }
500
521
  view.removeEventListener('resize', reread);
501
522
  };
502
523
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PowerSearch.d.ts","sourceRoot":"","sources":["../../src/PowerSearch/PowerSearch.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PowerSearch.d.ts","sourceRoot":"","sources":["../../src/PowerSearch/PowerSearch.tsx"],"names":[],"mappings":"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"}
@@ -16,6 +16,7 @@
16
16
  import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
17
17
  import * as stylex from '@stylexjs/stylex';
18
18
  import { Tokenizer } from "../Tokenizer/index.js";
19
+ import { TokenizerClearAllMessageContext } from "../Tokenizer/TokenizerClearAllMessageContext.js";
19
20
  import { TypeaheadItem } from "../Typeahead/TypeaheadItem.js";
20
21
  import { Token } from "../Token/index.js";
21
22
  import { Avatar } from "../Avatar/index.js";
@@ -769,6 +770,9 @@ export function PowerSearch({
769
770
  // replay (which resets a flag and would speak the count present on mount
770
771
  // unprompted) announces nothing until the count changes.
771
772
  const announce = useAnnounce();
773
+ // PowerSearch words its Clear all as its record does; the field announces
774
+ // these words instead of Tokenizer's (component:Tokenizer AR7).
775
+ const clearAllMessage = useMemo(() => t('@astryx.powersearch.allFiltersRemoved'), [t]);
772
776
  const announcedResultCountRef = useRef(resultCountText);
773
777
  useEffect(() => {
774
778
  if (resultCountText === announcedResultCountRef.current) {
@@ -799,36 +803,39 @@ export function PowerSearch({
799
803
  children: [/*#__PURE__*/_jsx("div", {
800
804
  ref: useMergedRefs(ref, popover.triggerRef),
801
805
  ...themeProps('power-search'),
802
- children: /*#__PURE__*/_jsx(Tokenizer, {
803
- handleRef: tokenizerRef,
804
- label: label,
805
- isLabelHidden: isLabelHidden,
806
- searchSource: searchSource,
807
- value: tokenizerValue,
808
- onChange: handleTokenizerChange,
809
- renderToken: renderToken,
810
- renderItem: renderItem,
811
- maxMenuItems: MAX_BROWSE_MENU_ITEMS,
812
- menuWidth: menuWidth,
813
- placeholder: filters.length === 0 ? placeholder : '',
814
- hasAutoFocus: hasAutoFocus,
815
- hasClear: hasClear && !isReadOnly,
816
- startIcon: startIcon,
817
- endContent: combinedEndContent,
818
- isDisabled: isDisabled,
819
- disabledMessage: disabledMessage,
820
- size: size,
821
- tokenOverflowBehavior: tokenOverflowBehavior,
822
- hasEntriesOnFocus: true,
823
- debounceMs: 0,
824
- status: status,
825
- statusVariant: statusVariant,
826
- onFocus: onFocus,
827
- onBlur: onBlur,
828
- containerXstyle: xstyle,
829
- containerClassName: className,
830
- containerStyle: style,
831
- "data-testid": testId
806
+ children: /*#__PURE__*/_jsx(TokenizerClearAllMessageContext, {
807
+ value: clearAllMessage,
808
+ children: /*#__PURE__*/_jsx(Tokenizer, {
809
+ handleRef: tokenizerRef,
810
+ label: label,
811
+ isLabelHidden: isLabelHidden,
812
+ searchSource: searchSource,
813
+ value: tokenizerValue,
814
+ onChange: handleTokenizerChange,
815
+ renderToken: renderToken,
816
+ renderItem: renderItem,
817
+ maxMenuItems: MAX_BROWSE_MENU_ITEMS,
818
+ menuWidth: menuWidth,
819
+ placeholder: filters.length === 0 ? placeholder : '',
820
+ hasAutoFocus: hasAutoFocus,
821
+ hasClear: hasClear && !isReadOnly,
822
+ startIcon: startIcon,
823
+ endContent: combinedEndContent,
824
+ isDisabled: isDisabled,
825
+ disabledMessage: disabledMessage,
826
+ size: size,
827
+ tokenOverflowBehavior: tokenOverflowBehavior,
828
+ hasEntriesOnFocus: true,
829
+ debounceMs: 0,
830
+ status: status,
831
+ statusVariant: statusVariant,
832
+ onFocus: onFocus,
833
+ onBlur: onBlur,
834
+ containerXstyle: xstyle,
835
+ containerClassName: className,
836
+ containerStyle: style,
837
+ "data-testid": testId
838
+ })
832
839
  })
833
840
  }), popover.render(popoverContent, {
834
841
  placement: 'below',
@@ -1 +1 @@
1
- {"version":3,"file":"Tokenizer.d.ts","sourceRoot":"","sources":["../../src/Tokenizer/Tokenizer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Tokenizer.d.ts","sourceRoot":"","sources":["../../src/Tokenizer/Tokenizer.tsx"],"names":[],"mappings":"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"}
@@ -23,12 +23,13 @@
23
23
  * - /apps/storybook/stories/Tokenizer.stories.tsx
24
24
  * - /packages/cli/assets/templates/blocks/components/Tokenizer/ (showcase blocks)
25
25
  */
26
- import React, { lazy, Suspense, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from 'react';
26
+ import React, { lazy, Suspense, use, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from 'react';
27
27
  import * as stylex from '@stylexjs/stylex';
28
28
  import { BusyIndicatorLaneProvider, createBusyIndicatorLane, useIsBusy } from "../Typeahead/busyIndicatorLane.js";
29
29
  import { TrailingKeyDownContext } from "../Typeahead/trailingKeyDown.js";
30
30
  import { useFieldInputStyling } from "../Field/fieldParts.js";
31
31
  import { useMergedRefs } from "../hooks/useMergedRefs.js";
32
+ import { TokenizerClearAllMessageContext } from "./TokenizerClearAllMessageContext.js";
32
33
  import { useBusyAnnouncement } from "../hooks/useBusyAnnouncement.js";
33
34
  import { useAdaptivePresentation } from "../hooks/useAdaptivePresentation.js";
34
35
  import { useIsHydrating } from "../hooks/useIsHydrating.js";
@@ -832,7 +833,10 @@ export function Tokenizer({
832
833
  }, [value, onChange, isDisabled, announce, t]);
833
834
 
834
835
  // Handle clearing all items
835
- const handleClearAll = useCallback(() => {
836
+ // A composing component that words its own Clear all (PowerSearch) provides
837
+ // its words; otherwise the field's own (component:Tokenizer AR7).
838
+ const clearAllMessage = use(TokenizerClearAllMessageContext) ?? t('@astryx.tokenizer.allRemoved');
839
+ const handleClearAll = useCallback(options => {
836
840
  if (isDisabled || value.length === 0) {
837
841
  return;
838
842
  }
@@ -842,8 +846,13 @@ export function Tokenizer({
842
846
  item: lastItem,
843
847
  type: 'remove'
844
848
  });
849
+ // Focus moves to the input, so the emptied selection is announced, in
850
+ // the sheet's words (WCAG 4.1.3). The sheet announces its own Clear all.
851
+ if (!options?.silent) {
852
+ announce(clearAllMessage);
853
+ }
845
854
  inputRef.current?.focus();
846
- }, [value, onChange, isDisabled]);
855
+ }, [value, onChange, isDisabled, announce, clearAllMessage]);
847
856
 
848
857
  // Handle backspace on empty input — remove last token
849
858
  const handleKeyDown = useCallback(e => {
@@ -1149,7 +1158,9 @@ export function Tokenizer({
1149
1158
  emptySearchText: emptySearchText,
1150
1159
  onAdd: handleAdd,
1151
1160
  onRemove: handleRemove,
1152
- onClearAll: handleClearAll,
1161
+ onClearAll: () => handleClearAll({
1162
+ silent: true
1163
+ }),
1153
1164
  onChangeQuery: handleSheetQueryChange
1154
1165
  })]
1155
1166
  });
@@ -0,0 +1,2 @@
1
+ export declare const TokenizerClearAllMessageContext: import("react").Context<string | null>;
2
+ //# sourceMappingURL=TokenizerClearAllMessageContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenizerClearAllMessageContext.d.ts","sourceRoot":"","sources":["../../src/Tokenizer/TokenizerClearAllMessageContext.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,+BAA+B,wCAE3C,CAAC"}
@@ -0,0 +1,17 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file TokenizerClearAllMessageContext.ts
7
+ * @input None
8
+ * @output A context carrying the words a composing component announces for
9
+ * the field's Clear all
10
+ * @position Internal to Core: not exported from the Tokenizer barrel. A
11
+ * component that composes Tokenizer and words its own Clear all (PowerSearch:
12
+ * "All filters removed") provides it; Tokenizer otherwise announces its own
13
+ * words (component:Tokenizer AR7).
14
+ */
15
+ import { createContext } from 'react';
16
+ export const TokenizerClearAllMessageContext = /*#__PURE__*/createContext(null);
17
+ TokenizerClearAllMessageContext.displayName = 'TokenizerClearAllMessageContext';
@@ -1 +1 @@
1
- {"version":3,"file":"en.generated.d.ts","sourceRoot":"","sources":["../../../src/i18n/generated-locales/en.generated.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,QAAA,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAymBpD,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"en.generated.d.ts","sourceRoot":"","sources":["../../../src/i18n/generated-locales/en.generated.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,QAAA,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA0mBpD,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -288,6 +288,7 @@ const runtimeCatalog = {
288
288
  '@astryx.outline.label': 'Table of contents',
289
289
  '@astryx.powersearch.label': 'Search',
290
290
  '@astryx.powersearch.placeholder': 'Search…',
291
+ '@astryx.powersearch.allFiltersRemoved': 'All filters removed',
291
292
  '@astryx.resizable.collapsed': 'Collapsed',
292
293
  '@astryx.resizable.handle.label': 'Resize handle',
293
294
  '@astryx.selector.placeholder': 'Select…',
@@ -1 +1 @@
1
- {"version":3,"file":"pseudo.generated.d.ts","sourceRoot":"","sources":["../../../src/i18n/generated-locales/pseudo.generated.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,QAAA,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAonBpD,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"pseudo.generated.d.ts","sourceRoot":"","sources":["../../../src/i18n/generated-locales/pseudo.generated.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,QAAA,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqnBpD,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -288,6 +288,7 @@ const runtimeCatalog = {
288
288
  '@astryx.outline.label': '⟦Ţàƀłé óƒ çóñţéñţš⟧',
289
289
  '@astryx.powersearch.label': '⟦Šéàřçĥ⟧',
290
290
  '@astryx.powersearch.placeholder': '⟦Šéàřçĥ…⟧',
291
+ '@astryx.powersearch.allFiltersRemoved': '⟦Àłł ƒíłţéřš řéɱóṽéð⟧',
291
292
  '@astryx.resizable.collapsed': '⟦Çółłàþšéð⟧',
292
293
  '@astryx.resizable.handle.label': '⟦Řéšížé ĥàñðłé⟧',
293
294
  '@astryx.selector.placeholder': '⟦Šéłéçţ…⟧',
package/locales/en.json CHANGED
@@ -1123,6 +1123,10 @@
1123
1123
  "defaultMessage": "Search…",
1124
1124
  "description": "Grey placeholder text inside the empty PowerSearch input. Imperative verb; trailing `…` is one character."
1125
1125
  },
1126
+ "@astryx.powersearch.allFiltersRemoved": {
1127
+ "defaultMessage": "All filters removed",
1128
+ "description": "Screen-reader announcement after a PowerSearch field's Clear all removes every filter. Distinct from @astryx.tokenizer.allRemoved: PowerSearch words its items as filters."
1129
+ },
1126
1130
  "@astryx.resizable.collapsed": {
1127
1131
  "defaultMessage": "Collapsed",
1128
1132
  "description": "aria-valuetext for the Resizable drag handle (role=separator) while its panel is collapsed to zero size. Replaces the numeric value announcement, since the numeric value is clamped to the minimum while collapsed. Adjective describing the panel state, not a verb/command."
@@ -1741,7 +1745,7 @@
1741
1745
  },
1742
1746
  "@astryx.tokenizer.allRemoved": {
1743
1747
  "defaultMessage": "All items removed",
1744
- "description": "Polite screen-reader announcement after a confirmed Clear all in a Tokenizer's bottom sheet has removed every selected item. Live-region text, never shown on screen."
1748
+ "description": "Polite screen-reader announcement after a Tokenizer's Clear all has removed every selected item: the inline Clear all button, or a confirmed Clear all in the bottom sheet. Live-region text, never shown on screen."
1745
1749
  },
1746
1750
  "@astryx.tokenizer.selectedCount": {
1747
1751
  "defaultMessage": "{count, number} selected",
@@ -842,6 +842,9 @@
842
842
  "@astryx.powersearch.placeholder": {
843
843
  "defaultMessage": "⟦Šéàřçĥ…⟧"
844
844
  },
845
+ "@astryx.powersearch.allFiltersRemoved": {
846
+ "defaultMessage": "⟦Àłł ƒíłţéřš řéɱóṽéð⟧"
847
+ },
845
848
  "@astryx.resizable.collapsed": {
846
849
  "defaultMessage": "⟦Çółłàþšéð⟧"
847
850
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/core",
3
- "version": "0.7.6-canary.19ae6fc",
3
+ "version": "0.7.6-canary.25f61ea",
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",
@@ -847,9 +847,9 @@
847
847
  "react-dom": ">=19.0.0"
848
848
  },
849
849
  "devDependencies": {
850
- "@astryxdesign/a11y-spec": "0.7.6-canary.19ae6fc",
851
- "@astryxdesign/cli": "0.7.6-canary.19ae6fc",
852
- "@astryxdesign/interaction-harness": "0.7.6-canary.19ae6fc",
850
+ "@astryxdesign/a11y-spec": "0.7.6-canary.25f61ea",
851
+ "@astryxdesign/cli": "0.7.6-canary.25f61ea",
852
+ "@astryxdesign/interaction-harness": "0.7.6-canary.25f61ea",
853
853
  "@babel/cli": "^7.29.7",
854
854
  "@babel/core": "^7.29.7",
855
855
  "@babel/preset-react": "^7.29.7",
@@ -39,9 +39,24 @@ async function mount(
39
39
  );
40
40
  await expect(copyButton(page)).toBeVisible();
41
41
  if (Object.keys(args).length > 0) {
42
+ // Measure only the story as these args render it. Storybook emits
43
+ // `storyRendered` for the story's own first render too, shortly after it
44
+ // is visible, so the update waits for that render to finish first; the
45
+ // `storyRendered` it then waits for is the re-render with the args, and a
46
+ // re-render that never comes fails here instead of being measured. Each
47
+ // arg the cases pass is also checked in the page, so a slow runner can
48
+ // never measure the default story in its place.
49
+ await page.waitForFunction(
50
+ () =>
51
+ (
52
+ window as unknown as {
53
+ __STORYBOOK_PREVIEW__?: {currentRender?: {phase?: string}};
54
+ }
55
+ ).__STORYBOOK_PREVIEW__?.currentRender?.phase === 'finished',
56
+ );
42
57
  await page.evaluate(
43
58
  async ({storyId, updatedArgs}) =>
44
- new Promise<void>(resolve => {
59
+ new Promise<void>((resolve, reject) => {
45
60
  const channel = (
46
61
  window as unknown as {
47
62
  __STORYBOOK_ADDONS_CHANNEL__: {
@@ -50,12 +65,32 @@ async function mount(
50
65
  };
51
66
  }
52
67
  ).__STORYBOOK_ADDONS_CHANNEL__;
53
- channel.once('storyRendered', () => resolve());
68
+ const timer = setTimeout(
69
+ () =>
70
+ reject(new Error('the story did not re-render with its args')),
71
+ 20_000,
72
+ );
73
+ channel.once('storyRendered', () => {
74
+ clearTimeout(timer);
75
+ resolve();
76
+ });
54
77
  channel.emit('updateStoryArgs', {storyId, updatedArgs});
55
- setTimeout(resolve, 1500);
56
78
  }),
57
79
  {storyId: STORY, updatedArgs: args},
58
80
  );
81
+ if (typeof args.code === 'string') {
82
+ await expect(
83
+ page.locator('pre.astryx-code-block [data-line]'),
84
+ ).toHaveCount(args.code.split('\n').length);
85
+ }
86
+ if (typeof args.title === 'string') {
87
+ await expect(page.getByText(args.title, {exact: true})).toBeVisible();
88
+ }
89
+ if (args.isWrapped === true) {
90
+ await expect(
91
+ page.locator('pre.astryx-code-block [data-line="1"]'),
92
+ ).toHaveCSS('white-space', 'pre-wrap');
93
+ }
59
94
  }
60
95
  }
61
96
 
@@ -2233,6 +2233,16 @@ describe('a layer that keeps its anchor clear (spec:AST-059 FR4)', () => {
2233
2233
 
2234
2234
  const TRIGGER = rect(300, 400, 30, 100);
2235
2235
 
2236
+ /** Let the next animation frame run, as the browser does before it paints. */
2237
+ async function nextFrame() {
2238
+ await act(
2239
+ async () =>
2240
+ new Promise<void>(resolve => {
2241
+ requestAnimationFrame(() => resolve());
2242
+ }),
2243
+ );
2244
+ }
2245
+
2236
2246
  /** Give the document a 720px-tall viewport for the duration of a test. */
2237
2247
  function viewport720() {
2238
2248
  return vi
@@ -2540,10 +2550,74 @@ describe('a layer that keeps its anchor clear (spec:AST-059 FR4)', () => {
2540
2550
  placeBoxes(TRIGGER, rect(16, 350, 688, 300));
2541
2551
  const before = observers.length;
2542
2552
  act(() => move?.callback([{intersectionRatio: 0.4}]));
2543
- expect(layerEl).toHaveAttribute('data-astryx-clears-anchor');
2544
- // Re-armed on the new rect.
2553
+ // Re-armed on the new rect at once; read in the next frame.
2545
2554
  expect(move?.disconnected).toBe(true);
2546
2555
  expect(observers.length).toBe(before + 1);
2556
+ await nextFrame();
2557
+ expect(layerEl).toHaveAttribute('data-astryx-clears-anchor');
2558
+ vi.unstubAllGlobals();
2559
+ });
2560
+
2561
+ it('reads a move its observer reports after paint in the next frame, before that frame paints, never in the reporting task', async () => {
2562
+ // Placing a layer afresh in the task right after the frame that scrolled
2563
+ // its anchor lets Chromium lay it out for one more frame against the
2564
+ // anchor's position before the scroll, over the anchor (spec:AST-059 FR8:
2565
+ // clear from the second painted frame). The read waits for the next
2566
+ // frame, before it paints, and several reports in one frame share it.
2567
+ const observers: {
2568
+ callback: (entries: {intersectionRatio: number}[]) => void;
2569
+ options: IntersectionObserverInit;
2570
+ }[] = [];
2571
+ vi.stubGlobal(
2572
+ 'IntersectionObserver',
2573
+ class {
2574
+ constructor(
2575
+ callback: (entries: {intersectionRatio: number}[]) => void,
2576
+ options: IntersectionObserverInit,
2577
+ ) {
2578
+ observers.push({callback, options});
2579
+ }
2580
+ observe() {}
2581
+ disconnect() {}
2582
+ unobserve() {}
2583
+ },
2584
+ );
2585
+ const latestMove = () =>
2586
+ observers.filter(o => o.options?.rootMargin != null).at(-1);
2587
+ const user = userEvent.setup();
2588
+ const spy = placeBoxes(TRIGGER, rect(200, 350, 100, 200));
2589
+ const {container, getByText} = render(<ClearHarness />);
2590
+ await user.click(getByText('Trigger'));
2591
+ const layerEl = container.querySelector('[popover]') as HTMLElement;
2592
+ act(() => latestMove()?.callback([{intersectionRatio: 1}]));
2593
+ await nextFrame();
2594
+ expect(layerEl).not.toHaveAttribute('data-astryx-clears-anchor');
2595
+
2596
+ // A scroll carries the trigger under the layer; the observer reports it
2597
+ // after the frame that painted the move, twice before the next frame.
2598
+ spy.mockRestore();
2599
+ const reads = vi.fn();
2600
+ const covering = rect(16, 350, 688, 300);
2601
+ vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(
2602
+ function (this: HTMLElement) {
2603
+ if (!this.hasAttribute('popover')) {
2604
+ return TRIGGER;
2605
+ }
2606
+ reads();
2607
+ return covering;
2608
+ },
2609
+ );
2610
+ act(() => {
2611
+ latestMove()?.callback([{intersectionRatio: 0.4}]);
2612
+ latestMove()?.callback([{intersectionRatio: 0.6}]);
2613
+ });
2614
+ expect(layerEl).not.toHaveAttribute('data-astryx-clears-anchor');
2615
+ expect(reads).not.toHaveBeenCalled();
2616
+
2617
+ // The next frame reads it once, switches it, and places it afresh.
2618
+ await nextFrame();
2619
+ expect(layerEl).toHaveAttribute('data-astryx-clears-anchor');
2620
+ expect(reads).toHaveBeenCalled();
2547
2621
  vi.unstubAllGlobals();
2548
2622
  });
2549
2623
 
@@ -2595,9 +2669,10 @@ describe('a layer that keeps its anchor clear (spec:AST-059 FR4)', () => {
2595
2669
  spy = placeBoxes(TRIGGER, rect(16, 350, 688, 300));
2596
2670
  let armed = moves().length;
2597
2671
  act(() => latest().callback([{intersectionRatio: 0.7}]));
2598
- expect(layerEl).toHaveAttribute('data-astryx-clears-anchor');
2599
2672
  expect(moves().length).toBe(armed + 1);
2600
2673
  expect(latest().options.threshold).toBe(0.7);
2674
+ await nextFrame();
2675
+ expect(layerEl).toHaveAttribute('data-astryx-clears-anchor');
2601
2676
 
2602
2677
  // A first report of 0 for a trigger that moved since the observer read
2603
2678
  // its rectangle re-arms at once on the new rectangle.
@@ -627,8 +627,9 @@ function failsAnchor(layer: HTMLElement, anchor: HTMLElement): boolean {
627
627
  * every show and remount, and while the layer is open whenever its size, its
628
628
  * content's size, its anchor's size or the viewport changes; each of those
629
629
  * reads lands before the next frame paints. A same-size anchor that moves on
630
- * screen, in layout or because something scrolled, is read after paint, from
631
- * the anchor-move observer (FR8). Scroll events are not read: during one the
630
+ * screen, in layout or because something scrolled, is reported after paint by
631
+ * the anchor-move observer and read in the next frame, before it paints (FR8).
632
+ * Scroll events are not read: during one the
632
633
  * anchor has moved but the layer anchored to it not yet, so the read would
633
634
  * compare a moved anchor with a stale layer. Once switched, a layer keeps its
634
635
  * anchor-clear list until it next opens, so it never jumps back over its
@@ -779,9 +780,16 @@ function observeMove(element: HTMLElement, onMove: () => void): () => void {
779
780
  * range (spec:AST-059 FR4, FR8): the layer, its content or the anchor
780
781
  * resizing (ResizeObserver, before paint), the viewport resizing (before
781
782
  * paint), and the anchor moving on screen without resizing, in layout or by a
782
- * scroll (`observeMove`, after paint). Each calls `keepAnchorClear` for this layer and anchor while
783
- * `isCurrent` says they are still the live pair. Returns the function that
784
- * removes every observer and listener it added.
783
+ * scroll (`observeMove`, reported after paint). Each calls `keepAnchorClear`
784
+ * for this layer and anchor while `isCurrent` says they are still the live
785
+ * pair. A move is read in the next animation frame, not in the task that
786
+ * reports it: placing the layer afresh there, right after the frame that
787
+ * scrolled its anchor, lets Chromium lay it out for one more frame against
788
+ * the anchor's position before the scroll, over the anchor, so the second
789
+ * painted frame would still cover it. Read in the next frame, before it
790
+ * paints, the fresh placement uses the moved anchor. Reports in one frame
791
+ * share one read. Returns the function that removes every observer and
792
+ * listener it added.
785
793
  */
786
794
  function observeAnchorClearance(
787
795
  layer: HTMLElement,
@@ -806,11 +814,24 @@ function observeAnchorClearance(
806
814
  }
807
815
  observer.observe(anchor);
808
816
  }
809
- const stopObservingMove = observeMove(anchor, reread);
817
+ let moveFrame: number | null = null;
818
+ const stopObservingMove = observeMove(anchor, () => {
819
+ if (moveFrame !== null) {
820
+ return;
821
+ }
822
+ moveFrame = view.requestAnimationFrame(() => {
823
+ moveFrame = null;
824
+ reread();
825
+ });
826
+ });
810
827
  view.addEventListener('resize', reread);
811
828
  return () => {
812
829
  observer?.disconnect();
813
830
  stopObservingMove();
831
+ if (moveFrame !== null) {
832
+ view.cancelAnimationFrame(moveFrame);
833
+ moveFrame = null;
834
+ }
814
835
  view.removeEventListener('resize', reread);
815
836
  };
816
837
  }
@@ -476,6 +476,30 @@ describe('PowerSearch', () => {
476
476
  });
477
477
  });
478
478
 
479
+ it('announces Clear all in its own words, "All filters removed" (component:PowerSearch; Tokenizer AR7)', async () => {
480
+ const onChange = vi.fn();
481
+ render(
482
+ <PowerSearch
483
+ config={config}
484
+ filters={[
485
+ {
486
+ field: 'status',
487
+ operator: 'is',
488
+ value: {type: 'enum', value: 'open'},
489
+ },
490
+ ]}
491
+ onChange={onChange}
492
+ />,
493
+ );
494
+ fireEvent.click(screen.getByRole('button', {name: 'Clear all'}));
495
+ expect(onChange).toHaveBeenCalledWith([], 'remove', 0);
496
+ await waitFor(() => {
497
+ expect(politeRegion()).toHaveTextContent('All filters removed');
498
+ });
499
+ // Not the Tokenizer's own words.
500
+ expect(politeRegion()).not.toHaveTextContent('All items removed');
501
+ });
502
+
479
503
  it('does not announce the result count present on mount under StrictMode, only a later change', async () => {
480
504
  // StrictMode replays the mount effects: a mounted-flag guard is reset by
481
505
  // the replay and speaks the initial count unprompted; comparing against
@@ -30,6 +30,7 @@ import {
30
30
  type TokenizerHandle,
31
31
  type TokenizerOverflowBehavior,
32
32
  } from '../Tokenizer';
33
+ import {TokenizerClearAllMessageContext} from '../Tokenizer/TokenizerClearAllMessageContext';
33
34
  import {TypeaheadItem} from '../Typeahead/TypeaheadItem';
34
35
  import {Token} from '../Token';
35
36
  import {Avatar} from '../Avatar';
@@ -1062,6 +1063,12 @@ export function PowerSearch({
1062
1063
  // replay (which resets a flag and would speak the count present on mount
1063
1064
  // unprompted) announces nothing until the count changes.
1064
1065
  const announce = useAnnounce();
1066
+ // PowerSearch words its Clear all as its record does; the field announces
1067
+ // these words instead of Tokenizer's (component:Tokenizer AR7).
1068
+ const clearAllMessage = useMemo(
1069
+ () => t('@astryx.powersearch.allFiltersRemoved'),
1070
+ [t],
1071
+ );
1065
1072
  const announcedResultCountRef = useRef(resultCountText);
1066
1073
  useEffect(() => {
1067
1074
  if (resultCountText === announcedResultCountRef.current) {
@@ -1099,37 +1106,41 @@ export function PowerSearch({
1099
1106
  popover.triggerRef as React.Ref<HTMLDivElement>,
1100
1107
  )}
1101
1108
  {...themeProps('power-search')}>
1102
- <Tokenizer
1103
- handleRef={tokenizerRef}
1104
- label={label}
1105
- isLabelHidden={isLabelHidden}
1106
- searchSource={searchSource}
1107
- value={tokenizerValue}
1108
- onChange={handleTokenizerChange}
1109
- renderToken={renderToken}
1110
- renderItem={renderItem}
1111
- maxMenuItems={MAX_BROWSE_MENU_ITEMS}
1112
- menuWidth={menuWidth}
1113
- placeholder={filters.length === 0 ? placeholder : ''}
1114
- hasAutoFocus={hasAutoFocus}
1115
- hasClear={hasClear && !isReadOnly}
1116
- startIcon={startIcon}
1117
- endContent={combinedEndContent}
1118
- isDisabled={isDisabled}
1119
- disabledMessage={disabledMessage}
1120
- size={size}
1121
- tokenOverflowBehavior={tokenOverflowBehavior}
1122
- hasEntriesOnFocus
1123
- debounceMs={0}
1124
- status={status}
1125
- statusVariant={statusVariant}
1126
- onFocus={onFocus}
1127
- onBlur={onBlur}
1128
- containerXstyle={xstyle}
1129
- containerClassName={className}
1130
- containerStyle={style}
1131
- data-testid={testId}
1132
- />
1109
+ {/* The field's Clear all announces PowerSearch's own words
1110
+ (component:PowerSearch; component:Tokenizer AR7). */}
1111
+ <TokenizerClearAllMessageContext value={clearAllMessage}>
1112
+ <Tokenizer
1113
+ handleRef={tokenizerRef}
1114
+ label={label}
1115
+ isLabelHidden={isLabelHidden}
1116
+ searchSource={searchSource}
1117
+ value={tokenizerValue}
1118
+ onChange={handleTokenizerChange}
1119
+ renderToken={renderToken}
1120
+ renderItem={renderItem}
1121
+ maxMenuItems={MAX_BROWSE_MENU_ITEMS}
1122
+ menuWidth={menuWidth}
1123
+ placeholder={filters.length === 0 ? placeholder : ''}
1124
+ hasAutoFocus={hasAutoFocus}
1125
+ hasClear={hasClear && !isReadOnly}
1126
+ startIcon={startIcon}
1127
+ endContent={combinedEndContent}
1128
+ isDisabled={isDisabled}
1129
+ disabledMessage={disabledMessage}
1130
+ size={size}
1131
+ tokenOverflowBehavior={tokenOverflowBehavior}
1132
+ hasEntriesOnFocus
1133
+ debounceMs={0}
1134
+ status={status}
1135
+ statusVariant={statusVariant}
1136
+ onFocus={onFocus}
1137
+ onBlur={onBlur}
1138
+ containerXstyle={xstyle}
1139
+ containerClassName={className}
1140
+ containerStyle={style}
1141
+ data-testid={testId}
1142
+ />
1143
+ </TokenizerClearAllMessageContext>
1133
1144
  </div>
1134
1145
  {popover.render(popoverContent, {
1135
1146
  placement: 'below',
@@ -16,6 +16,7 @@ verified_by:
16
16
  packages/core/src/Tokenizer/Tokenizer.ssr.test.tsx,
17
17
  packages/core/src/Tokenizer/TokenizerBottomSheet.test.tsx,
18
18
  packages/core/src/Tokenizer/Tokenizer.props-type.test.tsx,
19
+ packages/core/src/PowerSearch/PowerSearch.test.tsx,
19
20
  packages/core/src/internal/tokenizer-presentation.test.ts,
20
21
  packages/core/src/Typeahead/Typeahead.test.tsx,
21
22
  packages/core/src/Layer/useLayer.test.tsx,
@@ -325,7 +326,7 @@ presentation, which has no inline combobox, suggestion menu, or end lane.
325
326
 
326
327
  ## Accessibility contract
327
328
 
328
- AR1–AR5 describe the `popover` presentation; AR6 describes the sheet
329
+ AR1–AR5 and AR7 describe the `popover` presentation; AR6 describes the sheet
329
330
  presentation.
330
331
 
331
332
  - **AR1 — Modality equivalence.** The same input or token/control produces the
@@ -382,6 +383,16 @@ presentation.
382
383
  through `spec:AST-020/FR1`). After the sheet closes focus returns to the
383
384
  trigger and the field's announcements follow the same FR14 route from
384
385
  wherever the field renders.
386
+ - **AR7 — Clear all is announced.** In the `popover` presentation, the field's
387
+ Clear all, which empties every token at once and moves focus to the input,
388
+ announces "All items removed" (`@astryx.tokenizer.allRemoved`, the words of
389
+ the sheet's Clear all in AR6) the same way the field announces a token added
390
+ or removed: politely, through the shared channel (`spec:AST-086/FR2`, FR5).
391
+ It is announced once per Clear all (`spec:AST-086/FR6`); a Clear all
392
+ confirmed in the sheet is announced by the sheet alone (AR6), and the field
393
+ stays silent for it (WCAG 2.2 SC 4.1.3 through `spec:AST-020/FR9`). A
394
+ component that composes Tokenizer and words its own Clear all announces its
395
+ own words instead (`component:PowerSearch`).
385
396
 
386
397
  ## Design relationships
387
398
 
@@ -462,6 +473,7 @@ become Tokenizer policy.
462
473
  | FR13–FR15, AR5 | Focused Tokenizer/BaseTypeahead tests and exact-head Chromium interaction evidence | pointer/keyboard commit, first/middle/last choice, rejected/deferred control, loading, disabled, max, exhaustion, Escape, Tab, outside focus | Selected values remain available, order changes, focus leaves the combobox, active descendant names no option, stale work reopens, or a terminal state stays interactive. | `audit:Tokenizer/multi-select` |
463
474
  | PR1–PR5 | Instrumented reads, pending-frame cleanup tests, and first-frame Chromium capture | open, close before frame, reopen opposite side, resize while open | Render-time/repeated reads, observer/pointer tracking, uncancelled frame, or first-frame jump occurs. | `audit:Tokenizer/performance` |
464
475
  | AR1–AR5 | Existing interaction suites plus focused pointer/keyboard/browser checks; native-disabled assertions pending conformance (`spec:AST-085`) | add/edit, focus/refocus, overflow, disabled, dismissal, retained bootstrap selection | Modality changes placement, semantics/focus regress, active descendant becomes invalid, or visible placement is wrong before fallback. | `audit:Tokenizer/accessibility` |
476
+ | AR7 | Focused Tokenizer tests, inline and sheet, and the PowerSearch test | inline Clear all; a localized catalog; a Clear all confirmed in the sheet counted across every live region; PowerSearch's own words | The inline Clear all is silent, speaks words other than its catalog message, a sheet Clear all is announced twice, or PowerSearch speaks Tokenizer's words. | `audit:Tokenizer/accessibility` |
465
477
  | FR16–FR24, AR6, PR6 | Focused Tokenizer sheet tests with the compact-touch query stubbed both ways, a server-render-then-hydrate test, plus real-engine touch evidence | default popover, forced sheet, adaptive both ways, `adaptive` server-rendered as the combobox and hydrated without a recoverable error, then the trigger on a coarse-pointer client with the body still active, with `hasAutoFocus` the autofocused input's focus on the trigger after the swap and no `onBlur`/`onFocus` reported, the trigger's box against the hydrating field's at 390 and 414px, base props and ref on the trigger, the trigger's description in the inline input's order — a disabled trigger with `disabledMessage`, `description` and a status exposing the caller's ids, the description, the status message and the reason before the count and the tokens —, inert-prop warning, pending search, in-sheet announcements of toggles, busy start, result count and the rendered empty outcome, the confirmed Clear all announced once the confirmation has closed, a below-threshold query, a rejecting source, focus on the panel at open, the query cleared by a swipe, scrim, or Escape close, focus after a presentation change with the sheet closed, focus after the create row, focus after a confirmed Clear all, a spent Clear all focusable with `aria-disabled` and no action, focus after a settled search removes the focused row, no busy feedback or busy-start announcement on open with an instant source, only the search field — no list, no message, no announcement — for an empty query with nothing to list (a search-only source, nothing selected), and the list once a query finds items, element counts under a fine and a coarse pointer at rest and open (Chromium, Experimental stories: adaptive 154/120 at rest — the closed trigger in place of the inline field, a different element that stays under the fine count — and 157/165 open at the query "e" with no footer, the coarse total reconciled as rest − the re-hosted surfaces host + the dialog of 47 by part, forced sheet 114/114 and 165/165; the two owner exceptions DEC-3 records beyond the FR60 allowance, with before and after counts), no confirmation dialog in the DOM while none is pending, the query kept across a disabled-forced close, `aria-setsize`/`aria-posinset` on each row's `listitem` and the next batch on focus reaching the last rendered row, open/toggle/reopen order, create, batches, limit, Clear all, disabled while open, focus return | A field without `experimental` changes, a toggle moves a row, Clear all skips confirmation, the limit disables checked rows, or the popover bundle carries the sheet. | `audit:Tokenizer/sheet` |
466
478
  | Record structure | `scripts/check-knowledge.mjs` | Template-v3 current record and current-only frontmatter links | Invalid metadata, authority, or relationship references fail repository validation. | `audit:Tokenizer/knowledge` |
467
479
 
@@ -1403,6 +1403,33 @@ describe('Tokenizer', () => {
1403
1403
  });
1404
1404
  });
1405
1405
  describe('announcements', () => {
1406
+ it("announces Clear all politely, in the sheet's words", async () => {
1407
+ const onChange = vi.fn();
1408
+ render(
1409
+ <InternationalizationProvider
1410
+ locale="fr"
1411
+ overrides={{
1412
+ fr: {'@astryx.tokenizer.allRemoved': 'Tout a été retiré'},
1413
+ }}>
1414
+ <Tokenizer
1415
+ label="Members"
1416
+ searchSource={userSource}
1417
+ value={[users[0], users[1]]}
1418
+ onChange={onChange}
1419
+ hasClear
1420
+ />
1421
+ </InternationalizationProvider>,
1422
+ );
1423
+ fireEvent.click(screen.getByRole('button', {name: /clear/i}));
1424
+ expect(onChange).toHaveBeenCalledWith(
1425
+ [],
1426
+ expect.objectContaining({type: 'remove'}),
1427
+ );
1428
+ await waitFor(() => {
1429
+ expect(politeRegion()?.textContent).toBe('Tout a été retiré');
1430
+ });
1431
+ });
1432
+
1406
1433
  it('announces removal politely on Backspace with an empty input', async () => {
1407
1434
  const onChange = vi.fn();
1408
1435
  render(
@@ -27,6 +27,7 @@
27
27
  import React, {
28
28
  lazy,
29
29
  Suspense,
30
+ use,
30
31
  useCallback,
31
32
  useEffect,
32
33
  useId,
@@ -51,6 +52,7 @@ import {
51
52
  useFieldInputStyling,
52
53
  } from '../Field/fieldParts';
53
54
  import {useMergedRefs} from '../hooks/useMergedRefs';
55
+ import {TokenizerClearAllMessageContext} from './TokenizerClearAllMessageContext';
54
56
  import {useBusyAnnouncement} from '../hooks/useBusyAnnouncement';
55
57
  import {useAdaptivePresentation} from '../hooks/useAdaptivePresentation';
56
58
  import {useIsHydrating} from '../hooks/useIsHydrating';
@@ -1223,15 +1225,27 @@ export function Tokenizer<T extends SearchableItem>({
1223
1225
  );
1224
1226
 
1225
1227
  // Handle clearing all items
1226
- const handleClearAll = useCallback(() => {
1227
- if (isDisabled || value.length === 0) {
1228
- return;
1229
- }
1230
- // Report the last item as removed (convention)
1231
- const lastItem = value[value.length - 1];
1232
- onChange([], {item: lastItem, type: 'remove'});
1233
- inputRef.current?.focus();
1234
- }, [value, onChange, isDisabled]);
1228
+ // A composing component that words its own Clear all (PowerSearch) provides
1229
+ // its words; otherwise the field's own (component:Tokenizer AR7).
1230
+ const clearAllMessage =
1231
+ use(TokenizerClearAllMessageContext) ?? t('@astryx.tokenizer.allRemoved');
1232
+ const handleClearAll = useCallback(
1233
+ (options?: {silent?: boolean}) => {
1234
+ if (isDisabled || value.length === 0) {
1235
+ return;
1236
+ }
1237
+ // Report the last item as removed (convention)
1238
+ const lastItem = value[value.length - 1];
1239
+ onChange([], {item: lastItem, type: 'remove'});
1240
+ // Focus moves to the input, so the emptied selection is announced, in
1241
+ // the sheet's words (WCAG 4.1.3). The sheet announces its own Clear all.
1242
+ if (!options?.silent) {
1243
+ announce(clearAllMessage);
1244
+ }
1245
+ inputRef.current?.focus();
1246
+ },
1247
+ [value, onChange, isDisabled, announce, clearAllMessage],
1248
+ );
1235
1249
 
1236
1250
  // Handle backspace on empty input — remove last token
1237
1251
  const handleKeyDown = useCallback(
@@ -1636,7 +1650,7 @@ export function Tokenizer<T extends SearchableItem>({
1636
1650
  emptySearchText={emptySearchText}
1637
1651
  onAdd={handleAdd}
1638
1652
  onRemove={handleRemove}
1639
- onClearAll={handleClearAll}
1653
+ onClearAll={() => handleClearAll({silent: true})}
1640
1654
  onChangeQuery={handleSheetQueryChange}
1641
1655
  />
1642
1656
  )}
@@ -808,6 +808,14 @@ describe('Tokenizer sheet presentation (single-sheet touch flow)', () => {
808
808
  ),
809
809
  ).toHaveTextContent('All items removed'),
810
810
  );
811
+ // Exactly one live region says it: the field's own region stays silent
812
+ // for a Clear all the sheet already announced.
813
+ const saying = Array.from(
814
+ document.querySelectorAll(
815
+ '[aria-live], [role="status"], [role="alert"]',
816
+ ),
817
+ ).filter(region => region.textContent?.includes('All items removed'));
818
+ expect(saying).toHaveLength(1);
811
819
  } finally {
812
820
  modalSpy.mockRestore();
813
821
  }
@@ -0,0 +1,21 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ 'use client';
4
+
5
+ /**
6
+ * @file TokenizerClearAllMessageContext.ts
7
+ * @input None
8
+ * @output A context carrying the words a composing component announces for
9
+ * the field's Clear all
10
+ * @position Internal to Core: not exported from the Tokenizer barrel. A
11
+ * component that composes Tokenizer and words its own Clear all (PowerSearch:
12
+ * "All filters removed") provides it; Tokenizer otherwise announces its own
13
+ * words (component:Tokenizer AR7).
14
+ */
15
+
16
+ import {createContext} from 'react';
17
+
18
+ export const TokenizerClearAllMessageContext = createContext<string | null>(
19
+ null,
20
+ );
21
+ TokenizerClearAllMessageContext.displayName = 'TokenizerClearAllMessageContext';
@@ -314,6 +314,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
314
314
  '@astryx.outline.label': 'Table of contents',
315
315
  '@astryx.powersearch.label': 'Search',
316
316
  '@astryx.powersearch.placeholder': 'Search…',
317
+ '@astryx.powersearch.allFiltersRemoved': 'All filters removed',
317
318
  '@astryx.resizable.collapsed': 'Collapsed',
318
319
  '@astryx.resizable.handle.label': 'Resize handle',
319
320
  '@astryx.selector.placeholder': 'Select…',
@@ -321,6 +321,7 @@ const runtimeCatalog: Readonly<Record<string, string>> = {
321
321
  '@astryx.outline.label': '⟦Ţàƀłé óƒ çóñţéñţš⟧',
322
322
  '@astryx.powersearch.label': '⟦Šéàřçĥ⟧',
323
323
  '@astryx.powersearch.placeholder': '⟦Šéàřçĥ…⟧',
324
+ '@astryx.powersearch.allFiltersRemoved': '⟦Àłł ƒíłţéřš řéɱóṽéð⟧',
324
325
  '@astryx.resizable.collapsed': '⟦Çółłàþšéð⟧',
325
326
  '@astryx.resizable.handle.label': '⟦Řéšížé ĥàñðłé⟧',
326
327
  '@astryx.selector.placeholder': '⟦Šéłéçţ…⟧',
@@ -19,7 +19,8 @@ so each keeps its measured value; no shipped component reads the roles yet.
19
19
  | `uikit/timing.ts` | Non-spring clocks and curves: press highlight, Core Animation's named curves, keyboard, Reduced Motion cross-fade. |
20
20
  | `uikit/navigation.ts` | UINavigationController: push velocity, parallax, dimming, transition clock, interactive pop finish rule. |
21
21
  | `uikit/gestures.ts` | Gesture recognizer thresholds, the velocity filter and haptic stand-ins, as `{value, unit}` entries (sources in JSDoc). |
22
- | `roles.ts` | Motion roles: each spring, duration and curve of a registered table, named by its path, and its CSS variable. |
22
+ | `roles.ts` | The internal motion roles: the springs, durations and curves `spec:AST-094` FR4 names, registered by the shared tables, and a role's CSS form read back. |
23
+ | `roleValue.ts` | A role's CSS form, written: a spring's duration and bounce, a duration in ms, a curve's `cubic-bezier()`. |
23
24
  | `resolveMotion.ts` | A role's value: its table value, as the per-role tables stay internal (`spec:AST-094` DEC-15). |
24
25
  | `MEASUREMENTS.md` | The UIKit reference captures each unconfirmed value still needs. |
25
26
  | `spring.ts` | Closed-form damped spring, every Apple parameterization, settling duration, a spring chasing a moving target. |
@@ -2,10 +2,11 @@
2
2
 
3
3
  /**
4
4
  * @file roles.test.ts
5
- * @input roles.ts and the shared UIKit tables (registered on import)
6
- * @output Proof that each spring, duration and curve of a registered table is
7
- * a role named by its path, that non-motion values are not, and that a
8
- * role's CSS value reads back as the same motion
5
+ * @input roles.ts and the shared UIKit tables (registered by this test)
6
+ * @output Proof that the registered roles are exactly the ones spec:AST-094
7
+ * FR4 names, each by its table path; that every other table value, a
8
+ * component's measured spring included, is no role; and that a role's CSS
9
+ * value reads back as the same motion
9
10
  * @position Testing for /packages/core/src/motion/roles.ts
10
11
  */
11
12