Skip to content

fix: DataMapper: Document node is a duplicate of the header#3135

Draft
matheusandre1 wants to merge 2 commits intoKaotoIO:mainfrom
matheusandre1:fix-3132
Draft

fix: DataMapper: Document node is a duplicate of the header#3135
matheusandre1 wants to merge 2 commits intoKaotoIO:mainfrom
matheusandre1:fix-3132

Conversation

@matheusandre1
Copy link
Copy Markdown
Contributor

@matheusandre1 matheusandre1 commented Apr 16, 2026

Fixes: #3132

I duplicated the code, could you advise me if it's correct or if in this case I need to create a utility for it?

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved document tree rendering logic to properly handle structured root nodes and their child visibility states.
  • Tests

    • Enhanced test coverage with stricter validation of schema attachment and detachment operations, ensuring proper UI state and button presence.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

The PR eliminates duplicate document node rendering by skipping the root node when it's structured (schema-attached), adjusts child traversal to always display structured root's children, and recalibrates depth during traversal. Test assertions were tightened to validate node removal and button state during schema operations.

Changes

Cohort / File(s) Summary
Document Tree Traversal Logic
packages/ui/src/models/datamapper/document-tree.ts
Modified flatten() to skip adding structured root nodes, updated child traversal to always show structured root's children regardless of expansion state, and adjusted depth progression (children of structured root maintain depth; other parent-child pairs increment).
Panel State Computation
packages/ui/src/components/View/SourcePanel.tsx, packages/ui/src/components/View/TargetPanel.tsx
Moved hasSchema variable declaration earlier in render flow (before useEffect) and reordered useCallback dependency arrays for consistency (no functional logic changes).
Schema Attachment/Detachment Tests
packages/ui/src/components/View/SourceTargetView.test.tsx
Tightened test assertions to validate both absence of expected nodes and presence of exactly one detach button before interaction in Source Body, Target Body (XSD), and Target Body (JSON schema) test cases.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • igarashitm
  • lordrip
  • PVinaches

Poem

🐰 No more roots that stand twice tall,
The structured tree now shows just one!
Children dance without a wall,
Depth keeps pace when roots are spun—
A flatter map for mapping fun! 🌳✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main fix: removing duplicate document node display that appears alongside the header in the DataMapper component.
Linked Issues check ✅ Passed The PR addresses issue #3132 by modifying tree flattening logic to skip document root nodes when structured, prevents duplicate header/document display, and removes redundant UI-level filtering.
Out of Scope Changes check ✅ Passed All changes directly address the duplication issue: document-tree.ts modifies flattening logic, SourcePanel/TargetPanel adjust variable timing, and tests validate the fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@igarashitm
Copy link
Copy Markdown
Member

This would rather have to be addressed in tree-ui.service.ts and document-tree.ts side, so that the flattenedNodes is already excluding the document node.

@matheusandre1
Copy link
Copy Markdown
Contributor Author

I will fix this.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (2)
packages/ui/src/models/datamapper/document-tree.ts (2)

52-78: Optional: hoist isStructured out of traverse.

isStructured depends solely on this.root.nodeData.isPrimitive and is invariant for the whole flattening pass, but it is recomputed on every recursive call. Hoisting it (and deriving isRoot only where needed) makes the traversal a bit tighter and the structured-root special-case more obvious.

