Skip to content

test(core): the swipe spring-back read stops the scene at the lift - #10432

Merged
cixzhang merged 1 commit into
meta:mainfrom
vjeux:uikit/f1-swipe-spring-freeze
Nov 7, 2026
Merged

cixzhang merged 1 commit into
meta:mainfrom
vjeux:uikit/f1-swipe-spring-freeze

Conversation

@vjeux

@vjeux vjeux commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

The spring-back check in ItemSwipeActions.a11y.chromium.spec.ts now reads the row 60 ms after its lift on the scene's own clock, by stopping the scene at the lift, instead of from a timer the test process starts after it. This is a test-only change on main.

Maintainer impact

"Paints no panel at rest, keeps the panel fixed while the row moves, rests open on a real button, and springs back with the panel" reads the row mid-flight, 60 ms into its spring back. The test process started that 60 ms timer only once its read reached the page, a round trip after the lift. On a loaded runner the spring back was over by then, and the check failed with nothing wrong.

Intended invariant

60 ms after the lift, the row is still on its way home and the panel is as wide as the gap the row has not yet covered, as before. The assertions are unchanged.

Change

Evidence

  • Failure before the change, with 150 ms of runner lag between the lift and the read: 5 of 5 fail in Chromium (vjeux@d065587d41).
  • Success after the change, with the same lag after the lift: 5 of 5 pass (vjeux@1379d3aafb). Without lag: 5 of 5.
  • Product/runtime behavior verified unchanged: test-only. The whole spec passes (3 of 3).

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

  • ItemSwipeActions.a11y.chromium.spec.ts: 3 of 3 in Chromium (the spec is Chromium-only: it drives touch through the browser's protocol).
  • The lag arms above, five runs each.
  • ESLint, Prettier and TypeScript on the changed file.

The spring-back proof read the row 60 ms after its lift, from a timer
the test process started after the lift. That read comes a round trip
late, and on a loaded runner the spring back was over. The proof now
freezes the scene in the lift's own task, moves it on 60 ms by its own
clock, reads, and thaws.
@vercel

vercel Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 7, 2026 1:55am UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 7, 2026
@github-actions

github-actions Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · e5ce07e

Storybook · Sandbox · built from this PR's head

Modified components

Component Size LoC Visual · A11y · RTL
Item · Storybook 16.3 kB (0) · 5.5 kB CSS (0) 1,697 (0) ✓ · ✓ · —

Package size: measured; main has no measurement of the merge base to compare yet.

API changes: cli +14 · ~5 · −0 (against the merge base's committed report)

Committed API report behind this build: cli 19 rows, core 24 rows; run pnpm api:report.

19 API changes (advisory)
Package Where Signature Usage
+ cli ./api · Types · type SearchResultEntry deprecated?: ComponentDeprecation | undefined import type {SearchResultEntry} from '@astryxdesign/cli/api'; const x: SearchResultEntry = {deprecated: …}
+ cli ./authoring · Types · interface ComponentDeprecation interface ComponentDeprecation import type {ComponentDeprecation} from '@astryxdesign/cli/authoring'
+ cli ./authoring · Types · interface ComponentIconSlotDoc interface ComponentIconSlotDoc import type {ComponentIconSlotDoc} from '@astryxdesign/cli/authoring'
+ cli ./authoring · Types · interface ComponentIconSlotStateDoc interface ComponentIconSlotStateDoc import type {ComponentIconSlotStateDoc} from '@astryxdesign/cli/authoring'
+ cli ./authoring · Types · interface MultiComponentDoc deprecated?: ComponentDeprecation import type {MultiComponentDoc} from '@astryxdesign/cli/authoring'; const x: MultiComponentDoc = {deprecated: …}
+ cli ./authoring · Types · interface SingleComponentDoc deprecated?: ComponentDeprecation import type {SingleComponentDoc} from '@astryxdesign/cli/authoring'; const x: SingleComponentDoc = {deprecated: …}
+ cli ./doc · Types · interface ComponentBaseDoc deprecated?: ComponentDeprecation import type {ComponentBaseDoc} from '@astryxdesign/cli/doc'; const x: ComponentBaseDoc = {deprecated: …}
+ cli ./doc · Types · interface SubComponentDoc deprecated?: ComponentDeprecation import type {SubComponentDoc} from '@astryxdesign/cli/doc'; const x: SubComponentDoc = {deprecated: …}
+ cli ./doc · Also exported from an earlier entry point · ComponentDeprecation ComponentDeprecation (./authoring) import {ComponentDeprecation} from '@astryxdesign/cli/doc'
+ cli ./doc · Also exported from an earlier entry point · ComponentIconSlotDoc ComponentIconSlotDoc (./authoring) import {ComponentIconSlotDoc} from '@astryxdesign/cli/doc'
+ cli ./doc · Also exported from an earlier entry point · ComponentIconSlotStateDoc ComponentIconSlotStateDoc (./authoring) import {ComponentIconSlotStateDoc} from '@astryxdesign/cli/doc'
+ cli ./template · Also exported from an earlier entry point · ComponentDeprecation ComponentDeprecation (./authoring) import {ComponentDeprecation} from '@astryxdesign/cli/template'
+ cli ./template · Also exported from an earlier entry point · ComponentIconSlotDoc ComponentIconSlotDoc (./authoring) import {ComponentIconSlotDoc} from '@astryxdesign/cli/template'
+ cli ./template · Also exported from an earlier entry point · ComponentIconSlotStateDoc ComponentIconSlotStateDoc (./authoring) import {ComponentIconSlotStateDoc} from '@astryxdesign/cli/template'
~ cli ./api · Functions · function search function search: (query: string, options?: { cwd?: string | undefined; type?: "template" | "component" | "hook" | "theme" | "doc" | undefined; limit?: number | undefined }) => Promise<SearchResponse> → function search: (query: string, options?: { cwd?: string | undefined; type?: "template" | "component" | "doc" | "theme" | "hook" | undefined; limit?: number | undefined }) => Promise<SearchResponse> import {search} from '@astryxdesign/cli/api'; search(…)
~ cli ./authoring · Types · interface MultiComponentDoc theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } → theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[]; componentIcons?: ComponentIconSlotDoc[] } import type {MultiComponentDoc} from '@astryxdesign/cli/authoring'; const x: MultiComponentDoc = {theming: …}
~ cli ./authoring · Types · interface SingleComponentDoc theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } → theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[]; componentIcons?: ComponentIconSlotDoc[] } import type {SingleComponentDoc} from '@astryxdesign/cli/authoring'; const x: SingleComponentDoc = {theming: …}
~ cli ./doc · Types · interface ComponentBaseDoc theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } → theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[]; componentIcons?: ComponentIconSlotDoc[] } import type {ComponentBaseDoc} from '@astryxdesign/cli/doc'; const x: ComponentBaseDoc = {theming: …}
~ cli ./doc · Types · interface SubComponentDoc theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } → theming?: { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[]; componentIcons?: ComponentIconSlotDoc[] } import type {SubComponentDoc} from '@astryxdesign/cli/doc'; const x: SubComponentDoc = {theming: …}

