Skip to content

[DREAM-886] Focus model and cell navigation for the work package table - #26259

Open
myabc wants to merge 14 commits into
devfrom
implementation/dream-886-focus-model
Open

myabc wants to merge 14 commits into
devfrom
implementation/dream-886-focus-model

Conversation

@myabc

@myabc myabc commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Ticket

https://community.openproject.org/wp/DREAM-886

What are you trying to accomplish?

tl;dr for humans: Getting past the work package table by keyboard takes dozens of Tab presses today; this makes the whole table one Tab stop that you move around in with the arrow keys, like a spreadsheet.

The table becomes a single Tab stop with a roving tabindex: Tab enters on the first row (or the remembered position), arrow keys move between rows and cells, Enter activates, and Tab/Shift+Tab leave in one press. Behind keyboardNavigationEnabled, which only the main work package list view turns on; embedded tables and the BCF list are unchanged until DREAM-896.

Behaviour

Key Row focus Cell focus
Tab / Shift+Tab Leaves the table; re-entering lands on the remembered position Same
Arrow Up / Down Previous / next row, including header and totals; collapsed rows are skipped Same column in the previous / next row
Arrow Right Expands a collapsed parent or group, otherwise moves into the first cell Next cell
Arrow Left Collapses an expanded parent or group Previous cell; from the first cell, back to the row
Home / End First / last row First / last cell of the row
Page Up / Down One visible page of rows Same, keeping the column
Ctrl/Cmd+Home / End First / last row First cell of the first row / last cell of the last row
Enter Work package row: same as a click; group header: toggles collapse Header cell: sort menu or column configuration; editable cell: opens the inline editor
Escape / save in an editor – Focus returns to the edited cell