♻️ Proposed refactor
   flatten(expansionState: Record<string, boolean>, startDepth = 0): FlattenedNode[] {
     const result: FlattenedNode[] = [];
+    const isStructured = !this.root.nodeData.isPrimitive;

     const traverse = (node: DocumentTreeNode, depth: number) => {
-      const isRoot = node === this.root;
-      const isStructured = !this.root.nodeData.isPrimitive;
-
-      // Always add current node to result, EXCEPT for the root node when it has a schema
-      if (!(isRoot && isStructured)) {
+      const isStructuredRoot = isStructured && node === this.root;
+
+      // Skip the structured root; it is represented by the DocumentHeader.
+      if (!isStructuredRoot) {
         result.push({
           treeNode: node,
           depth,
           index: result.length,
           path: node.path,
         });
       }

-      // Only traverse children if:
-      // 1. Node has children
-      // 2. Node is expanded (checked via expansion state)
-      // OR Node is the structured root node (we always show its children)
-      const isExpanded = expansionState[node.path] ?? false;
-      const shouldTraverse = (isRoot && isStructured) || isExpanded;
-
-      if (node.children.length > 0 && shouldTraverse) {
-        node.children.forEach((child) => {
-          traverse(child, isRoot && isStructured ? depth : depth + 1);
-        });
+      // Children are shown when the node is expanded, or unconditionally for the
+      // structured root (so the schema's root field is always visible).
+      const shouldTraverse = isStructuredRoot || (expansionState[node.path] ?? false);
+      if (shouldTraverse && node.children.length > 0) {
+        const childDepth = isStructuredRoot ? depth : depth + 1;
+        node.children.forEach((child) => traverse(child, childDepth));
       }
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/models/datamapper/document-tree.ts` around lines 52 - 78,
Hoist the invariant check this.root.nodeData.isPrimitive out of the recursive
function: compute const isStructured = !this.root.nodeData.isPrimitive once
before defining/traversing with traverse, remove its recomputation inside
traverse, and keep deriving isRoot = node === this.root only where needed;
update uses in the conditional that skips adding the structured root and in the
depth calculation (the rest of traverse, expansionState, result,
node.children.forEach, and shouldTraverse logic remain unchanged).

36-48: Stale JSDoc — update "always includes the root node".

The contract in the docblock no longer matches the implementation: with a structured (schema-attached) root, the root is intentionally skipped and its children are always traversed regardless of expansion state. Please refresh the "Key behavior" bullets and the startDepth note (which now applies to the structured root's children when the root is skipped).

📝 Proposed doc update
   /**
    * Flattens the tree into an array of visible nodes based on expansion state
    *
    * Key behavior:
-   * - Always includes the root node
-   * - Only includes children if parent is expanded
+   * - Includes the root node only when it is primitive (no attached schema).
+   *   A structured root is skipped and its children are always included at
+   *   `startDepth`, effectively taking the root's visual slot.
+   * - For all other nodes, children are included only when the parent is expanded.
    * - Recursively checks expansion state down the tree
    * - Calculates correct depth for indentation
    *
    * `@param` expansionState - Record mapping node paths to their expansion state
-   * `@param` startDepth - Starting depth for the root node (default: 0)
+   * `@param` startDepth - Starting depth for the root (or for the structured
+   *                     root's children when the root is skipped). Default: 0.
    * `@returns` Array of flattened nodes with depth and index information
    */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/models/datamapper/document-tree.ts` around lines 36 - 48,
Update the JSDoc on the tree-flattening function in document-tree.ts to reflect
the current implementation: remove or change the "Always includes the root node"
bullet to say that a structured (schema-attached) root is skipped and its
children are traversed regardless of the root's expansion state, keep the bullet
about only including children when the parent is expanded for non-structured
nodes, and revise the startDepth note to clarify that when the root is skipped
the provided startDepth applies to the structured root's children (i.e., the
visible top-level nodes); also ensure the param names (expansionState,
startDepth) and the "depth/indentation" behavior are accurate in the docblock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/ui/src/models/datamapper/document-tree.ts`:
- Around line 52-78: Hoist the invariant check this.root.nodeData.isPrimitive
out of the recursive function: compute const isStructured =
!this.root.nodeData.isPrimitive once before defining/traversing with traverse,
remove its recomputation inside traverse, and keep deriving isRoot = node ===
this.root only where needed; update uses in the conditional that skips adding
the structured root and in the depth calculation (the rest of traverse,
expansionState, result, node.children.forEach, and shouldTraverse logic remain
unchanged).
- Around line 36-48: Update the JSDoc on the tree-flattening function in
document-tree.ts to reflect the current implementation: remove or change the
"Always includes the root node" bullet to say that a structured
(schema-attached) root is skipped and its children are traversed regardless of
the root's expansion state, keep the bullet about only including children when
the parent is expanded for non-structured nodes, and revise the startDepth note
to clarify that when the root is skipped the provided startDepth applies to the
structured root's children (i.e., the visible top-level nodes); also ensure the
param names (expansionState, startDepth) and the "depth/indentation" behavior
are accurate in the docblock.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dfc8d7e0-9f52-4c14-832f-54aa3163248d

📥 Commits

Reviewing files that changed from the base of the PR and between edea366 and 55edbbb.

📒 Files selected for processing (4)
  • packages/ui/src/components/View/SourcePanel.tsx
  • packages/ui/src/components/View/SourceTargetView.test.tsx
  • packages/ui/src/components/View/TargetPanel.tsx
  • packages/ui/src/models/datamapper/document-tree.ts

@igarashitm
Copy link
Copy Markdown
Member

I think there is more architecturally sound way, but currently I don't have time to look into this issue. I'll look into later.

@igarashitm igarashitm marked this pull request as draft April 20, 2026 14:40
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.

DataMapper: Document node is a duplicate of the header

2 participants