Touched components checked; add ci:run-visual or ci:run-rtl to run the full suites.


Generated by PR Enrichment workflow | View full report

@astracat-bot

astracat-bot Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 7, 2026, 2:08 AM UTC

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved pending CI: CI has not run on this head yet. The pull request is a draft from a fork, so CI starts once it is marked ready for review and a maintainer adds the ci:run label (it takes effect on the next push or a close and reopen).

The spring-back read now runs on the scene's clock: with 150 ms of runner lag after the lift, the old read failed in all five runs and the new one passed in all five, about 60 ms into the spring back as intended.

Non-blocking suggestion:

  • The comment above freezePageOn(page, 'touchend') says the scene stops before the row hears the lift, but the row hears pointerup first and the read relies on that; arming on pointerup instead reads at 0 ms and passes without checking the panel, so please reword the comment (suggestion inline) and the matching sentence in the description.

[Automated review]

Comment on lines +231 to +233
// The scene stops in the lift's own task, before the row hears the lift,
// and moves on 60 ms by its own clock. A read sent after the lift comes a
// round trip late, and a loaded runner can find the spring back over.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: the row hears the lift before this freeze. It handles pointerup (useSwipeAction.ts:672), and Chromium dispatches pointerup before touchend, so settle() has already written the duration and travel when the freeze lands; the freeze's style flush then starts the transitions and holds them at their start. Armed on pointerup, as this comment reads, the freeze runs before settle(), nothing is held, and the read lands at 0 ms with the row and the panel both at 30 px, so the check passes without testing the panel. Suggested wording:

Suggested change
// The scene stops in the lift's own task, before the row hears the lift,
// and moves on 60 ms by its own clock. A read sent after the lift comes a
// round trip late, and a loaded runner can find the spring back over.
// The row hears the lift first, on pointerup, and writes its settle. The
// freeze lands on touchend in the same task, before the settle's
// transitions start, and holds them at their start. A read sent after the
// lift comes a round trip late, and a loaded runner can find the spring
// back over.

@cixzhang cixzhang added ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) ci:run-a11y Run the accessibility suite on this PR; write access, next push (AST-030 FR12) labels Nov 7, 2026
@vjeux
vjeux marked this pull request as ready for review November 7, 2026 03:08
@cixzhang
cixzhang merged commit d232fe1 into meta:main Nov 7, 2026
121 checks passed
@vjeux
vjeux deleted the uikit/f1-swipe-spring-freeze branch November 7, 2026 03:48

This branch was successfully deployed

1 active deployment
Preview — e5ce07e6 Deployed Nov 7, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) ci:run-a11y Run the accessibility suite on this PR; write access, next push (AST-030 FR12) CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants