Repository navigation
feat(core): a checkbox and a radio option press the same way for every pointer (spec:AST-094 FR43) - #8696
feat(core): a checkbox and a radio option press the same way for every pointer (spec:AST-094 FR43)#8696vjeux wants to merge 1896 commits into
Conversation
…es its recognizers, and the group keeps answering afterwards Under a fine pointer the host is not installed, so usesTouchEvents keeps its initial true, and a group press's own touch pointermove and pointerup went down the touch-pairing path and returned: the finger's moves and lift never reached the group, its contact stayed in the arena, and the group answered no press after it, a mouse's included. The group press now feeds its own events through the pointer path for every pointer type. Test (failing first): under a fine pointer a finger tap on a group recognizes, and a mouse tap afterwards does too.
… are all passive spec:AST-094 FR20: a watcher registers only passive listeners, but a watcher-only page still got the host's cancelable document touchend and its contextmenu and window click listeners. The document touchend is now passive until an enabled claimer (a recognizer that claims touches, or a consumer) is attached, and the contextmenu and click listeners are on only then, swapped as attachments change. The new test (a lone watcher leaves no cancelable document or window listener; a claiming pan brings the three) fails on the previous head.
…r watchers) into the host line The host keeps this PR's isRtlElement and its fingers held before a recognizer begins (a kept press, a scroll-edge pan), beside meta#8061's view size and meta#8309's pointer-events finger lookup. meta#8589's claimer check counts only participants active on this pointer (isActive, isConsumerActive), and a group press's click listener now stays while a claimer keeps it installed. The browser spec keeps the fine-pointer describe beside the FR18-FR22 real-touch states.
…inter reaches its recognizers) as a dependency
Main's meta#8521 is its head 25b468c squashed, which this pull request already carries: every file it touches is byte-identical to 25b468c. In the two files that conflicted (the motion README and animator.ts), the resolution is this side's version, main's merged meta#8521 with this pull request's later work on top. Nothing of main is dropped.
The note's reword joined its next sentence onto one long line; it now wraps like the rest of the file. No code change.
…w press release change
…election hold change
cixzhang's meta#8589 keeps the host's touchend passive, and its contextmenu and click listeners off, until a claimer is attached (syncClaimingListeners). Ported onto this pull request's input stream with no change to what it does: install() subscribes the touchend passive, and syncClaimingListeners swaps it for a cancelling subscription and adds the contextmenu and click subscriptions while a claimer is attached. A group press brings its own click subscription only while the host has none, and the host's takes it over. Her watcher test reads those listeners on the window, where the input stream registers them; her new group test (a finger, then a mouse, under a fine pointer) sits before the detach test. The test file and the bare fixture import both her ScreenEdgePanGestureRecognizer and this layer's selection lock from utils.
Here the click guard (utils/clickGuard.ts) owns clicks, so cixzhang's meta#8589 syncClaimingListeners, ported at meta#8356, switches only the touchend's cancelability and the contextmenu subscription; the group press subscribes no click of its own. Her meta#8072 5c59af1 (a finger on an action group under a fine pointer reaches its recognizers) arms the click guard where it armed the swallower. Her watcher test reads the touchend and the contextmenu on the window, and no click listener: the guard's comes only once a gesture acts.
… the lift, under real touch input spec:AST-094 FR20's "follows the scroll to the lift" had only a jsdom test with a synthetic takeover; the FR18-FR22 verification row asks for real touch input. In Chromium with injected touches, a vertical drag on a list a watcher pan is attached to scrolls the list natively, no touchmove is cancelled, and the watcher goes from began to ended at the lift.
… at the edge stays the page's spec:AST-094 FR10: two fingers before a claim are the page's pinch. The scroll-edge hold (DEC-18) counted any finger, so with two fingers down it cancelled the pinch's moves before any drag was claimed. It now holds only while a single finger is down. The new two-finger test fails on the previous head. Co-authored-by: Christopher Chedeau <vjeuxx@gmail.com>
…e selection hold at once (spec:AST-094 FR41) The hold a claimed mouse or pen drag puts on the document is released a task after the press ends, so the press's own mouseup and click, which follow a lift in the same task, still meet it. No mouseup or click follows a pointercancel, a blur or a detach, yet the hold lingered that task too: a check right after the cancel, or a test that ends there, met a page that still refused selection. Fails until a cancel releases at once.
…ing it; a cancel lifts the drag's selection hold at once (spec:AST-094 FR41) The click guard keeps the window it listens on and, at disarm, removes nothing when that window is gone (the document has another view, or its listener methods went with a torn-down environment): its listeners went with it. The disarm timer that outlived a test file no longer throws. The document-wide selection hold of a claimed mouse or pen drag ends a task after a lift, so the press's own mouseup and click still meet it, and at once after a pointercancel, a blur or a detach, which nothing follows: the host passes isCancel from its cancel paths. Also drops an unused import from the host and has a test assert the translation it kept in a variable.
There was a problem hiding this comment.
Approval waits on one spec decision: the branch rewords the current component:Button FR15 (a 250 ms coarse-pointer release painted on ::before, 200 ms for a Space press and for fine pointers, and a Reduce Motion split), so an engineering owner needs to approve that rewording by commenting /approve-spec <full head SHA> on this pull request, or the record should keep main's FR15 text; the author may do so as an engineering owner. The selection controls' own mouse and pen press is now covered by spec:AST-094 FR11, FR43 and DEC-21 on main. Whatever the FR15 decision, these still need changes:
- Rebase onto
mainonce the predecessor pull requests land, so this pull request carries its own 15 files (996 lines) instead of 269 files and 42,403 authored lines. - With Reduce Motion and a mouse, the checkbox and radio release still fades for 200 ms (
CheckboxInput.tsx,RadioListItem.tsx);design:reduced-motionDR3 asks for an immediate change, whichcontrolPressLayer.tsandrowPressLayer.tsalready make with their(prefers-reduced-motion: reduce) and (not (pointer: coarse))arm. - A standalone checkbox's layer declares a 250 ms release while the press controller clears a fine pointer's press after about 200 ms, so align the two clocks and assert how long the press stays
fading.
[Automated review]
The unhandled TypeError in full runs came from a click guard a test left armed: its 400 ms disarm timer fired after the environment tore that file's window down, in a later file. A test that ends with the guard armed now checks the next test starts with it disarmed. Fails until the shared test setup disarms every guard as each test ends.
…est ends; the guard has no special case for a removed window The cause of the unhandled TypeError was test isolation, not the guard: a test that ended inside the guard's 400 ms window left its timer to fire after the environment tore that file's window down. The shared setup now disarms every guard in afterEach, so no timer outlives its test. The check 2d365a7 added to disarm, which skipped unsubscribing once the window looked gone, only covered for that leak (a browser keeps a closed frame's listener methods), so it goes, with its case.
… paint Button.spec.md FR15: main's text (meta#8613: the 200 ms release at tempo 1, scaled by the theme's tempo, spec:AST-108 FR4) with this pull request's own wording kept; the measured 250 ms release is likewise stated at tempo 1.
…busy first frame outran pressFeedback.ts removes data-astryx-press="fading" on a timer of the release's duration, started at the write. When the main thread is busy right after the lift, the release animation starts only at the first frame after the block, so the timer removes the release partway through it: on a list row after a 150 ms block, 0.736 -> 0.558 -> gone about 52 ms in. These fail on that code: - pressFeedback.test.ts: with a release animation the stylesheet started late (or still waiting for its first frame) when the clock runs out, the controller's release and another painter's (paintPress) stay fading until the animation finishes. Guards: a cancelled animation ends it, no animation ends it on the clock (reduced motion's instant release, a surface without a press layer), a descendant's, an endless or an earlier animation is not waited for, and a new press keeps its paint when the old release ends later. - ButtonPressRelease.interaction.browser.spec.ts, both engines: after 150 ms of script at the release, the button's layer keeps the release until its strength reaches 0.
…y first frame started it endAfterRelease replaces the clock that removed data-astryx-press="fading" at the release's duration after the write, for the controller's release and for every other painter's (paintPress). The clock now only looks: at the release's duration, a release animation the write started on the surface (one of its own CSS animations, on it or its ::before or ::after, finite, started at or after the write or still waiting for its first frame) is waited for until it finishes or is cancelled, so a busy main thread that starts the animation late ends it late too, at strength 0. With no such animation (reduced motion's instant release, a surface without a press layer, an engine without Web Animations) the release ends on the clock, as before. Any new write, or a new press, cancels the wait. A slot's pending clock is now a cancel function, so the release's wait and every timer clear the same way.
…the press-paint base
…nto the fine-pointer press paint
There was a problem hiding this comment.
Approval waits on one spec decision: the branch rewords the current component:Button FR15 (a 250 ms coarse-pointer release painted on ::before, 200 ms for a Space press and for fine pointers, and a Reduce Motion split) and adds a verification row for it, so an engineering owner needs to approve that record by commenting /approve-spec <full head SHA> on this pull request, or the record should keep main's FR15 text; the author may do so as an engineering owner. The selection controls' own mouse and pen press is covered by spec:AST-094 FR11, FR43 and DEC-21 on main. Whatever the FR15 decision, these still need changes:
- Rebase onto
mainonce the predecessor pull requests land, so this pull request carries its own 15 files (996 lines) instead of 271 files and 42,815 authored lines. - With Reduce Motion and a mouse, the checkbox and radio release still fades for 200 ms (
CheckboxInput.tsx,RadioListItem.tsx);design:reduced-motionDR3 asks for an immediate change, which the shared press overlay already makes with its(prefers-reduced-motion: reduce) and (not (pointer: coarse))arm. - A standalone checkbox's layer declares a 250 ms release while the press controller clears a fine pointer's press after about 200 ms, so the fade snaps off at about 15%; align the two clocks and assert how long the press stays
fading.
[Automated review]
Depends on #8233 and #8682 (and their predecessors). Review only this PR's change: vjeux/astryx@b809fd6...d96069b
User need
spec:AST-094FR43 gives a fine pointer a finger's press model. With a mouse, CheckboxInput, CheckboxList and RadioList still pressed through:active: a CheckboxList row pressed on its label left its box blank, a pen on a fine-pointer page pressed the box instead of the row, and a RadioList row kept its own:activepaint.Problem and solution fit
:activearms go.:active:not(:has(…))arm and its--_radio-row-presspaint go (fix(RadioList): the pressed circle reads the row's press through a custom property, not a :has() scan #8424's rule, which it replaces). Its source-build test pins the new boundary.Current specification
spec:AST-094FR43, FR11's fine-pointer profile, DEC-21; FR24.Public delta and caller burden
No API change. With a mouse or a pen, a checkbox and a radio option paint their press at
pointerdownand fade on release, as under a finger; a CheckboxList row pressed anywhere paints the row and its box. A right click and a read-only row paint nothing. Touch targets stay coarse-only. The stylesheet loses the pointer-query wrappers and:activearms.analyze-pr.js).Design and alternatives
Keeping
:activefor a mouse would leave two press models. Tests that pinned a coarse-only press are conformed, includingscripts/build-css.test.mjs's desktop-rule case (from #8118's line), narrowed to the Button's host layer: a selection mark's layer is its only press for every pointer.Evidence
Chromium, a fine primary pointer, a real Storybook build: a mouse marks the press at
pointerdownand the layer paints the pressed token, then releases on the host's clock (checkbox, CheckboxList row, RadioList row; each fails on the previous head). A held pen paints the same way. With a mouse or a pen, a CheckboxList row presses from its label and its box.Scope
Testing
CI=true pnpm lint0 errors;pnpm buildthen rootpnpm typecheckclean; IndicatorPressRelease, FinePointerPress, ButtonPressRelease and ListRowPress in Chromium 48 passed (1 skip); IndicatorPressRelease and FinePointerPress in WebKit 24 passed (3 skips); CheckboxListPress, PressModality and RadioGroup a11y 41/41; units 16,545 underCI=true, with no unhandled error; the load timeouts that test(Item): wait for the swipe layer's module, not a one-second window #8352, test(DropdownMenu): catch an eager sheet import when it is requested, not by a timer #8353, test(Resizable): load the package root while the file is collected, not inside a timed test #8471, test(Selector, MultiSelector): wait for the on-demand sheet module before the first open #8642 and test(DropdownMenu): the SSR test loads the menu and its sheet at collection #8685 fix failed on a loaded machine and each passes alone; a mouse click still toggles every story's checkbox and radio.CI=true pnpm lint0 errors;pnpm buildthen rootpnpm typecheckclean; IndicatorPressRelease, FinePointerPress, ButtonPressRelease and ListRowPress in Chromium 47 passed (1 skip); IndicatorPressRelease and FinePointerPress in WebKit 24 passed (3 skips); CheckboxListPress, PressModality and RadioGroup a11y 41/41; units 16,453 underCI=true, where the load timeouts that test(Item): wait for the swipe layer's module, not a one-second window #8352, test(DropdownMenu): catch an eager sheet import when it is requested, not by a timer #8353, test(Resizable): load the package root while the file is collected, not inside a timed test #8471, test(Selector, MultiSelector): wait for the on-demand sheet module before the first open #8642 and test(DropdownMenu): the SSR test loads the menu and its sheet at collection #8685 fix failed on a loaded machine and each passes alone; a mouse click still toggles every story's checkbox and radio.CI=true pnpm lint0 errors;pnpm buildthen rootpnpm typecheckclean; IndicatorPressRelease, FinePointerPress, ButtonPressRelease and ListRowPress in Chromium 47 passed (1 skip); IndicatorPressRelease and FinePointerPress in WebKit 24 passed (3 skips: pen input and Chromium-only cases); CheckboxListPress, PressModality and RadioGroup a11y 41/41; units 16,448 underCI=true, with load timeouts that test(Item): wait for the swipe layer's module, not a one-second window #8352, test(DropdownMenu): catch an eager sheet import when it is requested, not by a timer #8353, test(Resizable): load the package root while the file is collected, not inside a timed test #8471 and test(Selector, MultiSelector): wait for the on-demand sheet module before the first open #8642 fix; a mouse click still toggles every story's checkbox and radio.scripts/build-css.test.mjs23/23, including the narrowed desktop-rule case; the CLI files that timed out in the full run pass alone (13 files, 442 tests); the other node reds arestory-tree(main's, closed by test(storybook): run the story-tree check where Storybook is built, and bring the tree back to its shape #8509) andcache.test.mjs's npm-lock cases, which fail on main on this machine too.