Conversation
Foundation for keying pivot row expansion by dimension-value paths instead of positional TanStack row ids, so expansion survives sorting, adding a field, and data refreshes (rilldata#9781). Keys are the dimension values from root to a node, NUL-joined and hierarchical (depth is the segment count, parent is strip-last-segment). Pure helpers with unit tests.
Wire the value-based expand keys through the pivot so expansion survives sorting, adding a field, and data refreshes (rilldata#9781). - PivotTable sets a hierarchical value-based getRowId, with a reserved id for the grand-totals row. - getValuesForExpandedKey and addExpandedDataToPivot resolve a key by matching each row's value (stored under rowDimensions[0]) instead of positional indices. - queryExpandedRowMeasureValues derives depth from the key's segment count. - getFiltersForCell, getValuesForFlatTable, getRawRowValues, the show-more keys, and the ancestor-highlight walk use the value keys. - Drop the expanded={} resets on add/sort/columns/rows and stop reading the legacy positional pivotExpanded proto field. - Tests updated for value-based resolution. Fixes rilldata#9781
63818ef to
0262ac9
Compare
Expand a nested row, then add a measure and sort, and assert the row stays expanded (rilldata#9781).
0262ac9 to
c35cd9a
Compare
|
@nishantmonu51 I'd appreciate if you or someone from the team could take a look at the PR, thanks! |
nishantmonu51
left a comment
There was a problem hiding this comment.
All three findings are anchored inline. Two smaller things: toProto no longer writes pivotExpanded and fromProto now ignores it, so the proto field is dead and marking it deprecated in proto/ would make that explicit; and the <NULL> sentinel collides with a genuine dimension value of "<NULL>", which is unlikely to bite but worth a one-line comment given that pivot-click-selection.ts encodes nulls differently. The Playwright test covers add-measure and sort but not the data-refresh case named in the title. The branch merges cleanly onto current main.
| */ | ||
|
|
||
| // NUL separator, since dimension values won't contain it. | ||
| export const EXPAND_KEY_SEP = "\0"; |
There was a problem hiding this comment.
Consumers of the old dot-path row ids were not migrated to this separator. web-common/src/features/canvas/components/pivot/pivot-click-to-filter.ts:658 still computes isNestedChild = isNested && rowId.includes("."), so every child row whose value has no dot now takes the dimKeyFromRow branch, and that branch only reads rowDimensionNames[0]. With rows [outer, inner], clicking the revenue cell of X under A stores dimKey "X" while NestedTable.svelte computes the same row's key via nestedDimKeyFromRow as "A\0X": the clicked cell never renders as selected, a second click is not recognised as a deselect, and X collides across parents, which is exactly the collision the comment above that line says the branch exists to prevent. parentExpandKey(rowId) !== "" is the equivalent test. pivot-click-to-filter.spec.ts also fails 23 of 47 tests on this head (all pass on main) because its fixtures still pass positional ids such as "1.0".
| for (const row of flatRows) { | ||
| // Always skip the prepended grand-totals row. | ||
| if (hasTotalsRow && row.id === "0") continue; | ||
| if (hasTotalsRow && row.id === PIVOT_TOTALS_ROW_ID) continue; |
There was a problem hiding this comment.
Three sibling totals-row checks still compare against the positional id "0", which getRowId no longer returns: PivotTable.svelte:391, FlatTable.svelte:245 and NestedTable.svelte:570. In PivotTable.svelte a canvas click on a totals-row measure cell is therefore no longer short-circuited and falls through to onCellClickToFilter; getValuesForExpandedKey resolves no row values for "\0totals", so getFiltersForCell applies a filter built from the column entries alone. In the two table components isTotalsRow is false for the totals row, so getCellFormatting stops returning null and totals cells pick up conditional-formatting colours, and flatCellState/nestedCellState mark them interactiveCell when click-to-filter is enabled.
| config.pivot?.showTotalsRow !== false && numMeasures > 0, | ||
| ); | ||
|
|
||
| if ( |
There was a problem hiding this comment.
This guard only rejects an empty resolution, but getValuesForExpandedKey now stops at the deepest resolvable segment, so a partially resolved key falls through and fires an aggregation query with an incomplete filter on every refresh. With rows [publisher, time], key "A\0Jan" expanded, and domain then inserted to give [publisher, domain, time], the key still has depth 2 so the nestLevel early return does not fire, but it resolves to values = ["A"]; the query for time values filtered only by publisher = A is issued and then discarded by addExpandedDataToPivot, since node is undefined after the failed second segment. Because the PR deliberately keeps inert keys around after setPivotRows, this repeats until the key is collapsed. Guarding on values.length !== nestLevel would cover it.
The pivot table tracked expanded rows by TanStack's positional row ids (dot paths like
0.1.2that index intosubRows). Any config change re-partitions or re-orders the row tree, so those ids stop pointing at the same rows and the expanded state was discarded. Adding a measure or dimension, reordering row dimensions, or sorting all collapsed the tree back to the top level.This switches expansion to value-based hierarchical keys: the dimension values from the root to a node, so a node keeps the same key across add, sort, and data refreshes.
PivotTablesets a value-basedgetRowId(parent id plus this row's value), with a reserved id for the prepended grand-totals row.getValuesForExpandedKeyandaddExpandedDataToPivotresolve a key by matching each row's own value (stored underrowDimensions[0]at every depth) instead of positional indices, which also removes the totals-row offset.queryExpandedRowMeasureValuesderives nesting depth from the key's segment count.getFiltersForCell,getValuesForFlatTable,getRawRowValues, the show-more row limits, and the ancestor-highlight walk all use the value keys.expanded = {}resets on add/sort/columns/rows are dropped. A reorder just leaves keys that match nothing (inert), and the legacy positionalpivotExpandedproto field is no longer read.Reordering the row dimensions changes a node's value path, so those specific nodes are not restored (they were not before either). Adding a field, sorting, and refreshing now keep the tree expanded.
Closes #9781
Checklist: