Skip to content

fix(tree-node-web): keep sort order and expansion state on refresh - #2399

Open
davidofsky wants to merge 2 commits into
mendix:mainfrom
davidofsky:fix/tree-node-refresh-sort-and-expansion
Open

fix(tree-node-web): keep sort order and expansion state on refresh#2399
davidofsky wants to merge 2 commits into
mendix:mainfrom
davidofsky:fix/tree-node-refresh-sort-and-expansion

Conversation

@davidofsky

Copy link
Copy Markdown

Pull request type

Bug fix (non-breaking change which fixes an issue)


Description

Two bugs in the infinite-depth Tree node (the Parent association configuration added in
3.11.0, i.e. the TreeNodeV2 code path). Both are caused by useIncrementalTreeData,
which builds its node map incrementally and reuses nodes across data source updates.

1. A new sort order is never applied

A node is appended to rootsRef / parent.children only the first time its id is seen.
On later updates an already-known node takes the "already exists" branch, which refreshes
item, title and parentId but never re-orders the sibling arrays. So when the data
source returns the same items in a different order. For example after a microflow changes
a sequence attribute that the data source sorts on, the tree keeps the order it captured
on first load. The new order only becomes visible when the page is reopened and the widget
remounts.

Fixed by re-applying the data source order on every update, for the root nodes and for each node's children

2. Every node collapses when the data source refreshes

On a refresh the widget throws its tree away and rebuilds it, and rebuilt nodes always
start out collapsed. Three things trigger that rebuild: the data source being mid-load
(datasource.items is undefined, which the hook read as "all items removed"), the client
handing over new prop instances (isConfigChanged compares them by reference, so this
happens on every refresh), and any single item being deleted. While loading, the tree also
briefly showed "No data available".

Fixed by keeping the current tree while the data source is loading, and by remembering
each node's expanded/collapsed state so a rebuilt node gets its state back.

The v1 code path (nested Tree node widgets, no parent association) rebuilds from
datasource.items on every update and was never affected.

7 unit tests added to useIncrementalTreeData.spec.ts. 3 for sibling/root re-ordering
and 4 for expansion state surviving a reload, config churn, an item removal, and a
user-collapsed node with startExpanded on. All 7 fail on main and pass with this
change. Full package suite: 69 tests passing, pnpm lint clean.

What should be covered while testing?

Requires a Tree node configured with Parent association (self-referencing
association), so the infinite-depth code path is used.

Sort order

  1. Set the data source sort order to a sequence/order attribute.
  2. Expand a few nodes at different levels.
  3. Trigger a microflow that changes the sequence of some children (move up/down buttons).
  4. The children re-order in place immediately, without reopening the page, and the
    expanded nodes stay expanded.

Expansion state on refresh

  1. Expand several nodes across multiple levels, leave others collapsed.
  2. Trigger a refresh: commit an object with Refresh in client, and also try
    Refresh entity / a microflow that only reloads the list.
  3. Every node keeps its expanded/collapsed state, the tree does not flash
    "No data available" while reloading, and no loading spinners remain on the chevrons.
  4. Delete one item and confirm the remaining nodes keep their expansion state.

Regression checks

  1. Start expanded on and off, on first load, still behaves as before.
  2. Expanding a node still lazy-loads its children.
  3. A Tree node without a parent association (nested Tree node widgets) is unchanged.

The infinite-depth tree (`Parent association`) builds its node map
incrementally. A data-source refresh never re-ordered siblings: reordering items through a
sequence attribute stayed invisible until the page was reopened.

The same hook also collapsed the whole tree on any refresh.

Expansion state is now remembered per item id and restored when a node is
recreated, and a loading data source no longer clears the tree.
@davidofsky
davidofsky requested a review from a team as a code owner August 25, 2026 06:31
@gjulivan

Copy link
Copy Markdown
Collaborator

linked to WC-3557

In useInfiniteTreeNode.ts: expanding a node only pre-loaded the children that happened to be in the datasource at that moment, so a child added later (or arriving in a later batch) was never pre-loaded and its parent lost the expand chevron below the second level.

Now the hook keeps a set of expanded node ids (expandedIdsRef) and, on every datasource update, pre-loads any new item whose parent is in that set, so every visible node knows whether it has children
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants