Skip to content

Fix tree view checkbox selection for backup dialog. #9649#9671

Open
RohitBhati8269 wants to merge 1 commit intopgadmin-org:masterfrom
RohitBhati8269:GH-9649
Open

Fix tree view checkbox selection for backup dialog. #9649#9671
RohitBhati8269 wants to merge 1 commit intopgadmin-org:masterfrom
RohitBhati8269:GH-9649

Conversation

@RohitBhati8269
Copy link
Contributor

@RohitBhati8269 RohitBhati8269 commented Feb 24, 2026

  • Modified toggleCheck to collect all checked/indeterminate nodes from the
    entire tree, not just the clicked node's subtree
  • Fixed ancestor state calculation to properly distinguish between fully
    checked (state === true) and indeterminate states
  • Added the isIndeterminate flag to node data to help the backup dialog differentiate
    between full schema selection and partial table selection
  • Added traversal logic to find root nodes and iterate through all siblings
    to collect a complete selection state

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved tree selection behavior to properly handle parent-child relationships when toggling items.
    • Selection state now accurately reflects all checked and partially-selected items in the tree hierarchy.

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Walkthrough

Modified selection logic in PgTreeView component to compute and track complete tree-wide selection state. Updates clicked node and descendants, marks indeterminate states, and passes all checked/indeterminate nodes to callback instead of only directly toggled nodes.

Changes

Cohort / File(s) Summary
Tree Selection State Logic
web/pgadmin/static/js/PgTreeView/index.jsx
Refactored selection callback mechanism to update node and descendants, compute indeterminate states for partial selections, and collect all checked/indeterminate nodes from tree traversal before passing to callback handler.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • #9501 — Directly modifies the same tree selection logic in PgTreeView with checkedState/toggleCheck flow and indeterminate state handling.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: fixing tree view checkbox selection for the backup dialog, with a reference to the issue number.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/pgadmin/static/js/PgTreeView/index.jsx`:
- Line 91: Change the concise-arrow forEach callbacks to block-bodied arrows
that explicitly perform their side effects and return nothing; e.g., replace the
single-expression callback in the n.children?.forEach(child =>
updateDescendants(child, val)); call with a block-bodied callback that calls
updateDescendants(child, val) inside braces, do the same for the forEach inside
collectAllCheckedNodes (which currently has a bare return;) and the third
flagged forEach at line 146 so none of the forEach callbacks have implicit
returns; this will satisfy the lint rule and keep intent explicit.
- Around line 126-131: The code mutates the parent-owned node data by setting
n.data.isIndeterminate, causing stale flags; instead stop mutating n.data and
create a wrapper object for each selected node (e.g., push { node: n,
isIndeterminate: state === 'indeterminate' } into allCheckedNodes) so
isIndeterminate is derived from checkedState/newState rather than stored on
n.data; update the selectionChange call-site and the backup-dialog consumer to
read isIndeterminate from the wrapper object (or the wrapper's property) instead
of node.data.isIndeterminate so the flag is always correct and never mutates the
original data.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97c90f1 and 3969615.

📒 Files selected for processing (1)
  • web/pgadmin/static/js/PgTreeView/index.jsx

if (val) {
selectedChNodes.push(n);
}
n.children?.forEach(child => updateDescendants(child, val));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix forEach callbacks to use block bodies — Biome lint errors on lines 91, 133, and 146.

All three arrow functions use concise bodies, causing Biome to flag them as forEach callbacks that return a value (lint/suspicious/useIterableCallbackReturn). collectAllCheckedNodes even has an explicit bare return;, making its void intent clear.

🔧 Proposed fix
-      n.children?.forEach(child => updateDescendants(child, val));
+      n.children?.forEach(child => { updateDescendants(child, val); });
-      n.children?.forEach(child => collectAllCheckedNodes(child));
+      n.children?.forEach(child => { collectAllCheckedNodes(child); });
-      rootParent.children.forEach(root => collectAllCheckedNodes(root));
+      rootParent.children.forEach(root => { collectAllCheckedNodes(root); });

Also applies to: 133-133, 146-146

🧰 Tools
🪛 Biome (2.4.4)

[error] 91-91: This callback passed to forEach() iterable method should not return a value.

(lint/suspicious/useIterableCallbackReturn)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/pgadmin/static/js/PgTreeView/index.jsx` at line 91, Change the
concise-arrow forEach callbacks to block-bodied arrows that explicitly perform
their side effects and return nothing; e.g., replace the single-expression
callback in the n.children?.forEach(child => updateDescendants(child, val));
call with a block-bodied callback that calls updateDescendants(child, val)
inside braces, do the same for the forEach inside collectAllCheckedNodes (which
currently has a bare return;) and the third flagged forEach at line 146 so none
of the forEach callbacks have implicit returns; this will satisfy the lint rule
and keep intent explicit.

Comment on lines +126 to +131
if (state === true || state === 'indeterminate') {
// Set isIndeterminate flag to differentiate full schema selection
// from partial selection (only specific tables) in backup dialog
n.data.isIndeterminate = (state === 'indeterminate');
allCheckedNodes.push(n);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Mutating n.data directly violates React's immutability principle and leaves stale flags.

n.data is the original object from the data prop array owned by the parent component. Assigning n.data.isIndeterminate mutates the caller's data silently. There is also a concrete stale-state bug: when a node transitions from indeterminate → unchecked, it is skipped by the state === true || state === 'indeterminate' guard, so data.isIndeterminate is never reset to false and permanently remains true on the data object.

The isIndeterminate flag is purely derived state that already exists in checkedState/newState — it doesn't need to be stored on node.data at all. Pass it alongside the node without mutating the data:

♻️ Proposed fix — avoid data mutation
-      if (state === true || state === 'indeterminate') {
-        // Set isIndeterminate flag to differentiate full schema selection
-        // from partial selection (only specific tables) in backup dialog
-        n.data.isIndeterminate = (state === 'indeterminate');
-        allCheckedNodes.push(n);
-      }
+      if (state === true || state === 'indeterminate') {
+        allCheckedNodes.push({ node: n, isIndeterminate: state === 'indeterminate' });
+      }

Update the selectionChange call-site (and the backup dialog consumer) to read isIndeterminate from the wrapper object instead of node.data.isIndeterminate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/pgadmin/static/js/PgTreeView/index.jsx` around lines 126 - 131, The code
mutates the parent-owned node data by setting n.data.isIndeterminate, causing
stale flags; instead stop mutating n.data and create a wrapper object for each
selected node (e.g., push { node: n, isIndeterminate: state === 'indeterminate'
} into allCheckedNodes) so isIndeterminate is derived from checkedState/newState
rather than stored on n.data; update the selectionChange call-site and the
backup-dialog consumer to read isIndeterminate from the wrapper object (or the
wrapper's property) instead of node.data.isIndeterminate so the flag is always
correct and never mutates the original data.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant