Repository navigation
[DREAM-967] Pin the table paths DREAM-874 changed - #26512
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The console.warn spy is not restored, so it can hide warnings from subsequent tests in the file.
1 open issue
What changed in this PR
Adds regression coverage for work package table paths affected by DREAM-874, building on the DREAM-959 refactors before DREAM-897 verification. Existing behavior is preserved, including explicitly documented edge cases.
Changes:
- Covers keyboard navigation, editor focus, teardown, and details-pane switching.
- Adds rendering checks for grouping, hierarchy, relations, baseline cells, and row positions.
- Removes unused methods, a redundant guard, and an unused constructor default.
All paths below are relative to frontend/src/app/features/work-packages/components/wp-fast-table/.
| File | Description |
|---|---|
wp-table-editing.ts |
Removes unused stopEditing method. |
wp-fast-table-lifecycle.spec.ts |
Tests mutation after destruction. |
testing/work-package-fixture.ts |
Supports unchanged baseline fixtures. |
semantics/table-row-positions.spec.ts |
Tests positions with unset groups state. |
semantics/body-row-positions.spec.ts |
Covers empty groups and missing headers. |
row-mutations.ts |
Requires the render-pending callback explicitly. |
handlers/state/keyboard/keyboard-navigation-transformer.ts |
Removes redundant destruction guard. |
handlers/state/keyboard/keyboard-navigation-transformer.spec.ts |
Covers paging and focus-recovery edges. |
handlers/state/keyboard/focus-position.spec.ts |
Tests movement boundary cases. |
handlers/state/keyboard/cell-interaction.spec.ts |
Covers editor Tab behavior and overlay re-entry. |
handlers/row/click-handler.spec.ts |
Tests details-pane switching and tab preservation. |
handlers/cell/edit-cell-entry.spec.ts |
Tests activation cancellation during teardown. |
builders/timeline/timeline-row-builder.ts |
Removes unused insert method. |
builders/rows/single-row-builder.spec.ts |
Tests unchanged baseline presentation. |
builders/relations/relations-render-pass.spec.ts |
Tests retry after child-loading failure. |
builders/modes/hierarchy/hierarchy-order.spec.ts |
Records current deferred-leaf ordering. |
builders/modes/grouped/grouped-render-pass.spec.ts |
Covers group matching and member highlighting. |
Review effort: Balanced
a760c2e to
82cc331
Compare
0af0dc2 to
8c49d33
Compare
041292c to
5f7c9b3
Compare
5f7c9b3 to
bc4275b
Compare
bc4275b to
448a246
Compare
982b7fa to
ab64c9c
Compare
a108add to
f1f43ec
Compare
f1f43ec to
1747170
Compare
1747170 to
f742be6
Compare
| await harness.render(); | ||
|
|
||
| const highlighted = (row:Element) => Array.from(row.classList).some((name) => name.startsWith('__hl_')); | ||
| expect(highlighted(harness.row('1'))).toBe(true); |
There was a problem hiding this comment.
Is every row expected to be highlighted with this class individually? Perhaps it would make the test more robust to build a table with multiple rows, and check that they all have the highlight.
Nothing to do with these tests: if they’re all highlighted, what does the highlight mean? :D Does it have some semantic meaning that we could indicate using an (aria-)attribute, or is it purely aesthetic?
There was a problem hiding this comment.
Enabling highlighting does this btw (using status as an example):
If this were phrasal content, then we would have <mark> in our toolbox. For rows, I'm really not sure how to handle. I'm also quite sure that colour highlighting flagrantly disregards WCAG colour contrast guidelines. / @bsatarnejad
aria-describedby referencing the column used for highlighting might be one option.
There was a problem hiding this comment.
The spec now renders two rather than one row .. does that suffice?
There was a problem hiding this comment.
(sorry, that was meant flippantly. I confess that I didn't put enough thought into how extensive the assertions should be with this particular case)
There was a problem hiding this comment.
Yes two rows works 👍
For the highlighted rows, possible alternative aria attributes would be aria-selected (suitable for gridcell, option, row, and tab roles), or perhaps aria-current (“indicates that this element represents the current item within a container or set of related elements.”, from mdn). But does either of those fit the meaning of the highlight? It looks like this highlighting is indicating something else.
(And to be clear, that’s not a suggestion to do it here, just asking for possible future improvements)
f742be6 to
484d275
Compare
Removes WorkPackageTableEditingContext.stopEditing and TimelineRowBuilder.insert. Nothing calls either of them, specs included; the other stopEditing calls in the app belong to the resource editing service, and the timeline render pass only uses build. https://community.openproject.org/wp/DREAM-967
Pins keyboard paths that no unit or feature spec covered: PageUp and PageDown move by the rows that fit the viewport, and by one row when every group is collapsed; Enter on a group sums row is not consumed; an overlay that already holds focus keeps the return path; focus on an unlisted grid row counts as at rest, while focus on the scroll container itself does not; repainting before any focus keeps the first row as the Tab stop; and focusing an unrendered cell leaves focus and scroll alone. Each spec fails when the line it pins is broken. https://community.openproject.org/wp/DREAM-967
Pins how Tab and re-entry behave around open editors: Tab between controls inside a multi-control editor stays native and does not park it, and re-engaging a parked editor restores its overlay root's own tabindex. Also pins two behaviours that are still open questions rather than settled decisions: an overlay without a usable control receives focus itself, and Tab from a control in a cell that is not a walk target stays native and leaves the grid. Each spec fails when the line it pins is broken. https://community.openproject.org/wp/DREAM-967
Pins three edges of the pure movement model: ArrowRight on a row without cells does nothing, a position on a row that is not rendered moves nowhere, and a cell position without a desired column lands in the first cell of the next row. The last one is an open question rather than a decision: the current cell may be the better fallback. Each spec fails when the line it pins is broken. https://community.openproject.org/wp/DREAM-967
Pins that clicking another row while the details pane is open switches the pane to that row and keeps its tab, and that clicking the row already shown leaves it alone. Also pins that an edit activation abandoned because the table was torn down mid-load marks nothing read-only and opens no editor. Each spec fails when the line it pins is broken. https://community.openproject.org/wp/DREAM-967
Pins that a destroyed table runs no further mutation, and that row positions are still painted when the groups state is unset, as it can be when a response carries no groups. Also pins two behaviours of the grouped position check that are still open questions: a zero-count group is skipped, and member rows drawn without their group header are rejected. Each spec fails when the line it pins is broken. https://community.openproject.org/wp/DREAM-967
Pins four render-pass paths: child relation targets are requested again on the next redraw after a failed load, row highlighting skips group headers, an unchanged work package leaves its baseline cell empty, and a deferred filtered-out parent is taken from the cache when it is not in the result window. The hierarchy case pins the order DREAM-959 settled: the filtered-out parent renders before its deferred leaf. Each spec fails when the line it pins is broken. https://community.openproject.org/wp/DREAM-967
Pins how rows find their group: a linked value matches by href, a multi-value property with a different number of values matches no group, a progress group shown with a percent sign matches the bare number, and a work package without the property lands in the empty group. https://community.openproject.org/wp/DREAM-967
Removes the destroyed check in keyboard navigation's recover, whose only caller rowsChanged already returns early on a destroyed table, and makes RowMutations' renderPending callback required, since both constructions pass it. No behaviour change. https://community.openproject.org/wp/DREAM-967
Asserts the "All rows should have a group" warning the multi-value mismatch spec expects instead of silencing it, passes group hrefs as a plain array so the matching specs need no cast, types the renderedUnder helper's boolean return, and highlights two member rows so the spec covers every member rather than one. https://community.openproject.org/wp/DREAM-967
Adds TableHarness#groupSums, which finds a group's sums row by its group class the way groupHeader finds the header, and uses it in place of the selector strings the keyboard navigation and roving tabindex specs repeated. https://community.openproject.org/wp/DREAM-967
Renames three keyboard specs so their titles state the expected outcome: Tab before an editor's last control stays with the browser and parks nothing, Enter on a group sums row is left to the browser, and returnFocus takes focus back from a row the table did not render. https://community.openproject.org/wp/DREAM-967
484d275 to
c5d98a8
Compare
|
Caution The provided work package version does not match the core version Details:
Please make sure that:
|
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy 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. |
planktonic
left a comment
There was a problem hiding this comment.
Looks great. My remaining unresolved thread is a question for me to understand the UI here better, it’s not something that’s related to/blocking this PR

Ticket
https://community.openproject.org/wp/DREAM-967
What are you trying to accomplish?
tl;dr for humans: Adds unit specs that lock in how the work package table behaves on paths the DREAM-874 keyboard and accessibility work changed but no spec exercised, so regressions show up before DREAM-897 verification. Spec-only apart from deleting dead code and two redundant guards; no behaviour change.
First slice of DREAM-967. Uncovered code was attributed with
git blameto the epic's tickets; this slice pins the reachable epic paths plus grouped row matching.wp-fast-table)What approach did you choose and why?
WorkPackageTableEditingContext.stopEditingandTimelineRowBuilder.insert, which have no callersfocus-position.ts%progress values and missing propertiesEvery new spec was checked by breaking the line it pins and watching it fail.
Behaviour notes for reviewers
These specs pin today's behaviour where it is an open question rather than a decision; the questions go to DREAM-967:
currently renders a deferred leaf before its filtered-out parent). This looks like a bug.Not pinned:
HierarchyTransformer's reset of collapsed classes uses[class^="__hierarchy-group-"], which never matches a table row, so it never runs. Fixing the selector would change behaviour, so it is left for DREAM-967.Hints for dev review
Each commit is green on its own (typecheck plus the
wp-fast-tablespecs). The full Chromium suite and the keyboard specs on Firefox pass.AI involvement
Directed – I specified the requirements and AI implemented most of it; I validated via testing rather than a full line-by-line review.
Merge checklist