Clicks set the position too. Other modified keys pass through to existing shortcuts (Ctrl/Cmd+A, Shift+Alt+F10); Shift+Arrow is left for DREAM-894 (#26294).

Screenshots

What approach did you choose and why?

A pure position model (focus-position.ts) driven by one keydown listener on the scroll container, so header and body rows share one code path. One rule (isNavigableColumn) decides which cells are visited, so vertical moves keep their column. Inner controls (ID link, toggles, menu buttons, sort links) leave the Tab order; focus landing on one moves to its cell or row. ARIA semantics follow in DREAM-885 (#26288).

A re-render restores focus only if the table owned it and the user has not moved elsewhere; after an inline save, focus returns to the edited cell.

Two fixes apply without the flag: a form that survives a required-column redraw registers itself again, and Escape from the column header menu on a relation column no longer throws.

Known gaps

  • No keyboard route yet to the relation expander, the row menu button (Shift+Alt+F10 still works) or the ID link (Enter on the row opens the work package); date and progress editors do not return focus on close. All DREAM-895.
  • Choosing a column header menu action does not return focus to the table (DREAM-894).
  • The inline-create button stays a native Tab stop after the grid (DREAM-860).
  • DREAM-897 is the release gate: no incomplete keyboard state ships.

After the mob review (2026-10-26)

Three follow-up commits on top of the reviewed ones: row action icons (context menu, drag handle) are shown while the row or one of its cells has keyboard focus (they were hover-only); the global j/k shortcuts move between visible rows from cell focus too and skip collapsed rows; the pointer-collapse smoke example is dropped because the transformer spec pins that case. A fourth commit drops an ESLint directive that dev no longer needs.

Ruled for DREAM-895, not this PR: Tab from a focused row or cell walks that row's interactive controls and leaves the table after the last one.

AI involvement

Collaborative – AI generated a substantial part of the code; I reviewed and understand every line.

Hints for review

  • Start with focus-position.ts and its spec, then keyboard-navigation-transformer.ts and roving-tabindex.ts (all in wp-fast-table/handlers/state/keyboard/).
  • End to end: spec/features/work_packages/table/keyboard_navigation_spec.rb. The table page object gains keyboard focus helpers, and select_all_work_packages now sends Ctrl/Cmd+A to the roving stop.
  • The scroll container gets tabindex="-1" so Firefox does not stop on it; clicking empty space in it now focuses it.
  • QA with VoiceOver and NVDA: moving the reading cursor onto an ID or sort link must not trap it in that cell.

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...)

@myabc
myabc added this pull request to stack #25836 October 22, 2026 21:24
@myabc myabc changed the title Implementation/dream 886 focus model [DREAM-886] focus model Oct 22, 2026
@myabc myabc added Accessibility javascript Pull requests that update Javascript code DO NOT MERGE labels Oct 22, 2026
@github-actions github-actions Bot added the ai: Collaborative 💻 AI generated a substantial part of the code; A human reviewed and understands every line. label Oct 22, 2026
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/work_packages/table/queries/id_filter_spec.rb[1:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #26259, linked for reference only):

- `rspec ./spec/features/work_packages/table/queries/id_filter_spec.rb[1:1]`

Treat this as a standalone task, unrelated to PR #26259. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #26259 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @myabc to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @myabc, and request a review from @myabc.
On every commit, set @myabc as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

@myabc myabc changed the title [DREAM-886] focus model [DREAM-886] Focus model and cell navigation for the work package table Oct 22, 2026
@myabc myabc added this to the 18.0.x milestone Oct 22, 2026
@myabc
myabc force-pushed the implementation/dream-886-focus-model branch from 6bd2a45 to 6fc634b Compare October 22, 2026 21:41
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/components/lookbook_accessibility_spec.rb[1:14:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #26259, linked for reference only):

- `rspec ./spec/features/components/lookbook_accessibility_spec.rb[1:14:1]`

Treat this as a standalone task, unrelated to PR #26259. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #26259 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @myabc to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @myabc, and request a review from @myabc.
On every commit, set @myabc as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

@myabc
myabc force-pushed the implementation/dream-886-focus-model branch 2 times, most recently from 738953a to d0c0ec7 Compare October 23, 2026 02:52
@github-actions

Copy link
Copy Markdown

Warning

Flaky specs

  • rspec ./spec/features/components/lookbook_accessibility_spec.rb[1:14:1]
🤖 Ask Copilot to investigate

Copy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer.

@copilot The following spec(s) are flaky in CI (first seen on PR #26259, linked for reference only):

- `rspec ./spec/features/components/lookbook_accessibility_spec.rb[1:14:1]`

Treat this as a standalone task, unrelated to PR #26259. Create a new branch from origin/dev and open a new pull request targeting dev — do not stack it on PR #26259 or reuse that branch.

Follow the playbook in docs/development/testing/handling-flaky-tests/README.md to find the root cause and fix the underlying race — do not skip, delete, or weaken the spec to make it pass; disabling is a last resort per the playbook, and only with a bug ticket. Verify the fix by running the spec(s) repeatedly (e.g. `script/bulk_run_rspec --run-count 10`).

If you cannot reproduce the flake or are not confident in a fix after reasonable investigation, do not fabricate a change or skip the spec to force CI green. Instead, leave the pull request in draft and document what you tried, the suspected cause, and any leads in its description, then assign @myabc to take over.

Once the fix is verified, title the PR after the spec(s) it fixes, and use the PR description to explain the root cause, how the change resolves it, and the before/after results. Label the PR `flaky-spec`, assign @myabc, and request a review from @myabc.
On every commit, set @myabc as the sole co-author with a `Co-authored-by:` trailer (use their GitHub no-reply email so it links to their account), so it is traceable who dispatched the fix.

@myabc
myabc marked this pull request as ready for review October 23, 2026 07:22
@myabc
myabc requested a review from bsatarnejad October 23, 2026 07:26
@myabc
myabc force-pushed the implementation/dream-886-focus-model branch from d0c0ec7 to 0de620a Compare October 23, 2026 07:44
Base automatically changed from implementation/dream-893-selection-scope to dev October 23, 2026 11:54
@myabc
myabc force-pushed the implementation/dream-886-focus-model branch from 0de620a to 2592aa4 Compare October 23, 2026 11:54
@myabc
myabc requested a review from HDinger October 23, 2026 20:41
myabc added 6 commits October 26, 2026 03:06
Adds the keyboardNavigationEnabled table configuration flag, off by
default. Teaches the fast-table test harness an opt-in productionMarkup
mode that renders the sticky thead, its column and configuration
cells, and the totals row, with headerRow(), headerCells() and
totalsRow() accessors, so keyboard navigation specs can walk
production-shaped markup.

addInlineCreateRow now appends to the table's tbody[wpInlineCreate],
as production does, and creates that body after the totals body only
when there is none.

https://community.openproject.org/wp/DREAM-886
Adds a DOM-free navigation model for the work package table.
nextPosition maps a key press and the current position to a move,
expand, collapse or none result, skipping hidden rows and keeping the
desired column across rows of different widths. initialPosition picks
the first visible work package row, and reconcilePosition keeps focus
on the same row after a re-render, rebinding group headers and sums
rows by group identifier, else falling back to the nearest visible row.

https://community.openproject.org/wp/DREAM-886
Adds an optional groupIdentifier to rendered occurrences and sets it
on group header and sums rows, so focus recovery can find a group by
its stable identifier instead of its ordinal key.

Adds WorkPackageTable#rowsRefreshed$, which emits the work package id
once refreshRows replaced at least one of its rows, so consumers can
restore state lost when cells are rebuilt in place.
WorkPackageTable#notifyRowsRefreshed lets other owners of table rows,
such as the totals row, emit an edge row key on the same stream.

GroupedRowsBuilder#refreshExpansionState returns the visibility
updates instead of applying them, and the table publishes a render
after a group collapse even when no row changed visibility, since the
group header elements are replaced regardless.

https://community.openproject.org/wp/DREAM-886
With keyboardNavigationEnabled on, rows, display field spans, the
hierarchy and relation indicators, action links, the ID link, share
cells and the header sort link and configuration button no longer
take Tab focus. Rows get tabindex -1, display fields lose tabindex
and role=button, and the details pane renderer keeps both through a
new tabbable option that defaults to true.

Adds roving-tabindex.ts with navigableCellClassName,
paintRovingTabindex, navigableCells, keyboardNavigationEnabled and
isNavigableColumn. One eligibility rule decides which cells are
navigable: every query column, and the context-menu column while the
actions column or column menu is enabled; never the internal drag or
baseline columns. Body, group sums, totals and relation sub-rows
apply it alike, so vertical moves keep their column. The drag
placeholder is built without the class, and relation sub-rows take
their tr out of the Tab order and mark every relation cell. With the
flag off the DOM is unchanged.

Each roving paint also sweeps late-rendered tabbable descendants
(custom elements, modal focus anchors) to tabindex -1, sparing the
inline-create body and an open inline editor. insideGrid defines the
grid as the header, ledger and totals rows; the inline-create body is
outside it.

Adds removeTabStops to shared/helpers/dom for the builders and the
display field renderer. It covers anchors without an href too, since
display fields such as spent time set the href after rendering.

The harness renders the inline-create body, builds the totals cells
from the table's augmented columns with the same eligibility rule,
and accepts non-sortable column specs whose header has no sort link.

https://community.openproject.org/wp/DREAM-886
Adds the KeyboardNavigationTransformer behind
keyboardNavigationEnabled and turns the flag on for the work package
list view. The transformer walks the header row, the rendered
occurrences and the totals row with the arrow keys, Home, End, Page
Up/Down and Ctrl/Cmd+Home/End, keeps one roving Tab stop (the first
work package row until focus moves) and expands or collapses
hierarchy parents and groups with ArrowRight/ArrowLeft. A recognised
key that cannot move is consumed, so the page does not scroll.

Enter activates a work package row the way a plain click does,
toggles a group header, clicks a header cell's sort link or the
column configuration button, and opens the inline editor of an
editable cell through the onCellEnter hook. Keys with Alt or Shift,
during composition, with Ctrl/Cmd other than Home/End, or from
inner controls pass through.

Tab or Shift+Tab from the managed row or cell repaints the roving
stop first, sweeping late-rendered tabbable descendants, so Tab
leaves the grid instead of stopping on them.

Extracts activateRow from RowClickHandler and activateTableField
from EditCellHandler so both paths share one implementation, passes
the view to state transformers, and makes the group header cell
navigable when the flag is on.

https://community.openproject.org/wp/DREAM-886
Adds a mousedown listener on the table scroll container that records
the clicked row and navigable cell as the keyboard position and paints
the roving tabindex there. It neither focuses nor prevents default, so
links, toggles and editor activation keep working. Targets outside the
table rows, such as the timeline side, the inline-create row and the
placeholder row, change nothing.

TableEditForm now remembers the position it was opened from as origin
when its first field activates and clears it once the last field
closes.

https://community.openproject.org/wp/DREAM-886
myabc added 4 commits October 26, 2026 03:06
Records whether focus was inside the table before a render pass, a
collapse or a row refresh replaces or hides its rows, and afterwards
reconciles the remembered position against the new rows. The same
occurrence keeps focus, a removed row hands it to the nearest visible
row, a hidden row to the group header or parent that hides it, and an
empty result to the header row. Group positions rebind by identifier
and are dropped when the grouping changes.

Ownership is captured against the grid, so focus on the inline-create
button counts as outside, and a capture never downgrades while a
render is pending, so an editor reset that blurs to body cannot
revoke it. DOM focus is restored only when the table owned it, no
inline editor is open and the active element is at rest: null, body,
disconnected, or on a row the ledger now hides. A still-connected
control the user focused keeps focus. A row refresh or collapse
between scheduling a full render and its frame commit leaves
ownership to the commit. With the flag on, the transformer also
handles requested focus after render; SelectionTransformer keeps
doing so for tables without the flag.

The totals row joins the recovery seam: the sums-row directive
captures ownership before it refreshes or clears the row, skips the
clear when the row is already empty, and notifies the table
afterwards, so focus survives a sums refresh and the totals row is
reachable once it renders late.

https://community.openproject.org/wp/DREAM-886
Makes the keyboard navigation transformer the single owner of focus
return after inline editing. Cancelling an editor, saving it or
submitting an unchanged value focuses the cell the editor was opened
in again, once the save's row refresh has run. Focus stays where it
is when the origin cell is gone, when focus is outside the table,
when it moved out of the grid while the save was pending, or when
another editor in the table is open. Focus landing outside the grid,
including on the inline-create button, marks it as having left, and
the mark survives that element being removed.

Separates the user's position from the reconciled one. Only pointer
bookkeeping, keyboard moves, explicit requests and an editor opening
write userPosition; a click or arrow elsewhere during a pending save
cancels the return, while a render that merely reconciled the
position does not. The origin is reconciled before returning, so a
row hidden during the save hands focus to its hiding owner.

The origin is the activated field's cell, not the remembered
position, so programmatic activation, required-field activation and
the inline-create row (which has no origin) return correctly. A
failed activation leaves no origin behind. A later field of the same
form re-records the origin, and a form that survives a
required-column redraw registers itself again so refreshes neither
overwrite its cell nor steal its focus.

Adds an onSubmitComplete hook to EditForm that the field handler calls
after a successful user submit; it does nothing for other forms. With
the flag off, the table edit form still focuses the cell through
FocusHelper.

Drives the harness editing portal through the production field
handler, lets specs supply a save and edit more than one field of a
row.

https://community.openproject.org/wp/DREAM-886
Moves focus to the enclosing navigable cell, or else the row, when
keyboard or programmatic focus arriving from outside the grid lands
on an inner control the table took out of the Tab order (header sort
link, ID link, hierarchy toggle, relation expander, context-menu
button, column-configuration button). Menus that return focus to such
a control on Escape otherwise left focus where arrow keys do nothing.

Focus moving between elements inside the grid, such as a screen
reader cursor, is left alone. The redirect does not scroll and is
skipped while a pointer press is in progress, so the press keeps its
target and its click; mousedown already records the position.
Elements without tabindex="-1" and anything inside an open inline
editor keep focus.

Escape from the work package context menu returns to the roving stop
under the flag, also when the menu was opened by pointer on an inner
element. The column header menu falls back to its th, or else its own
element, when a column has no id element, and Enter on a header
without a sort link clicks its plain anchor.

https://community.openproject.org/wp/DREAM-886
Adds a feature spec that walks the work package list by keyboard: Tab
enters the table on the first row, arrow keys move between rows, cells
and the header row, Escape from the column header menu returns to the
header cell, Enter on the Subject header sorts, Tab re-enters at the
remembered position, Shift+Tab leaves the table, Tab from the last row
lands on the inline-create button, Enter on a row replaces the
selection, Escape in an inline editor returns focus to the cell, a
native Enter, type, Enter round trip saves and returns, a custom
element that renders a Tab stop does not catch Tab, and Down from a
Work cell lands on the Work total.

Adds page-object helpers to tab into the table, reach a row or cell by
arrow keys, send keys without clicking, and assert where focus is with
retrying matchers and accessible selectors. select_all_work_packages
now sends the shortcut to the table's roving Tab stop, falling back to
the first row when there is none.

Points the work package context menu accessibility examples at the
Subject cell, where Escape returns focus to the cell the menu was
opened from.

https://community.openproject.org/wp/DREAM-886
@myabc
myabc force-pushed the implementation/dream-886-focus-model branch from 2592aa4 to 9d38ed7 Compare October 26, 2026 03:52
Comment thread spec/features/work_packages/table/keyboard_navigation_spec.rb Outdated
myabc added 4 commits October 26, 2026 14:18
Reveals the context-menu icon and the drag handle while the row or one
of its cells holds keyboard focus. They were shown on hover and when the
action cell itself had focus, so row and cell focus from the navigation
model showed no actions (mob review, 2026-10-26). Covers it in the
keyboard navigation spec.

https://community.openproject.org/wp/DREAM-886
The global j and k shortcuts indexed every body row by the active
element. From cell focus the active element is a cell, so j jumped to
the first row and k did nothing; from a collapsed group header j
targeted a hidden child and stalled. Resolves the active element to
its row and skips rows that are not rendered. Covers both cases in the
keyboard navigation spec.

https://community.openproject.org/wp/DREAM-886
The example clicked the parent's hierarchy indicator, which moved focus
to the indicator before the child disappeared, so it passed without
exercising the recovery it was named for. The transformer spec pins
that case.

https://community.openproject.org/wp/DREAM-886
Dev switched @typescript-eslint/no-empty-function off, so the directive
on EditForm#onSubmitComplete is reported as unused when CI lints the
merge with dev. Documents the empty hook with a body comment instead,
which lints clean before and after the rule change.

https://community.openproject.org/wp/DREAM-886

@planktonic planktonic 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.

Tested this in firefox & safari, both in combination with voiceover and without, and it works well ✨ The only functional issues I see:

  • the screenreader focus and the treegrid focus can become out of sync, and it seems to produce odd results when you switch. This is quite a niche issue, perhaps something for the future
  • when using a screenreader, the actions buttons are reachable, but when only using keyboard I couldn’t reach those buttons. Perhaps an issue for our keyboard power-users?

I only had time to look at the JS in the keyboard folder, and I think most of comments are questions on naming :)

I didn’t find a WP table with nested rows in the seed data, so I also couldn’t try that out yet. Are there seeds for that somewhere?

internalSortColumn,
} from 'core-app/features/work-packages/components/wp-fast-table/builders/internal-sort-columns';

export const navigableCellClassName = 'wp-table--navigable-cell';

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.

I see these classnames are not introduced here, so this is a general question, not specific to this PR: is the classname following a BEM naming scheme? (the double dash suggests it is). Since the cell is a child of the wp-table, I would expect this to be wp-table__navigable-cell`. Is this a variant of BEM naming, what’re the convention rules?

Comment on lines +84 to +86
if (element !== focused && !element.closest(inlineCreateSelector) && !element.closest(`.${activeFieldContainerClassName}`)) {
element.setAttribute('tabindex', '-1');
}

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.

Is this responsible for buttons & links not being reachable by tabbing right now? (I can’t tab to the actions buttons at the end of the row, for example). This clause seems to be taking interactive elements out of the normal tab order.

groupIdentifier?:string;
}

export type NavKey = 'ArrowUp'|'ArrowDown'|'ArrowLeft'|'ArrowRight'|'Home'|'End'|'PageUp'|'PageDown'|'CtrlHome'|'CtrlEnd';

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.

Tab (and shift+tab), and enter/return are included in the spec but missing here.

// See COPYRIGHT and LICENSE files for more details.
//++

export type RowKind = 'header'|'workPackage'|'groupHeader'|'sums'|'totals';

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.

For setting focus, does the content of the row make a difference?

rows:readonly NavigableRow[],
position:FocusPosition,
edge:'first'|'last',
column:'keep'|'edge',

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.

An edge column seems clear to me, but what’s a keep column? It implies we “lose” the edge?

}

function navigateVertically(rows:readonly NavigableRow[], position:FocusPosition, key:NavKey, index:number, pageSize:number):NavResult {
switch (key) {

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.

Love the switch statements in these two functions, super clear

return toResult(positionOn(target, cell, cell), position);
}

function moveWithinRow(row:NavigableRow, position:FocusPosition, direction:'Home'|'End'|'ArrowLeft'|'ArrowRight'):NavResult {

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.

This looks like it’s the other in the pair of navigating cells, so would it make sense to call it navigateHorizontally?

}

/** Computes what a navigation key does at the given position. */
export function nextPosition(

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.

Like the comment says: would something like computeNextPosition make it’s purpose clearer?

return Array.from(rows).find((row) => !table.tbody.contains(row)) ?? null;
}

/** The navigable kind of a rendered occurrence, or null for rows navigation skips. */

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.

null for rows navigation skips is hard to parse :D Is there a missing apostrophe on row’s/rows’?

return occurrence.workPackageId === null ? null : 'workPackage';
}

function expandedOf(

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.

If this returns boolean, the naming convention used in other JS files here seems to be that the function would be called something like isExpanded?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Accessibility ai: Collaborative 💻 AI generated a substantial part of the code; A human reviewed and understands every line. javascript Pull requests that update Javascript code needs review

Development

Successfully merging this pull request may close these issues.

2 participants