@astryxdesign/lab 0.7.5-canary.cd53c2d → 0.7.5-canary.d26cdf3
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/ListInput/ListInput.d.ts.map +1 -1
- package/dist/ListInput/ListInput.js +19 -0
- package/dist/TransferList/TransferList.d.ts.map +1 -1
- package/dist/TransferList/TransferList.js +20 -0
- package/dist/lab.css +1 -1
- package/dist/reorderStyles.d.ts +2 -2
- package/dist/reorderStyles.d.ts.map +1 -1
- package/dist/reorderStyles.js +2 -2
- package/package.json +7 -7
- package/src/Chat/__tests__/ChatTypingIndicator.a11y.chromium.spec.ts +1 -0
- package/src/CircularProgress/__tests__/CircularProgress.a11y.chromium.spec.ts +1 -0
- package/src/Comments/Comments.test.tsx +10 -4
- package/src/ListInput/ListInput.test.tsx +43 -3
- package/src/ListInput/ListInput.tsx +23 -0
- package/src/MobileTokenizer/MobileTokenizer.test.tsx +12 -3
- package/src/TransferList/TransferList.composition.test.tsx +4 -4
- package/src/TransferList/TransferList.test.tsx +36 -0
- package/src/TransferList/TransferList.tsx +24 -0
- package/src/TransferList/TransferListSelector.test.tsx +4 -4
- package/src/__tests__/ReorderGripPinchZoom.a11y.chromium.spec.ts +178 -0
- package/src/reorderStyles.ts +7 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListInput.d.ts","sourceRoot":"","sources":["../../src/ListInput/ListInput.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ListInput.d.ts","sourceRoot":"","sources":["../../src/ListInput/ListInput.tsx"],"names":[],"mappings":"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"}
|
|
@@ -476,6 +476,25 @@ export function ListInput({
|
|
|
476
476
|
announce(t('@astryx.listInput.announceReorderCancelled'));
|
|
477
477
|
}
|
|
478
478
|
}, [isDisabled, isLoading, announce, t]);
|
|
479
|
+
|
|
480
|
+
// A second pointer that lands anywhere during a pointer reorder (the second
|
|
481
|
+
// finger of a pinch) cancels the reorder. A pinch whose first finger slid
|
|
482
|
+
// first zooms without a pointercancel, so the drag would otherwise commit on
|
|
483
|
+
// release and drop the row at the end of the list.
|
|
484
|
+
useEffect(() => {
|
|
485
|
+
if (!isPointerReorderActive) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const onPointerDown = event => {
|
|
489
|
+
const activeState = reorderStateRef.current;
|
|
490
|
+
if (activeState?.mode === 'pointer' && activeState.pointerId !== event.pointerId) {
|
|
491
|
+
setReorderState(null);
|
|
492
|
+
announce(t('@astryx.listInput.announceReorderCancelled'));
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
document.addEventListener('pointerdown', onPointerDown, true);
|
|
496
|
+
return () => document.removeEventListener('pointerdown', onPointerDown, true);
|
|
497
|
+
}, [isPointerReorderActive, announce, t]);
|
|
479
498
|
useEffect(() => {
|
|
480
499
|
const activeAnimations = activeMutationAnimationsRef.current;
|
|
481
500
|
return () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransferList.d.ts","sourceRoot":"","sources":["../../src/TransferList/TransferList.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"TransferList.d.ts","sourceRoot":"","sources":["../../src/TransferList/TransferList.tsx"],"names":[],"mappings":"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"}
|
|
@@ -655,6 +655,26 @@ export function TransferList({
|
|
|
655
655
|
event.currentTarget.releasePointerCapture?.(event.pointerId);
|
|
656
656
|
}
|
|
657
657
|
}, [finishReorder]);
|
|
658
|
+
|
|
659
|
+
// A second pointer that lands anywhere during a pointer reorder (the second
|
|
660
|
+
// finger of a pinch) cancels the reorder. A pinch whose first finger slid
|
|
661
|
+
// first zooms without a pointercancel, so the drag would otherwise commit on
|
|
662
|
+
// release and drop the row at the end of the list.
|
|
663
|
+
const isPointerReorderActive = previewCloneValue != null;
|
|
664
|
+
useEffect(() => {
|
|
665
|
+
if (!isPointerReorderActive) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const onPointerDown = event => {
|
|
669
|
+
const session = reorderSessionRef.current;
|
|
670
|
+
if (session?.mode === 'pointer' && session.pointerId !== event.pointerId) {
|
|
671
|
+
suppressClickRef.current = false;
|
|
672
|
+
finishReorder(true);
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
document.addEventListener('pointerdown', onPointerDown, true);
|
|
676
|
+
return () => document.removeEventListener('pointerdown', onPointerDown, true);
|
|
677
|
+
}, [isPointerReorderActive, finishReorder]);
|
|
658
678
|
const handleSearch = useCallback(nextQuery => {
|
|
659
679
|
setQuery(nextQuery);
|
|
660
680
|
const normalized = nextQuery.trim().toLowerCase();
|
package/dist/lab.css
CHANGED
|
@@ -544,7 +544,7 @@
|
|
|
544
544
|
.xkrqix3:not(#\#):not(#\#):not(#\#){text-decoration-line:none}
|
|
545
545
|
.xlyipyv:not(#\#):not(#\#):not(#\#){text-overflow:ellipsis}
|
|
546
546
|
.xtvhhri:not(#\#):not(#\#):not(#\#){text-transform:uppercase}
|
|
547
|
-
.
|
|
547
|
+
.x1j4r261:not(#\#):not(#\#):not(#\#){touch-action:pinch-zoom}
|
|
548
548
|
.x1bd091l:not(#\#):not(#\#):not(#\#){transform-origin:left bottom}
|
|
549
549
|
.x9tu13d:not(#\#):not(#\#):not(#\#){transform:rotate(-90deg)}
|
|
550
550
|
.x19jd1h0:not(#\#):not(#\#):not(#\#){transform:rotate(180deg)}
|
package/dist/reorderStyles.d.ts
CHANGED
|
@@ -59,11 +59,11 @@ export declare const reorderStyles: Readonly<{
|
|
|
59
59
|
}>;
|
|
60
60
|
readonly handle: Readonly<{
|
|
61
61
|
readonly cursor: stylex.StyleXClassNameFor<"cursor", "default" | "grab">;
|
|
62
|
-
readonly touchAction: stylex.StyleXClassNameFor<"touchAction", "
|
|
62
|
+
readonly touchAction: stylex.StyleXClassNameFor<"touchAction", "pinch-zoom">;
|
|
63
63
|
}>;
|
|
64
64
|
readonly handleActive: Readonly<{
|
|
65
65
|
readonly cursor: stylex.StyleXClassNameFor<"cursor", "default" | "grabbing">;
|
|
66
|
-
readonly touchAction: stylex.StyleXClassNameFor<"touchAction", "
|
|
66
|
+
readonly touchAction: stylex.StyleXClassNameFor<"touchAction", "pinch-zoom">;
|
|
67
67
|
}>;
|
|
68
68
|
}>;
|
|
69
69
|
//# sourceMappingURL=reorderStyles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reorderStyles.d.ts","sourceRoot":"","sources":["../src/reorderStyles.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAoB3C;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"reorderStyles.d.ts","sourceRoot":"","sources":["../src/reorderStyles.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,kBAAkB,CAAC;AAoB3C;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqDxB,CAAC"}
|
package/dist/reorderStyles.js
CHANGED
|
@@ -71,12 +71,12 @@ export const reorderStyles = {
|
|
|
71
71
|
},
|
|
72
72
|
handle: {
|
|
73
73
|
kkrTdU: "x1jm3nie x16khyan",
|
|
74
|
-
kFalU9: "
|
|
74
|
+
kFalU9: "x1j4r261",
|
|
75
75
|
$$css: true
|
|
76
76
|
},
|
|
77
77
|
handleActive: {
|
|
78
78
|
kkrTdU: "xi9pz9s x16khyan",
|
|
79
|
-
kFalU9: "
|
|
79
|
+
kFalU9: "x1j4r261",
|
|
80
80
|
$$css: true
|
|
81
81
|
}
|
|
82
82
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/lab",
|
|
3
|
-
"version": "0.7.5-canary.
|
|
3
|
+
"version": "0.7.5-canary.d26cdf3",
|
|
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",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"@stylexjs/stylex": ">=0.10.0",
|
|
52
|
-
"@astryxdesign/core": "0.7.5-canary.
|
|
52
|
+
"@astryxdesign/core": "0.7.5-canary.d26cdf3",
|
|
53
53
|
"react": ">=19.0.0",
|
|
54
54
|
"react-dom": ">=19.0.0"
|
|
55
55
|
},
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
"d3-shape": "^3.2.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@astryxdesign/a11y-spec": "0.7.5-canary.
|
|
63
|
-
"@astryxdesign/cli": "0.7.5-canary.
|
|
62
|
+
"@astryxdesign/a11y-spec": "0.7.5-canary.d26cdf3",
|
|
63
|
+
"@astryxdesign/cli": "0.7.5-canary.d26cdf3",
|
|
64
64
|
"@babel/cli": "^7.29.7",
|
|
65
65
|
"@babel/core": "^7.29.7",
|
|
66
66
|
"@babel/preset-react": "^7.29.7",
|
|
@@ -74,14 +74,14 @@
|
|
|
74
74
|
},
|
|
75
75
|
"scripts": {
|
|
76
76
|
"build": "node ../../scripts/clean-dist.mjs && pnpm build:esm && tsc --project tsconfig.build.json && node ../../scripts/add-declaration-extensions.mjs && pnpm build:css && node ../../scripts/check-no-dev-jsx.mjs && node ../../scripts/check-fully-specified.mjs",
|
|
77
|
-
"build:esm": "babel src --out-dir dist --extensions .ts,.tsx --out-file-extension .js --ignore \"**/*.test.ts,**/*.test.tsx,**/__tests__/**,**/*.perf.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css,**/*.css.d.ts\" --config-file ./babel.config.
|
|
77
|
+
"build:esm": "babel src --out-dir dist --extensions .ts,.tsx --out-file-extension .js --ignore \"**/*.test.ts,**/*.test.tsx,**/__tests__/**,**/*.perf.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css,**/*.css.d.ts\" --config-file ./babel.config.cjs",
|
|
78
78
|
"build:css": "node ../../scripts/build-css.mjs --package lab",
|
|
79
|
-
"dev": "babel src --watch --out-dir dist --extensions .ts,.tsx --out-file-extension .js --ignore \"**/*.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css*\" --config-file ./babel.config.
|
|
79
|
+
"dev": "babel src --watch --out-dir dist --extensions .ts,.tsx --out-file-extension .js --ignore \"**/*.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css*\" --config-file ./babel.config.cjs",
|
|
80
80
|
"test": "vitest run --root ../..",
|
|
81
81
|
"test:watch": "vitest --root ../..",
|
|
82
82
|
"lint": "eslint src",
|
|
83
83
|
"typecheck:docs": "tsc --project tsconfig.docs.json",
|
|
84
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
84
|
+
"typecheck": "tsc --project tsconfig.json --noEmit && pnpm typecheck:docs",
|
|
85
85
|
"generate:emoji-data": "node scripts/generate-emoji-data.mjs"
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// @pr-a11y scope ^(internal/a11y-spec/src/patterns/progressbar\.|packages/core/src/ProgressBar/|packages/lab/src/CircularProgress/|apps/storybook/stories/ProgressbarA11y)
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @file CircularProgress.a11y.chromium.spec.ts
|
|
@@ -82,6 +82,12 @@ import type {
|
|
|
82
82
|
CommentUser,
|
|
83
83
|
} from './types';
|
|
84
84
|
|
|
85
|
+
// The page's shared live regions are not the component's error Banner.
|
|
86
|
+
const errorBanner = () =>
|
|
87
|
+
screen
|
|
88
|
+
.queryAllByRole('alert')
|
|
89
|
+
.find(element => !element.hasAttribute('data-astryx-live-region'));
|
|
90
|
+
|
|
85
91
|
const VIEWER: CommentUser = {id: 'avery', name: 'Avery Kim'};
|
|
86
92
|
const OTHER: CommentUser = {id: 'maya', name: 'Maya Chen'};
|
|
87
93
|
const AGENT: CommentUser = {
|
|
@@ -900,12 +906,12 @@ describe('Comments threads', () => {
|
|
|
900
906
|
}),
|
|
901
907
|
);
|
|
902
908
|
await flush();
|
|
903
|
-
const banner =
|
|
909
|
+
const banner = errorBanner()!;
|
|
904
910
|
expect(banner.textContent).toContain("Your reaction couldn't be saved.");
|
|
905
911
|
await click(within(banner).getByRole('button', {name: 'Retry'}));
|
|
906
912
|
await flush();
|
|
907
913
|
expect(onReact).toHaveBeenCalledTimes(2);
|
|
908
|
-
expect(
|
|
914
|
+
expect(errorBanner()).toBeUndefined();
|
|
909
915
|
});
|
|
910
916
|
|
|
911
917
|
it('copies a link to the comment', async () => {
|
|
@@ -999,7 +1005,7 @@ describe('Comments threads', () => {
|
|
|
999
1005
|
/>
|
|
1000
1006
|
</Sized>,
|
|
1001
1007
|
);
|
|
1002
|
-
const banner =
|
|
1008
|
+
const banner = errorBanner()!;
|
|
1003
1009
|
expect(banner.textContent).toContain("Couldn't post this comment.");
|
|
1004
1010
|
await click(within(banner).getByRole('button', {name: 'Retry'}));
|
|
1005
1011
|
await flush();
|
|
@@ -1022,7 +1028,7 @@ describe('Comments threads', () => {
|
|
|
1022
1028
|
);
|
|
1023
1029
|
await click(screen.getByRole('button', {name: 'Load earlier comments'}));
|
|
1024
1030
|
await flush();
|
|
1025
|
-
const banner =
|
|
1031
|
+
const banner = errorBanner()!;
|
|
1026
1032
|
expect(banner.textContent).toContain("Earlier comments couldn't load");
|
|
1027
1033
|
await click(within(banner).getByRole('button', {name: 'Retry'}));
|
|
1028
1034
|
await flush();
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
waitFor,
|
|
20
20
|
within,
|
|
21
21
|
} from '@testing-library/react';
|
|
22
|
-
import {useContainerReveal} from '@astryxdesign/core/hooks';
|
|
22
|
+
import {useAnnounce, useContainerReveal} from '@astryxdesign/core/hooks';
|
|
23
23
|
import {TextInput} from '@astryxdesign/core/TextInput';
|
|
24
24
|
import {
|
|
25
25
|
ListInput,
|
|
@@ -206,6 +206,13 @@ function mockMutationAnimations(events: string[] = []) {
|
|
|
206
206
|
|
|
207
207
|
afterEach(() => {
|
|
208
208
|
vi.restoreAllMocks();
|
|
209
|
+
// The shared live regions outlive each test, and a test that stubs
|
|
210
|
+
// requestAnimationFrame can strand an announcement's frame. An empty
|
|
211
|
+
// announcement clears what is still waiting, so it is not joined into the
|
|
212
|
+
// next test's announcements (spec:AST-086 FR9).
|
|
213
|
+
const announcer = renderHook(() => useAnnounce());
|
|
214
|
+
announcer.result.current('');
|
|
215
|
+
announcer.unmount();
|
|
209
216
|
if (originalAnimateDescriptor == null) {
|
|
210
217
|
Reflect.deleteProperty(HTMLElement.prototype, 'animate');
|
|
211
218
|
} else {
|
|
@@ -653,10 +660,13 @@ describe('ListInput', () => {
|
|
|
653
660
|
expect(animate).not.toHaveBeenCalled();
|
|
654
661
|
expect(screen.queryByDisplayValue('Ada')).not.toBeInTheDocument();
|
|
655
662
|
expect(screen.getByRole('button', {name: 'Add guest'})).toHaveFocus();
|
|
663
|
+
// The removal and the empty state that appears join in one frame (FR9).
|
|
656
664
|
await waitFor(() => {
|
|
657
665
|
expect(
|
|
658
666
|
Array.from(document.querySelectorAll('[aria-live="polite"]')).some(
|
|
659
|
-
node =>
|
|
667
|
+
node =>
|
|
668
|
+
node.textContent ===
|
|
669
|
+
'Removed guest 1. No guests yet Add a guest to get started.',
|
|
660
670
|
),
|
|
661
671
|
).toBe(true);
|
|
662
672
|
});
|
|
@@ -1195,6 +1205,32 @@ describe('ListInput', () => {
|
|
|
1195
1205
|
).not.toBeInTheDocument();
|
|
1196
1206
|
});
|
|
1197
1207
|
|
|
1208
|
+
it('cancels a pointer reorder when a second pointer lands, as a pinch does', () => {
|
|
1209
|
+
const onChange = vi.fn();
|
|
1210
|
+
const {container} = renderListInput({isReorderable: true, onChange});
|
|
1211
|
+
const handle = screen.getByRole('button', {name: 'Reorder guest 1'});
|
|
1212
|
+
|
|
1213
|
+
fireEvent.pointerDown(handle, {
|
|
1214
|
+
button: 0,
|
|
1215
|
+
pointerId: 8,
|
|
1216
|
+
clientX: 10,
|
|
1217
|
+
clientY: 10,
|
|
1218
|
+
});
|
|
1219
|
+
fireEvent.pointerMove(handle, {pointerId: 8, clientX: 10, clientY: 34});
|
|
1220
|
+
expect(
|
|
1221
|
+
container.querySelector('[data-list-input-reorder-source]'),
|
|
1222
|
+
).toBeInTheDocument();
|
|
1223
|
+
|
|
1224
|
+
// The second finger of a pinch lands elsewhere on the page.
|
|
1225
|
+
fireEvent.pointerDown(document.body, {pointerId: 9, clientX: 200});
|
|
1226
|
+
expect(
|
|
1227
|
+
container.querySelector('[data-list-input-reorder-source]'),
|
|
1228
|
+
).not.toBeInTheDocument();
|
|
1229
|
+
|
|
1230
|
+
fireEvent.pointerUp(handle, {pointerId: 8, clientX: 10, clientY: 90});
|
|
1231
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
1232
|
+
});
|
|
1233
|
+
|
|
1198
1234
|
it('clears a sub-threshold pointer drag without reordering', () => {
|
|
1199
1235
|
const onChange = vi.fn();
|
|
1200
1236
|
const {container} = renderListInput({isReorderable: true, onChange});
|
|
@@ -1273,10 +1309,14 @@ describe('ListInput', () => {
|
|
|
1273
1309
|
fireEvent.pointerUp(handle, {pointerId: 9, clientX: 15, clientY: 10});
|
|
1274
1310
|
|
|
1275
1311
|
expect(onChange).not.toHaveBeenCalled();
|
|
1312
|
+
// The grab and the return land in the same frame, so they are spoken
|
|
1313
|
+
// together, in order (spec:AST-086 FR9).
|
|
1276
1314
|
await waitFor(() => {
|
|
1277
1315
|
expect(
|
|
1278
1316
|
Array.from(document.querySelectorAll('[aria-live="polite"]')).some(
|
|
1279
|
-
node =>
|
|
1317
|
+
node =>
|
|
1318
|
+
node.textContent ===
|
|
1319
|
+
'guest 1 grabbed. Use arrow keys to move, Space or Enter to drop, and Escape to cancel. guest returned to position 1.',
|
|
1280
1320
|
),
|
|
1281
1321
|
).toBe(true);
|
|
1282
1322
|
});
|
|
@@ -809,6 +809,29 @@ export function ListInput<T>({
|
|
|
809
809
|
}
|
|
810
810
|
}, [isDisabled, isLoading, announce, t]);
|
|
811
811
|
|
|
812
|
+
// A second pointer that lands anywhere during a pointer reorder (the second
|
|
813
|
+
// finger of a pinch) cancels the reorder. A pinch whose first finger slid
|
|
814
|
+
// first zooms without a pointercancel, so the drag would otherwise commit on
|
|
815
|
+
// release and drop the row at the end of the list.
|
|
816
|
+
useEffect(() => {
|
|
817
|
+
if (!isPointerReorderActive) {
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
821
|
+
const activeState = reorderStateRef.current;
|
|
822
|
+
if (
|
|
823
|
+
activeState?.mode === 'pointer' &&
|
|
824
|
+
activeState.pointerId !== event.pointerId
|
|
825
|
+
) {
|
|
826
|
+
setReorderState(null);
|
|
827
|
+
announce(t('@astryx.listInput.announceReorderCancelled'));
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
document.addEventListener('pointerdown', onPointerDown, true);
|
|
831
|
+
return () =>
|
|
832
|
+
document.removeEventListener('pointerdown', onPointerDown, true);
|
|
833
|
+
}, [isPointerReorderActive, announce, t]);
|
|
834
|
+
|
|
812
835
|
useEffect(() => {
|
|
813
836
|
const activeAnimations = activeMutationAnimationsRef.current;
|
|
814
837
|
return () => {
|
|
@@ -56,7 +56,14 @@ function Harness({
|
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function CreateHarness({
|
|
59
|
+
function CreateHarness({
|
|
60
|
+
spy,
|
|
61
|
+
}: {
|
|
62
|
+
spy: (
|
|
63
|
+
items: SearchableItem[],
|
|
64
|
+
change: MobileTokenizerChange<SearchableItem>,
|
|
65
|
+
) => void;
|
|
66
|
+
}) {
|
|
60
67
|
const [value, setValue] = useState<SearchableItem[]>([]);
|
|
61
68
|
return (
|
|
62
69
|
<MobileTokenizer
|
|
@@ -105,8 +112,10 @@ beforeEach(() => {
|
|
|
105
112
|
window.scrollTo = vi.fn();
|
|
106
113
|
vi.stubGlobal(
|
|
107
114
|
'requestAnimationFrame',
|
|
115
|
+
// Next microtask, not synchronously: a real frame never runs before
|
|
116
|
+
// requestAnimationFrame returns, and callers may rely on its id.
|
|
108
117
|
vi.fn((cb: FrameRequestCallback) => {
|
|
109
|
-
cb(0);
|
|
118
|
+
queueMicrotask(() => cb(0));
|
|
110
119
|
return 1;
|
|
111
120
|
}),
|
|
112
121
|
);
|
|
@@ -302,7 +311,7 @@ describe('MobileTokenizer (Lab, single-sheet flow)', () => {
|
|
|
302
311
|
const title = await screen.findByRole('heading', {
|
|
303
312
|
name: 'No results found',
|
|
304
313
|
});
|
|
305
|
-
const emptyState = title.closest('
|
|
314
|
+
const emptyState = title.closest('.astryx-empty-state');
|
|
306
315
|
expect(emptyState).toHaveTextContent('Try a different search.');
|
|
307
316
|
expect(emptyState?.querySelector('.astryx-icon')).toHaveAttribute(
|
|
308
317
|
'data-size',
|
|
@@ -125,7 +125,7 @@ function StagedColumnSelector({
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
function openSelector(): void {
|
|
128
|
-
fireEvent.keyDown(screen.getByRole('
|
|
128
|
+
fireEvent.keyDown(screen.getByRole('combobox', {name: 'View options'}), {
|
|
129
129
|
key: 'ArrowDown',
|
|
130
130
|
});
|
|
131
131
|
}
|
|
@@ -144,7 +144,7 @@ describe('ComplexSelector + TransferList composition', () => {
|
|
|
144
144
|
openSelector();
|
|
145
145
|
await waitFor(() => {
|
|
146
146
|
expect(
|
|
147
|
-
screen.getByRole('
|
|
147
|
+
screen.getByRole('combobox', {name: 'View options'}),
|
|
148
148
|
).toHaveAttribute('aria-expanded', 'true');
|
|
149
149
|
});
|
|
150
150
|
|
|
@@ -173,7 +173,7 @@ describe('ComplexSelector + TransferList composition', () => {
|
|
|
173
173
|
const user = userEvent.setup();
|
|
174
174
|
const onCommit = vi.fn();
|
|
175
175
|
render(<StagedColumnSelector onCommit={onCommit} />);
|
|
176
|
-
const trigger = screen.getByRole('
|
|
176
|
+
const trigger = screen.getByRole('combobox', {name: 'View options'});
|
|
177
177
|
|
|
178
178
|
openSelector();
|
|
179
179
|
await user.click(
|
|
@@ -255,7 +255,7 @@ describe('ComplexSelector + TransferList composition', () => {
|
|
|
255
255
|
const user = userEvent.setup();
|
|
256
256
|
const onCommit = vi.fn();
|
|
257
257
|
render(<StagedColumnSelector onCommit={onCommit} />);
|
|
258
|
-
const trigger = screen.getByRole('
|
|
258
|
+
const trigger = screen.getByRole('combobox', {name: 'View options'});
|
|
259
259
|
|
|
260
260
|
openSelector();
|
|
261
261
|
await waitFor(() => {
|
|
@@ -534,6 +534,42 @@ describe('TransferList', () => {
|
|
|
534
534
|
expect(screen.getByTestId('popover-host')).toContainElement(layer);
|
|
535
535
|
});
|
|
536
536
|
|
|
537
|
+
it('cancels a pointer reorder when a second pointer lands, as a pinch does', () => {
|
|
538
|
+
const onValueChange = vi.fn();
|
|
539
|
+
const {container} = render(
|
|
540
|
+
<ControlledTransferList
|
|
541
|
+
initialValue={['name', 'owner', 'status']}
|
|
542
|
+
onValueChange={onValueChange}
|
|
543
|
+
/>,
|
|
544
|
+
);
|
|
545
|
+
const rows = [
|
|
546
|
+
...container.querySelectorAll<HTMLElement>('[data-transfer-list-row]'),
|
|
547
|
+
];
|
|
548
|
+
mockRowBounds(rows);
|
|
549
|
+
const handle = screen.getByRole('button', {name: 'Reorder Name'});
|
|
550
|
+
|
|
551
|
+
fireEvent.pointerDown(handle, {
|
|
552
|
+
button: 0,
|
|
553
|
+
pointerId: 7,
|
|
554
|
+
clientX: 300,
|
|
555
|
+
clientY: 10,
|
|
556
|
+
});
|
|
557
|
+
fireEvent.pointerMove(handle, {pointerId: 7, clientX: 300, clientY: 34});
|
|
558
|
+
expect(
|
|
559
|
+
container.querySelector('[data-transfer-list-reorder-source]'),
|
|
560
|
+
).toBeInTheDocument();
|
|
561
|
+
|
|
562
|
+
// The second finger of a pinch lands elsewhere on the page.
|
|
563
|
+
fireEvent.pointerDown(document.body, {pointerId: 8, clientX: 600});
|
|
564
|
+
expect(
|
|
565
|
+
container.querySelector('[data-transfer-list-reorder-source]'),
|
|
566
|
+
).not.toBeInTheDocument();
|
|
567
|
+
|
|
568
|
+
fireEvent.pointerMove(handle, {pointerId: 7, clientX: 300, clientY: 140});
|
|
569
|
+
fireEvent.pointerUp(handle, {pointerId: 7, clientX: 300, clientY: 140});
|
|
570
|
+
expect(onValueChange).not.toHaveBeenCalled();
|
|
571
|
+
});
|
|
572
|
+
|
|
537
573
|
it('keeps rows stationary, locks X, follows pointer Y, and commits once', () => {
|
|
538
574
|
const onValueChange = vi.fn();
|
|
539
575
|
const {container} = render(
|
|
@@ -994,6 +994,30 @@ export function TransferList<T extends string = string>({
|
|
|
994
994
|
[finishReorder],
|
|
995
995
|
);
|
|
996
996
|
|
|
997
|
+
// A second pointer that lands anywhere during a pointer reorder (the second
|
|
998
|
+
// finger of a pinch) cancels the reorder. A pinch whose first finger slid
|
|
999
|
+
// first zooms without a pointercancel, so the drag would otherwise commit on
|
|
1000
|
+
// release and drop the row at the end of the list.
|
|
1001
|
+
const isPointerReorderActive = previewCloneValue != null;
|
|
1002
|
+
useEffect(() => {
|
|
1003
|
+
if (!isPointerReorderActive) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
1007
|
+
const session = reorderSessionRef.current;
|
|
1008
|
+
if (
|
|
1009
|
+
session?.mode === 'pointer' &&
|
|
1010
|
+
session.pointerId !== event.pointerId
|
|
1011
|
+
) {
|
|
1012
|
+
suppressClickRef.current = false;
|
|
1013
|
+
finishReorder(true);
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
document.addEventListener('pointerdown', onPointerDown, true);
|
|
1017
|
+
return () =>
|
|
1018
|
+
document.removeEventListener('pointerdown', onPointerDown, true);
|
|
1019
|
+
}, [isPointerReorderActive, finishReorder]);
|
|
1020
|
+
|
|
997
1021
|
const handleSearch = useCallback(
|
|
998
1022
|
(nextQuery: string) => {
|
|
999
1023
|
setQuery(nextQuery);
|
|
@@ -106,7 +106,7 @@ function Harness({
|
|
|
106
106
|
async function openSelector(
|
|
107
107
|
name = 'Visible fields',
|
|
108
108
|
): Promise<HTMLButtonElement> {
|
|
109
|
-
const trigger = screen.getByRole('
|
|
109
|
+
const trigger = screen.getByRole('combobox', {
|
|
110
110
|
name,
|
|
111
111
|
}) as HTMLButtonElement;
|
|
112
112
|
fireEvent.keyDown(trigger, {key: 'ArrowDown'});
|
|
@@ -416,7 +416,7 @@ describe('TransferListSelector', () => {
|
|
|
416
416
|
);
|
|
417
417
|
|
|
418
418
|
expect(
|
|
419
|
-
screen.getByRole('
|
|
419
|
+
screen.getByRole('combobox', {name: 'Visible fields'}),
|
|
420
420
|
).toHaveTextContent('3 selected');
|
|
421
421
|
});
|
|
422
422
|
|
|
@@ -462,7 +462,7 @@ describe('TransferListSelector', () => {
|
|
|
462
462
|
expect(fieldset).not.toContainElement(cancel);
|
|
463
463
|
expect(apply).toBeDisabled();
|
|
464
464
|
expect(
|
|
465
|
-
screen.getByRole('
|
|
465
|
+
screen.getByRole('combobox', {name: 'Visible fields'}),
|
|
466
466
|
).toHaveAttribute('aria-disabled', 'true');
|
|
467
467
|
|
|
468
468
|
view.rerender(
|
|
@@ -564,7 +564,7 @@ describe('TransferListSelector', () => {
|
|
|
564
564
|
screen.getByTestId('column-selector').closest('.astryx-complex-selector'),
|
|
565
565
|
).toHaveAttribute('data-size', 'sm');
|
|
566
566
|
expect(
|
|
567
|
-
screen.getByRole('
|
|
567
|
+
screen.getByRole('combobox', {name: 'Table columns'}),
|
|
568
568
|
).toHaveTextContent('Customize columns');
|
|
569
569
|
expect(
|
|
570
570
|
screen.getByTestId('column-selector').closest('.astryx-field'),
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// @pr-a11y always
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file ReorderGripPinchZoom.a11y.chromium.spec.ts
|
|
6
|
+
* @input The checked-in Lab/ListInput › Tag Options and Lab/TransferList ›
|
|
7
|
+
* Default stories, a built Storybook and real Chromium touch input (CDP) on
|
|
8
|
+
* a phone viewport
|
|
9
|
+
* @output Browser evidence that a pinch which starts on, or reaches, a reorder
|
|
10
|
+
* grip zooms the page and leaves the order as it was, while a one-finger
|
|
11
|
+
* drag on the grip still reorders (WCAG 1.4.4)
|
|
12
|
+
* @position Chromium proof for the shared Lab reorder grip's `touch-action`
|
|
13
|
+
* (packages/lab/src/reorderStyles.ts)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {expect, test, type CDPSession, type Page} from '@playwright/test';
|
|
17
|
+
import {
|
|
18
|
+
DEFAULT_STORYBOOK_DIR,
|
|
19
|
+
serveStorybook,
|
|
20
|
+
type StaticServer,
|
|
21
|
+
} from '@astryxdesign/a11y-spec/storybook';
|
|
22
|
+
|
|
23
|
+
let storybook: StaticServer;
|
|
24
|
+
test.beforeAll(async () => {
|
|
25
|
+
storybook = await serveStorybook(
|
|
26
|
+
process.env.ASTRYX_STORYBOOK_DIR ?? DEFAULT_STORYBOOK_DIR,
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
test.afterAll(async () => {
|
|
30
|
+
await storybook?.close();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// A phone: the viewport meta applies and the page can pinch-zoom.
|
|
34
|
+
test.use({
|
|
35
|
+
hasTouch: true,
|
|
36
|
+
isMobile: true,
|
|
37
|
+
viewport: {width: 390, height: 844},
|
|
38
|
+
deviceScaleFactor: 3,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const sleep = async (ms: number) =>
|
|
42
|
+
new Promise(resolve => setTimeout(resolve, ms));
|
|
43
|
+
|
|
44
|
+
type Point = {x: number; y: number};
|
|
45
|
+
type Box = {x: number; y: number; width: number; height: number};
|
|
46
|
+
|
|
47
|
+
interface Surface {
|
|
48
|
+
name: string;
|
|
49
|
+
story: string;
|
|
50
|
+
/** The first row's reorder grip. */
|
|
51
|
+
grip: string;
|
|
52
|
+
/** Every reorderable row, in display order, keyed by this attribute. */
|
|
53
|
+
rowAttribute: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const SURFACES: Surface[] = [
|
|
57
|
+
{
|
|
58
|
+
name: 'ListInput',
|
|
59
|
+
story: 'lab-listinput--tag-options',
|
|
60
|
+
grip: 'button[data-list-input-reorder]',
|
|
61
|
+
rowAttribute: 'data-list-input-row',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'TransferList',
|
|
65
|
+
story: 'lab-transferlist--default',
|
|
66
|
+
grip: '[data-transfer-list-row]:not([data-transfer-list-row=""]) button[aria-pressed]',
|
|
67
|
+
rowAttribute: 'data-transfer-list-row',
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
async function openStory(page: Page, surface: Surface) {
|
|
72
|
+
await page.goto(
|
|
73
|
+
`${storybook.origin}/iframe.html?id=${surface.story}&viewMode=story`,
|
|
74
|
+
);
|
|
75
|
+
const grip = page.locator(surface.grip).first();
|
|
76
|
+
await expect(grip).toBeVisible();
|
|
77
|
+
expect(await page.evaluate(() => window.visualViewport?.scale)).toBe(1);
|
|
78
|
+
const box = (await grip.boundingBox()) as Box;
|
|
79
|
+
const center = {x: box.x + box.width / 2, y: box.y + box.height / 2};
|
|
80
|
+
const cdp = await page.context().newCDPSession(page);
|
|
81
|
+
return {center, cdp, order: await readOrder(page, surface)};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function readOrder(page: Page, surface: Surface) {
|
|
85
|
+
return page.evaluate(
|
|
86
|
+
attribute =>
|
|
87
|
+
Array.from(document.querySelectorAll(`[${attribute}]`))
|
|
88
|
+
.map(row => row.getAttribute(attribute))
|
|
89
|
+
.filter((key): key is string => key != null && key !== ''),
|
|
90
|
+
surface.rowAttribute,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const zoom = async (page: Page) =>
|
|
95
|
+
page.evaluate(() => window.visualViewport?.scale ?? 1);
|
|
96
|
+
|
|
97
|
+
async function touch(
|
|
98
|
+
cdp: CDPSession,
|
|
99
|
+
type: 'touchStart' | 'touchMove' | 'touchEnd',
|
|
100
|
+
points: Point[],
|
|
101
|
+
) {
|
|
102
|
+
await cdp.send('Input.dispatchTouchEvent', {
|
|
103
|
+
type,
|
|
104
|
+
touchPoints: points.map((point, index) => ({...point, id: index + 1})),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Two fingers land one after the other and spread apart, as a person pinches.
|
|
109
|
+
// The spread is wide enough to clear the browser's minimum pinch span.
|
|
110
|
+
async function pinch(cdp: CDPSession, first: Point, second: Point) {
|
|
111
|
+
const steps = 12;
|
|
112
|
+
const spread = 2.5;
|
|
113
|
+
const mid = {x: (first.x + second.x) / 2, y: (first.y + second.y) / 2};
|
|
114
|
+
const at = (p: Point, k: number) => ({
|
|
115
|
+
x: mid.x + (p.x - mid.x) * k,
|
|
116
|
+
y: mid.y + (p.y - mid.y) * k,
|
|
117
|
+
});
|
|
118
|
+
await touch(cdp, 'touchStart', [first]);
|
|
119
|
+
await sleep(30);
|
|
120
|
+
await touch(cdp, 'touchStart', [first, second]);
|
|
121
|
+
for (let i = 1; i <= steps; i++) {
|
|
122
|
+
await sleep(16);
|
|
123
|
+
const k = 1 + ((spread - 1) * i) / steps;
|
|
124
|
+
await touch(cdp, 'touchMove', [at(first, k), at(second, k)]);
|
|
125
|
+
}
|
|
126
|
+
await sleep(16);
|
|
127
|
+
await touch(cdp, 'touchEnd', []);
|
|
128
|
+
await sleep(600);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (const surface of SURFACES) {
|
|
132
|
+
test.describe(surface.name, () => {
|
|
133
|
+
test('a pinch whose first finger lands on a grip zooms the page', async ({
|
|
134
|
+
page,
|
|
135
|
+
}) => {
|
|
136
|
+
const {center, cdp, order} = await openStory(page, surface);
|
|
137
|
+
await pinch(cdp, center, {x: center.x + 40, y: center.y + 140});
|
|
138
|
+
expect(await zoom(page)).toBeGreaterThan(1.2);
|
|
139
|
+
expect(await readOrder(page, surface)).toEqual(order);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('a pinch whose second finger lands on a grip zooms the page', async ({
|
|
143
|
+
page,
|
|
144
|
+
}) => {
|
|
145
|
+
const {center, cdp, order} = await openStory(page, surface);
|
|
146
|
+
await pinch(cdp, {x: center.x + 40, y: center.y + 140}, center);
|
|
147
|
+
expect(await zoom(page)).toBeGreaterThan(1.2);
|
|
148
|
+
expect(await readOrder(page, surface)).toEqual(order);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('a one-finger drag on a grip still reorders', async ({page}) => {
|
|
152
|
+
const {center, cdp, order} = await openStory(page, surface);
|
|
153
|
+
const rows = await page.evaluate(
|
|
154
|
+
attribute =>
|
|
155
|
+
Array.from(document.querySelectorAll(`[${attribute}]`))
|
|
156
|
+
.filter(row => row.getAttribute(attribute))
|
|
157
|
+
.map(row => row.getBoundingClientRect().top),
|
|
158
|
+
surface.rowAttribute,
|
|
159
|
+
);
|
|
160
|
+
// Past the second row's centre: the first row lands second or later.
|
|
161
|
+
const distance = (rows[2] - rows[0]) * 0.9;
|
|
162
|
+
await touch(cdp, 'touchStart', [center]);
|
|
163
|
+
for (let i = 1; i <= 20; i++) {
|
|
164
|
+
await sleep(16);
|
|
165
|
+
await touch(cdp, 'touchMove', [
|
|
166
|
+
{x: center.x, y: center.y + (distance * i) / 20},
|
|
167
|
+
]);
|
|
168
|
+
}
|
|
169
|
+
await sleep(100);
|
|
170
|
+
await touch(cdp, 'touchEnd', []);
|
|
171
|
+
await sleep(600);
|
|
172
|
+
expect(await zoom(page)).toBe(1);
|
|
173
|
+
const after = await readOrder(page, surface);
|
|
174
|
+
expect(after).not.toEqual(order);
|
|
175
|
+
expect(after.indexOf(order[0])).toBeGreaterThan(0);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
package/src/reorderStyles.ts
CHANGED
|
@@ -65,18 +65,23 @@ export const reorderStyles = stylex.create({
|
|
|
65
65
|
insetBlockEnd: `calc(-1 * ${spacingVars['--spacing-0-5']})`,
|
|
66
66
|
},
|
|
67
67
|
},
|
|
68
|
+
// A finger on a grip drags its row from the first move, so the grip refuses
|
|
69
|
+
// panning. It still allows pinch-zoom: a pinch whose first finger lands on a
|
|
70
|
+
// grip zooms the page (WCAG 1.4.4); the browser's pointercancel then ends
|
|
71
|
+
// the drag, as it would any drag the browser takes. `touch-action` is read
|
|
72
|
+
// when the touch starts, so the active state keeps the same value.
|
|
68
73
|
handle: {
|
|
69
74
|
cursor: {
|
|
70
75
|
default: 'grab',
|
|
71
76
|
':is(:disabled,[aria-disabled="true"])': 'default',
|
|
72
77
|
},
|
|
73
|
-
touchAction: '
|
|
78
|
+
touchAction: 'pinch-zoom',
|
|
74
79
|
},
|
|
75
80
|
handleActive: {
|
|
76
81
|
cursor: {
|
|
77
82
|
default: 'grabbing',
|
|
78
83
|
':is(:disabled,[aria-disabled="true"])': 'default',
|
|
79
84
|
},
|
|
80
|
-
touchAction: '
|
|
85
|
+
touchAction: 'pinch-zoom',
|
|
81
86
|
},
|
|
82
87
|
});
|