Repository navigation
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
c1c5294 to
a361da6
Compare
a361da6 to
6154244
Compare
6154244 to
9be22df
Compare
There was a problem hiding this comment.
This needs an owner decision before it can be approved: no current spec covers a reorder handle opening its row's moves, or the new ListItem.reorderLabel prop. An engineering owner has to approve or reject both and identify the record that will govern them, recording that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so if they are an engineering owner. Whatever that decision, these need fixing:
- A real mouse click on a handle opens nothing: the handle's own mouse long-press recognizer makes the gesture host swallow the click, so the documented click path fails for mouse users, and a real-browser mouse-click test would catch it.
- The menu opens from an Effect one frame after the click and reads the DOM during render; opening it from the click handler, with the moves and the name computed there, would follow the React runtime rules.
- The diff carries the whole stack, about 44,600 authored lines, so landing #8139 and its predecessors first would leave only this pull request's own commit.
- The disabled-row test still passes without the disabled guard, because that row has no moves anyway.
[Automated review]
| aria-disabled={isDisabled || undefined} | ||
| aria-haspopup="menu" | ||
| aria-expanded={menu === 'open'} | ||
| onClick={() => { |
There was a problem hiding this comment.
A real mouse click never reaches this handler. The mouse LongPressGestureRecognizer above (minimumPressDuration: 0) recognizes every press, so GestureHost arms its stray-click swallower on pointerup and drops the trusted click. In Chromium a press and release on a handle opens no menu; only touch taps and clicks with detail === 0 get through. Opening the moves when the mouse recognizer ends without a changed, plus a page.mouse.click browser proof, would make the documented "a click" path work.
| // the handle, and a dragged handle would then lay out on every move. | ||
| // Mounted closed, then opened, so the menu moves focus into its rows. | ||
| const [menu, setMenu] = useState<'closed' | 'mounting' | 'open'>('closed'); | ||
| useEffect(() => { |
There was a problem hiding this comment.
This Effect only sequences React state (mount closed, open a frame later) and names no external system (architecture:react-component-runtime INV3); it also defers the click's open (INV2). Could the click handler compute the moves and the name, store them in the menu state, mount closed under flushSync and open in the same handler? That would also remove the render-time DOM reads at lines 318 and 331-333 (INV1, INV4).
| it('offers no moves from a disabled row, nor from a row that cannot move', () => { | ||
| render( | ||
| <List onReorder={() => {}}> | ||
| <ListItem label="Inbox" isDisabled /> |
There was a problem hiding this comment.
Inbox sits alone above the fixed Sent, so movesFor already offers it no moves, and this test passes even with the !isDisabled guard removed. A disabled row inside a movable group (no fixed row) would prove spec:AST-085 FR2 for the new menu.
9be22df to
e96e626
Compare
There was a problem hiding this comment.
This needs an owner decision before it can be approved: no current spec covers a reorder handle opening its row's moves, or the new ListItem.reorderLabel prop. An engineering owner has to approve or reject both and identify the record that will govern them, recording that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so if they are an engineering owner. Whatever that decision, these need fixing:
- A real mouse click on a handle opens nothing: the handle's own mouse recognizer makes the gesture host swallow the click, so the documented click path fails for mouse users, and a real-browser mouse-click test would catch it.
- After a drag, the next assistive-technology activation of the handle is ignored, because the drag flag is cleared only by the next pointer press.
- The menu opens from an Effect one frame after the click and reads the DOM during render; opening it from the click handler, with the moves and the name computed there, would follow the React runtime rules.
- The diff carries the whole stack, about 44,500 authored lines including a press change that fails a RadioList test in CI, so landing #8139 and its predecessors first would leave only this pull request's own commit.
[Automated review]
| aria-label={name} | ||
| aria-describedby={context.hintId} | ||
| aria-disabled={isDisabled || undefined} | ||
| aria-haspopup="menu" |
There was a problem hiding this comment.
aria-haspopup="menu" is set on every handle, but activation opens a menu only when the row has moves (line 275). On a row alone in its group, such as Drafts below a fixed Sent in the new test, a screen reader announces a menu button that does nothing. Could the popup state be exposed only on a row that can move, derived from the list's children rather than the DOM?
| // The row's own pointer handling (its swipe) never sees the handle's | ||
| // touches; the gesture system listens on the document, before this. | ||
| onPointerDown={(event: PointerEvent<HTMLButtonElement>) => { | ||
| draggedRef.current = false; |
There was a problem hiding this comment.
Apart from the click itself, this is the only place draggedRef is cleared, and a drag's own click never arrives because the gesture host suppresses it. After any drag the flag stays set, so the next detail 0 activation, which brings no pointerdown, is swallowed: in Chromium, after a mouse drag, the first click() on the handle opened nothing and the second opened the moves. Clearing the flag when the drag ends or is cancelled would fix it.
e96e626 to
d02fc3d
Compare
There was a problem hiding this comment.
This needs an engineering owner's decision before it can be approved: no current spec covers a reorder handle that opens its row's moves when activated without a drag, or the new ListItem.reorderLabel prop. An engineering owner has to approve or reject both and identify the record that will govern them, recording that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so if they are an engineering owner. Whatever that decision, these need fixing:
- A real mouse click on a handle opens nothing, because the handle's own mouse recognizer makes the gesture host swallow the click, so mouse users who cannot drag still have no path, and a real-browser
page.mouse.clicktest would catch it. - After a drag, the next activation of that handle with no pointer press, which is how the tests model a screen reader, is ignored, because the drag flag is cleared only by the next pointer press.
- The menu opens from an Effect a frame after the click and reads the DOM during render, and computing the moves and the name in the click handler would follow the React runtime rules.
- The diff carries the whole stack, about 44,600 authored lines, so landing #8139 and its predecessors first would leave only this pull request's own commit.
[Automated review]
|
Posted by Cindy's agent on her behalf. Cindy's rule for sizes on phones and touch screens: the default look doesn't grow controls to match iOS. Under a touch pointer only, touch targets reach 44 px through an invisible hit area, and that hit area never overlaps a neighboring control's target. Where neighbors are closer than 44 px, it's clamped to half the gap, or skipped. Shrinking something to fit a phone is fine. Mobile size increases will come from theme adaptations. For this PR: please keep the visible size unchanged and give the target its 44 px through an invisible, coarse-pointer-only hit area, clamped against its neighbors. A shared internal helper for that clamp is on its way. If this stays idle, we'll make the change after about 30 minutes, keeping your authorship. |
|
Thanks, please go ahead; this stack will fold your change and conform its proofs at the next push. |
There was a problem hiding this comment.
Changes are needed before this can merge: the reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, but the diff still carries the whole stack under #8139 and the hint's own browser proofs fail at this head. Please:
- Merge #8139 and its predecessors first, or rebase after they land, so this pull request holds only its own 964 changed lines instead of 40,690.
- Fix
ListReorderMode.interaction.browser.spec.ts:labels()counts the hidden insertion-linelithat reorder mode now adds, and the target case still requires 44 px button boxes, which the clamped hit layer no longer provides (6 of its 9 cases fail in Chromium). - Add a test that fails if the reorder hint leaves the keyboard hint's anchoring or theme roles, as the FR4 hint verification row in
spec:AST-103asks.
Because this pull request edits a current family record, the spec-owner-approval check also waits for an engineering owner's approval on the final head.
[Automated review]
| const labels = async (page: Page) => | ||
| page | ||
| .locator('#storybook-root li') | ||
| .evaluateAll(rows => rows.map(element => element.textContent ?? '')); |
There was a problem hiding this comment.
In reorder mode the list now also holds #8139's insertion line, an aria-hidden li (listReorderEngine.tsx:236, :273-276), and this helper reads it as a row: the Space, Enter, both start(key) and taps-alone cases time out in Chromium on an extra "" entry. Reading #storybook-root li:not([aria-hidden]), or rows by the listitem role, would fix all five.
| boxes.push({name, box, glyph}); | ||
| } | ||
| for (const {name, box, glyph} of boxes) { | ||
| expect(box.width, `${name} is 44 px wide`).toBeGreaterThanOrEqual(44); |
There was a problem hiding this comment.
At this head the hint's buttons keep their size and a finger's target is the clamped ::after layer, so this fails in Chromium: “Move up is 44 px wide … Expected: >= 44, Received: 20”. The centring check below now compares each button with its own glyph, the same box. Could this case measure the hit layer instead: each glyph's points hit its own button, a tap 1 px inside each layer edge reaches it, the target holds a centred minimum square, and no two layers overlap?
| expect(hintButton('Move up')).toBeInTheDocument(); | ||
| expect(hintButton('Done')).toBeInTheDocument(); | ||
| // In the anchored hint layer, outside the list; never a tab stop. | ||
| expect(down.closest('[popover]')).not.toBeNull(); |
There was a problem hiding this comment.
spec:AST-103's FR4 hint verification row expects “a hint off the keyboard hint's layer, anchoring, or theme roles” to fail, but this only checks for some [popover] ancestor. Asserting that the hint's popover carries the shared anchoredHint__styles.hint class, as useKeyboardHint.test.tsx:70 does for the keyboard hint, would pin it.
|
Posted by Cindy's agent on her behalf. Cindy's decision on gestures on the web: gestures attach for every pointer, not only touch.
This supersedes the earlier exclusion of mouse press paint. The |
There was a problem hiding this comment.
Changes are needed before this can merge: the reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, but the diff still carries the whole stack under #8139 and one of the hint's own browser proofs fails at this head. Please:
- Merge #8139 and its predecessors first, or rebase after they land, so this pull request holds only its own 1,128 changed lines instead of 38,806.
- Fix the finger-target case in
ListReorderMode.interaction.browser.spec.ts: it requires every hint button to be narrower than 44 px, but under the touch type scale Done's text makes it 44.03 px wide in Chromium, so the case fails before its hit-layer checks run; asserting that a coarse pointer adds no padding or minimum size would test what the case means.
Because this pull request edits a current family record, the spec-owner-approval check also waits for an engineering owner's approval on the final head.
[Automated review]
| ); | ||
| for (const {name, box, hit} of targets) { | ||
| // Its own size: no wider for a finger. | ||
| expect(box.right - box.left, `${name} keeps its size`).toBeLessThan(44); |
There was a problem hiding this comment.
This fails in Chromium at this head: “Done keeps its size … Expected: < 44, Received: 44.03125”. Under the phone profile the coarse-pointer type scale raises Done's text from 12 to 14 px, so its box widens from 38.89 px (mouse) to 44.03 px while its padding stays 4 px and min-width stays auto; the width depends on the platform's fallback font, since no web font loads. The non-overlap, hit-test and edge-tap checks below never run. Could this assert that the coarse pointer adds no padding or minimum size instead, or keep the < 44 ceiling only for the 20 px glyph buttons?
There was a problem hiding this comment.
Changes are needed before this can merge: the reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, but the diff still carries the whole stack under #8139, and the hint's finger-target browser proof checks the wrong thing. Please:
- Merge #8139 and its predecessors first, or rebase after they land, so this pull request holds only its own 1,171 changed lines instead of 40,505.
- In the finger-target case of
ListReorderMode.interaction.browser.spec.ts, assert that a coarse pointer adds no padding or minimum size instead of capping every button, Done included, below 44 px, a width that depends on the platform's font.
Because this pull request edits a current family record, the spec-owner-approval check also waits for an engineering owner's approval on the final head.
[Automated review]
| ); | ||
| for (const {name, box, hit} of targets) { | ||
| // Its own size: no wider for a finger. | ||
| expect(box.right - box.left, `${name} keeps its size`).toBeLessThan(44); |
There was a problem hiding this comment.
This caps every hint button, Done included, below 44 px. Done's width is its label in the platform's font under the mobile type scale, so the identical case failed in Chromium at the previous head (Done 44.03 px) and the non-overlap and hit-test checks below never ran, while a Done that did grow for a finger, up to 44 px, would still pass. Comparing each button's box under the coarse pointer with its box under a fine pointer, or asserting that no padding or minimum size is added, would test what the case means; the < 44 ceiling can stay for the 20 px glyph buttons.
There was a problem hiding this comment.
Changes are needed before this can merge: the reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, but the diff still carries the whole stack under #8139, and the hint's finger-target browser proof checks the wrong thing. Please:
- Merge #8139 and its predecessors first, or rebase after they land, so this pull request holds only its own 1,171 changed lines instead of 39,305.
- In the finger-target case of
ListReorderMode.interaction.browser.spec.ts, compare each hint button's box under the coarse pointer with its box under a fine pointer instead of capping every button, Done included, below 44 px, sincespec:AST-094FR42 leaves a box that grows only with its text outside its rule.
Because this pull request edits a current family record, the spec-owner-approval check also waits for an engineering owner's approval on the final head.
[Automated review]
| ); | ||
| for (const {name, box, hit} of targets) { | ||
| // Its own size: no wider for a finger. | ||
| expect(box.right - box.left, `${name} keeps its size`).toBeLessThan(44); |
There was a problem hiding this comment.
This ceiling makes the case measure Done's label in the platform's font: in this phone context the mobile type scale applies (design:mobile-typography DR3), and a label of 36 px or more, plus --spacing-1 on each side, fails it, although spec:AST-094 FR42 puts "a box that grows only with its text" outside its rule; the non-overlap check and hit-tests below then never run. A button that did grow for a finger, up to 44 px, would still pass. FR42's verification asks for geometry "under a coarse and a fine pointer": could this compare each button's box with its box in a fine-pointer context (or assert that the coarse pointer adds no padding or minimum size), keeping the 44 px ceiling only on the hit layer, as lines 474-475 already do?
There was a problem hiding this comment.
Changes are needed before this can merge: the reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, but the diff still carries the whole stack under #8139, and the hint's finger-target browser proof checks the wrong thing. Please:
- Merge #8139 and its predecessors first, or rebase after they land, so this pull request holds only its own 1,171 changed lines instead of 39,359.
- In the finger-target case of
ListReorderMode.interaction.browser.spec.ts, compare each hint button's box under the coarse pointer with its box under a fine pointer instead of capping every button, Done included, below 44 px, sincespec:AST-094FR42 leaves a box that grows only with its text outside its rule.
Because this pull request edits a current family record, the spec-owner-approval check also waits for an engineering owner's approval on the final head.
[Automated review]
| ); | ||
| for (const {name, box, hit} of targets) { | ||
| // Its own size: no wider for a finger. | ||
| expect(box.right - box.left, `${name} keeps its size`).toBeLessThan(44); |
There was a problem hiding this comment.
This caps every hint button below 44 px, Done included, so Done's label width under the mobile type scale decides the case (spec:AST-094 FR42 puts a box that grows only with its text outside its rule), and a button that grew for a finger up to 43 px would still pass. FR42's verification asks for geometry under a coarse and a fine pointer: compare each button's box here with its box under a fine pointer (or assert the coarse pointer adds no padding or minimum size), and keep the 44 px ceiling on the hit layer only, as the lines below already do.
There was a problem hiding this comment.
Changes are needed before this can merge: the reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, and its finger-target browser proof now compares each button with its fine-pointer box as spec:AST-094 FR42 asks, but the diff still carries the whole stack under #8139. Please merge #8139 and its predecessors first, or rebase after they land, so this pull request holds only its own 1,252 changed lines instead of 39,688. Because this pull request edits a current family record, the spec-owner-approval check also waits for an engineering owner's approval on the final head.
[Automated review]
|
Posted by Cindy's agent on her behalf: closed and reopened only to restart this PR's CI on the current workflow after tonight's queue-wide cancel; nothing in the PR changed. |
There was a problem hiding this comment.
Changes are needed before this can merge: the reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, but the diff still carries the whole stack under #8139 (37,508 changed lines against this pull request's own 1,252), and GitHub reports it as conflicting with main. Please merge #8139 and its predecessors first, or rebase after they land, so this pull request holds only the hint; the carried List.hasAnimatedChanges also disagrees with component:List FR12, which now specifies an animatedLayout handle. One small fix in the hint itself: the Effect at listReorderEngine.tsx:2024-2028 names no external system, which architecture:react-component-runtime INV3 requires, so fold it into the show and hide Effect or read the layer through an Effect Event.
[Automated review]
| // No external system: keeps the latest layer handle for the effect below, | ||
| // which re-runs only when the mode's row changes. Idempotent. | ||
| useIsomorphicLayoutEffect(() => { | ||
| layerRef.current = layer; | ||
| }); |
There was a problem hiding this comment.
This Effect says it synchronizes no external system, but architecture:react-component-runtime INV3 says a new Effect MUST name the external system it synchronizes. Fold the layer handle into the show and hide Effect below, or read it through an Effect Event, and delete this one.
|
An offer for the review's Effect point: offer/list-reorder-hint-effect fast-forwards this PR's head with one commit. The hint's show-and-hide Effect now reads the layer's anchor, show and hide directly. Those keep their identity while the layer opens and closes, so the Effect still runs only when the mode's row changes. That removes the separate Effect that kept the latest layer in a ref, which named no external system ( If you want it here, merge the branch. The rest of the review is about the stack under this PR. |
There was a problem hiding this comment.
This needs an engineering owner's decision before it can be approved, and it needs changes either way.
Owner decision: spec:AST-103 FR4 and DEC-5 define the reorder hint, but no record states the wording of its two move buttons, "Move up" and "Move down" (FR1 names only the moveUp and moveDown label keys). An engineering owner needs to settle that wording, by stating it in spec:AST-103 or by commenting /approve-spec <full head SHA> with that decision; the author may do so if they are an engineering owner.
Blocking findings:
- The diff carries the whole open stack under #8139 (37,500 authored lines, 1,252 of them this pull request's) and no longer merges with
main, where 85 files conflict, includinghooks/useKeyboardHint.tsxanddocs/families/overlay-dismissal.md; land #8139 and its predecessors or rebase so only this pull request's commits remain. - The layout Effect at
listReorderEngine.tsx:2024-2028names no external system, whicharchitecture:react-component-runtimeINV3 requires; fold it into the show-and-hide Effect as the offered branch does. HintButton(listReorderEngine.tsx:1944-1982) and the reorder handle start their ownwatchHitAreaand::afterlayer instead of the shareduseTouchHitAreaandtouchHitAreaStyles.layer, so the hint ignores a page's larger touch target; use the shared hook and delete the copies.- Done is drawn in the primary text role at semibold (
listReorderEngine.tsx:1951-1955), whilespec:AST-103FR4 asks for the keyboard hint's theme roles. - Stacked:
List.hasAnimatedChanges(List.tsx:88) contradictscomponent:ListFR12, which names ananimatedLayouthandle. - Stacked: a mouse click in a link row's padding now follows the link (
Item.tsx:749-767), a desktop behavior change that needs an owner decision or a touch-only gate. - Stacked:
createReorderableexposes onlystart(createReorderable.ts:88), whilespec:AST-103FR4b requiresattach.
The hint's own behavior matches FR4 in the head Storybook and its unit tests pass; the stacked findings belong to the pull requests that own those changes.
[Automated review]
| // No external system: keeps the latest layer handle for the effect below, | ||
| // which re-runs only when the mode's row changes. Idempotent. | ||
| useIsomorphicLayoutEffect(() => { |
There was a problem hiding this comment.
This Effect says it synchronizes no external system, and architecture:react-component-runtime INV3 says a new Effect must name the one it synchronizes. The layer's ref, show and hide keep their identity, so the show-and-hide Effect below can read them from layer directly and this Effect can go (the offered branch does that).
| useIsomorphicLayoutEffect(() => { | ||
| const button = ref.current; | ||
| if (button == null) { | ||
| return undefined; | ||
| } | ||
| return watchHitArea(button, insets => writeHitInsets(button, insets)); | ||
| }, []); |
There was a problem hiding this comment.
This repeats what useTouchHitArea (utils/touchHitArea.ts) and touchHitAreaStyles.layer already do for List rows, minus the page's --size-touch-target, the wait for animations to rest and the clipped layer, so a page that sets --size-touch-target above 44 px grows rows but not these buttons. Calling useTouchHitArea(ref, true) with touchHitAreaStyles.layer, here and in ListReorderHandle, removes this mount Effect and the ::after copy above.
| color: colorVars['--color-text-primary'], | ||
| fontWeight: fontWeightVars['--font-weight-semibold'], |
There was a problem hiding this comment.
Done renders black at 600 beside the moves' secondary grey at 400 (measured in the head Storybook). spec:AST-103 FR4 asks for the keyboard hint's theme roles, and its verification row fails a hint off them; dropping color and fontWeight here keeps Done in the hint's supporting text.
|
/approve-spec b637049 |
There was a problem hiding this comment.
Changes are needed before this can merge. An engineering owner's /approve-spec covers this head's spec content, including the hint's Move up and Move down wording, and the hint itself does what spec:AST-103 FR4 and DEC-5 specify, but four things need fixing:
- The diff carries #8106 and #8139 (8,679 of its 9,912 authored lines), and the description's stack line is not one identical
Stack: #a → #b → #cdeclaration across those pull requests; land them first, or rebase after they land, so only this pull request's commit remains. - Done is drawn in the primary text colour at semibold (
listReorderEngine.tsx:1954-1958), whilespec:AST-103FR4 asks for the keyboard hint's theme roles (its secondary text at regular weight); drop those two declarations and have the browser theme-role test compare Done. docs/specs/components/List/List.spec.md:60still saysreorderableis built "but for its mode's hint", which this pull request builds; drop "its mode's hint and".- Regression:
textOfinListReorderHandle.tsx:225-237copies the sharedgetNodeText(utils/getNodeText.ts) thatmainnow has, and the two already join adjacent text differently; call the shared utility, extending it if List needs another join, and delete the copy.
Non-blocking:
- Miss:
endMode's andblur's comments still say a blur ends reorder mode (listReorderEngine.tsx:765-766,:1794), which the code no longer does, andisMovingByKeyis never read.
[Automated review]
| done: { | ||
| paddingInline: spacingVars['--spacing-1'], | ||
| color: colorVars['--color-text-primary'], | ||
| fontWeight: fontWeightVars['--font-weight-semibold'], | ||
| }, |
There was a problem hiding this comment.
Done measures rgb(0, 0, 0) at 600 here while the moves and the keyboard hint's label are rgb(71, 71, 71) at 400 (the hint's --color-text-secondary). spec:AST-103 FR4 asks for "the same layer, anchoring, and theme roles as Core's keyboard hint". Keep paddingInline and drop color and fontWeight, so Done takes the hint's text role.
There was a problem hiding this comment.
Fixed in e856639: Done keeps only its padding and takes the hint's text role. The theme-role case now compares Done, and it fails on the previous head.
| | End-user impact | Row text can share the line of the list header and a heading above, with row paint stopping at the text, reaching into the host padding, or reaching the host edges. Touch users get a 44 px row target (FR10) and the cell press (FR11); with `animatedLayout`, row updates animate for every pointer (FR12). A row with a menu opens it by a long press, a right click, or Shift+F10, or through its More actions button (FR13). | | ||
| | Builder impact | One `edge` choice per list. `flush` works anywhere; `outset` and `bleed` act only inside a host that publishes container padding. Passing an `animatedLayout` handle is one new choice, and a row menu is the other: the caller passes a ListItem's ref to `useContextMenu` (FR13), and who draws a plain row's More actions button is open (OQ2); and rows animate only with an `itemKey`; the touch target and press need none. | | ||
| | Compatibility/readiness | Additive; omission keeps current geometry; `edgeCompensation` keeps working with a codemod to `edge`. `reorderable` and `itemKey` are additive and stable, specified and not implemented yet; without them nothing changes. `animatedLayout` is additive and specified, not implemented yet. The touch target (FR10) and the press's compositor release (FR11) are implemented under coarse primary pointers. Neither changes anything under a fine pointer. Row menus (FR13) are additive and specified, not implemented yet. | | ||
| | Compatibility/readiness | Additive; omission keeps current geometry; `edgeCompensation` keeps working with a codemod to `edge`. `reorderable` is stable and built, but for its mode's hint and `labels`. `itemKey` and `animatedLayout`'s row updates (FR12) are implemented; its start and end content motion is not. The touch target (FR10) and the press's compositor release (FR11) are implemented under coarse primary pointers. Neither changes anything under a fine pointer. Row menus (FR13) are additive and specified, not implemented yet. | |
There was a problem hiding this comment.
This pull request builds the hint, so "but for its mode's hint and labels" is no longer true. Suggested: "reorderable is stable and built, but for labels."
There was a problem hiding this comment.
Fixed in e856639: "reorderable is stable and built, but for its labels and positions." Neither option is built yet.
| function textOf(node: ReactNode): string { | ||
| if (typeof node === 'string' || typeof node === 'number') { | ||
| return String(node); | ||
| } | ||
| if (Array.isArray(node)) { | ||
| return node.map(textOf).join(''); | ||
| } | ||
| if (isValidElement<{children?: ReactNode}>(node)) { | ||
| return textOf(node.props.children); | ||
| } | ||
| return ''; | ||
| } | ||
|
|
There was a problem hiding this comment.
Regression: main now has getNodeText (utils/getNodeText.ts), the shared owner for a node's spoken text (Toast announcements, Table column-resize handle names). This copy joins siblings with no space where getNodeText joins with one, so ['Inbox', <b>3</b>] is spoken "Inbox3" here and "Inbox 3" there. Call getNodeText (extend it if List needs another join) and delete textOf.
| * into the hint goes back to the handle. A blur, a drag or the row leaving | ||
| * ends it without a word. |
There was a problem hiding this comment.
Non-blocking (INV29): blur() keeps the mode (:1817-1819), as spec:AST-103 FR4a wants, so this sentence and blur's JSDoc (:1794) describe a rule the code does not follow. Suggested: "A drag or the row leaving ends it without a word." isMovingByKey (:611-615) is also written and never read.
|
/approve-spec e856639 |
A List that takes the handle useAnimatedLayout returns animates its row changes on one shared motion, for every pointer. An inserted row enters, a removed row collapses in place, and a row that crosses rows moving the other way rides above them. The rows after a change, and the content after the list, glide to their new places. Rows match across renders by their itemKey; a row without one changes at once. The row marks the update module writes (item key, leaving, travelling) live in their own private-attribute owner modules. A list under the handle isolates for every pointer, so a sliding row stays below a page-level sticky header. The List and ListItem records now say the row updates and itemKey are implemented, and their verified_by lists the files that verify them.
There was a problem hiding this comment.
Changes are needed before this can merge. The reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, the three fixes from the last review (Done's text role, the List record's readiness line, the shared getNodeText) are in, and an engineering owner's /approve-spec covers this head's spec content. One thing remains:
- The diff carries #8106 and #8139 beside this pull request's own commit (10,179 authored lines, 1,246 of them its own), and the description's
Stack:line is not one identical declaration across #8106, #8139, #8143 and #8163, so land #8106 and #8139 first or rebase after they land, or give every listed pull request the sameStack:line.
Non-blocking:
- The open hint covers the next row's handle, so a click or tap aimed at its centre lands on Done and ends reorder mode instead of moving it to that row.
- Miss: List's per-commit Effect (
List.tsx:325-328) could name the reorder session it synchronizes and why it runs after the commit.
[Automated review]
createReorderable({onReorder, isReorderable}) makes a collection's
reorder behavior, and List takes it as `reorderable`. Each ListItem
with an itemKey gets a reorder handle at its end, and each committed
move calls onReorder({key, to}) once.
Under a finger, a 150 ms hold on a handle picks its row up, and a
flick on it still scrolls. The rows stay still while a dimmed source,
a preview and an insertion line show the move. On release they glide
on the shared settle role. A pen holds the same way where the primary
pointer is coarse. A mouse, or a pen where the primary pointer is
fine, drags a handle at once. A click, Space or Enter turns on reorder
mode: the insertion line marks the row's place, the arrow keys move
the row and announce its place, and Escape ends the mode.
The reorder's machinery ships with the object, so a List without
`reorderable` loads none of it. The UIKit table's reorder values and
the drop model come in here with their first user. With an
animatedLayout handle, a drag's commits stay the reorder's own. The
CLI lists createReorderable with the hooks.
While a reorderable List row is in reorder mode, a hint anchored below its handle shows Move up, Move down and Done until the mode ends. Each move does what its key does, with the same announcement, and leaves the focus on the handle. Done ends the mode as Escape does, keeping the moves. So a pointer or a finger can reorder by taps alone, as WCAG 2.5.7 asks. A screen reader that moves its focus into the hint keeps the mode, and the focus goes back to the handle. The hint shares the keyboard hint's layer: hooks/anchoredHint.tsx holds the anchored hint both render, and useKeyboardHint renders its hint through it unchanged. The hint joins the shared dismissal stack, so an Escape in it ends the mode and leaves a Dialog below it open.
There was a problem hiding this comment.
Changes are needed before this can merge. The reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, and its record edits and strings carry an engineering owner's approval. One thing remains:
- The diff still carries #8106 and #8139 beside this pull request's own commit (10,233 authored lines, 1,246 of them its own), and the
Stack:declaration does not hold yet: #8163's body lacks the identicalStack: #8106 → #8139 → #8337 → #8143 → #8163line and its head is not built on #8143's current head, so update #8163 or land #8106 and #8139 first and rebase.
Non-blocking:
- Miss:
endMode's comment says a drag ends reorder mode "without a word" (listReorderEngine.tsx:778-779), but a drag ends it throughendMode()and announces "Done, …". - The open hint covers the next row's handle, so a click or tap aimed at that handle's centre lands on Done and ends reorder mode.
[Automated review]
| * into the hint goes back to the handle. A drag or the row leaving ends it | ||
| * without a word. |
There was a problem hiding this comment.
Non-blocking: a drag does end the mode with a word. begin calls endMode() (:1077-1080, "A drag ends reorder mode, keeping its moves, and says so"), which announces "Done, …". Suggested: "The row leaving ends it without a word."
|
/approve-spec 1f67838 |
left a comment
There was a problem hiding this comment.
Approved pending CI: CI's test jobs skipped this draft head, so the hint's browser specs have not run in CI yet. The reorder-mode hint does what spec:AST-103 FR4 and DEC-5 specify, its record edits and strings are owner-approved, and the Stack: declaration now holds, so #8106 and #8139 are reviewed in their own pull requests. This pull request merges only after #8106 and #8139 are approved and land.
Non-blocking:
- Miss:
endMode's comment says a drag ends reorder mode "without a word" (listReorderEngine.tsx:778-779), but a drag ends it throughendMode()and announces "Done, …". - The open hint covers the next row's handle, so a click or tap aimed at that handle's centre lands on Done and ends reorder mode.
[Automated review]
| * into the hint goes back to the handle. A drag or the row leaving ends it | ||
| * without a word. |
There was a problem hiding this comment.
Non-blocking: a drag does end the mode with a word. begin calls endMode() (:1077-1080, "A drag ends reorder mode, keeping its moves, and says so"), which announces "Done, …". Suggested: "The row leaving ends it without a word."
Stack: #8106 → #8139 → #8337 → #8143 → #8163
This merges together with #8139: it completes #8139's reorder mode with the hint and its buttons. Review only this PR's change: vjeux/astryx@5e893e4...1f67838
Why
Reorder mode moves a row with the arrow keys. Someone using a mouse or a touch screen who can't drag has no arrow keys, and WCAG 2.2 requires a way to move it without dragging (SC 2.5.7).
What led to it
This is part of the UIKit-parity program, which makes Astryx's components behave like their iOS counterparts on touch screens.
spec:AST-103FR4 and DEC-5 call for on-screen moves in reorder mode. A small hint now appears below the handle, with Move up, Move down and Done:Options and tradeoffs
Why this choice
DEC-5 chose the hint, because it shows the moves where the row is, without opening a menu. The hint is the keyboard hint's own layer:
hooks/anchoredHint.tsxnow holds the anchored hint both render, and the keyboard hint renders through it unchanged. On touch, each button's tap area grows toward 44 px through the shared touch hit area (spec:AST-094FR42), so the hint looks the same with a mouse and with a finger. It adds three strings.How to check it
In Storybook
Core/List→ Reorderable:Gates at e856639, whose own change this head carries unchanged on #8139's new head:
check:knowledge,check:changesetsandcheck:repo: 0.review:pre: only the size note, counted from main.start(key), plus a check that tap areas never overlap.Evidence
Tap, Move down, Done on iOS 26.2. It was captured before Done took the hint's text role, so Done there is darker and bolder than it is now.
Writing rubric results
Over
family:overlay-dismissal(List's reorder-mode hint joins the members) andcomponent:List(the readiness line andverified_by):Pull request size
1,159 authored lines in 14 files: 312 of source, 813 of tests, and the rest the records, strings, docs and a changeset.