Skip to content

feat(Canvas): Filter placeholder nodes in screenshots and docs#3031

Merged
lordrip merged 1 commit intoKaotoIO:mainfrom
lordrip:feat/filter-placeholders
Mar 16, 2026
Merged

feat(Canvas): Filter placeholder nodes in screenshots and docs#3031
lordrip merged 1 commit intoKaotoIO:mainfrom
lordrip:feat/filter-placeholders

Conversation

@lordrip
Copy link
Copy Markdown
Member

@lordrip lordrip commented Mar 12, 2026

Context

Currently, there are multiple placeholder nodes across the Canvas making interacting with the flows easier. The downside is that when exporting screenshots or documentation about the flow, all these placeholders are included, making the picture more complicated than it needs to be.

Changes

The fix is to filter all placeholders before drawing the graph. In addition to that, both ExportDocumentPreviewModal and FlowExportImage components should use the same <HiddenCanvas> component to have a consistent image.

Note

A follow-up is needed to consolidate all placeholder-related logic, because in some cases, the placeholder wording gets added to the path, while in others not.

Screenshots

Canvas view

image
Exported screenshot Exported documentation
kaoto-flow (1) image

fix: #3029

Summary by CodeRabbit

  • New Features

    • Hidden canvas now supports automatic download and notifies when an image blob is generated.
    • Option to exclude placeholder nodes from flow diagram exports for cleaner visuals.
  • Improvements

    • Overhauled export preview workflow for more reliable image/ZIP generation and URL cleanup.
    • Better integration with visualization context for smoother export UX.
  • Tests

    • Expanded tests for export preview, hidden canvas, and overall export flows.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

The PR threads a new removePlaceholder option through FlowService to omit placeholder nodes/edges, updates HiddenCanvas to request placeholder-free diagrams and support auto-download/onBlobGenerated callbacks, refactors ExportDocument to use HiddenCanvas and new hooks, and removes DocumentationService.generateFlowImage.

Changes

Cohort / File(s) Summary
Flow Service Enhancement
packages/ui/src/components/Visualization/Canvas/flow.service.ts, packages/ui/src/components/Visualization/Canvas/flow.service.test.ts
Added options: { removePlaceholder?: boolean } to getFlowDiagram, appendNodesAndEdges, and getEdgesFromVizNode. Traversal and edge construction now skip placeholder nodes/edges when enabled. New test asserts placeholder removal.
HiddenCanvas & Flow Export
packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/HiddenCanvas.tsx, packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/HiddenCanvas.test.tsx, packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/FlowExportImage.tsx
HiddenCanvas gains autoDownload?: boolean and onBlobGenerated?: (blob: Blob) => void. It calls FlowService.getFlowDiagram(..., { removePlaceholder: true }), invokes onBlobGenerated, and performs optional auto-download with URL lifecycle management. FlowExportImage now uses useEntityContext and passes autoDownload. Tests expanded for blob callbacks and URL revocation.
ExportDocument Refactor
packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocument.tsx, packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocument.test.tsx, packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.tsx, packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.test.tsx
Refactored ExportDocument/PreviewModal to use HiddenCanvas for image generation, switch to useEntityContext/useVisualizationController, remove isOpen prop, manage blob/URL lifecycle, and update tests to cover markdown/ZIP generation and download flow.
Documentation Service & Tests
packages/ui/src/services/documentation.service.ts, packages/ui/src/services/documentation.service.test.ts
Removed DocumentationService.generateFlowImage and its associated test; image generation responsibility moved to HiddenCanvas.
Placeholder Type
packages/ui/src/models/placeholder.constants.ts
Changed PlaceholderType from enum to const enum for compile-time inlining.
Cypress Sync Fix
packages/ui-tests/cypress/support/next-commands/nodeConfiguration.ts
Added wait for markdown preview loading spinner to disappear before comparing documentation tables.

Sequence Diagram

sequenceDiagram
    participant User
    participant ExportDoc as ExportDocument
    participant HiddenCanvas as HiddenCanvas
    participant FlowService as FlowService
    participant DocService as DocumentationService
    participant Browser as BrowserDownload

    User->>ExportDoc: Click "Export"
    ExportDoc->>HiddenCanvas: Render (isGeneratingImage=true)
    HiddenCanvas->>FlowService: getFlowDiagram(entityVizNode, { removePlaceholder: true })
    FlowService-->>HiddenCanvas: CanvasNodesAndEdges (placeholders removed)
    HiddenCanvas->>HiddenCanvas: render & toBlob()
    HiddenCanvas->>ExportDoc: onBlobGenerated(blob)
    ExportDoc->>DocService: generateMarkdown(blob, filename)
    DocService-->>ExportDoc: markdown
    ExportDoc->>DocService: generateDocumentationZip(...)
    DocService-->>ExportDoc: zipBlob
    ExportDoc->>Browser: trigger download(zipBlob URL)
    Browser-->>User: download kaoto-export.zip
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related issues

  • Issue #2851: Adds removePlaceholder to FlowService and uses it in HiddenCanvas to exclude placeholder nodes from exported images, matching the issue objective.

Possibly related PRs

  • PR #2997: Modifies FlowService placeholder handling in traversal logic; strongly related to these changes.

Suggested reviewers

  • igarashitm
  • PVinaches

Poem

🐰 I scuttled through nodes both big and small,

Removed the ghosts that tripped the export call,
A hidden canvas hummed — a quiet art,
Blob wrapped up neat, a download to impart.
The rabbit hops off, zip in paw, joy in heart.

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 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 change: adding functionality to filter placeholder nodes from canvas exports and documentation generation.
Linked Issues check ✅ Passed The PR successfully implements the objective from issue #3029 by refactoring ExportDocumentPreviewModal to use HiddenCanvas, aligning it with FlowExportImage and ensuring consistent image generation without affecting the main canvas.
Out of Scope Changes check ✅ Passed All changes directly support the primary objectives of filtering placeholder nodes and consolidating export components to use HiddenCanvas. Supporting changes include placeholder type updates, flow service enhancements, and test infrastructure adjustments, all within scope.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can disable the changed files summary in the walkthrough.

Disable the reviews.changed_files_summary setting to disable the changed files summary in the walkthrough.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 12, 2026

Codecov Report

❌ Patch coverage is 90.00000% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.99%. Comparing base (89dd298) to head (dac90f6).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
...rc/components/Visualization/Canvas/flow.service.ts 88.23% 2 Missing ⚠️
...n/ContextToolbar/ExportDocument/ExportDocument.tsx 33.33% 2 Missing ⚠️
...lbar/ExportDocument/ExportDocumentPreviewModal.tsx 94.28% 2 Missing ⚠️
...on/ContextToolbar/FlowExportImage/HiddenCanvas.tsx 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3031      +/-   ##
==========================================
+ Coverage   89.73%   89.99%   +0.25%     
==========================================
  Files         564      564              
  Lines       21030    21038       +8     
  Branches     4926     4924       -2     
==========================================
+ Hits        18872    18933      +61     
+ Misses       2156     1980     -176     
- Partials        2      125     +123     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lordrip lordrip marked this pull request as ready for review March 12, 2026 12:13
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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.test.tsx (1)

121-135: Assert that the export path enables placeholder filtering.

These tests verify the async blob flow, but they never check that FlowService.getFlowDiagram is called in placeholder-removal mode. That is the behavior this PR is meant to protect, so a regression there would still leave this suite green. Add an expectation on the mocked flow-service call after GRAPH_LAYOUT_END_EVENT.

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

In
`@packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.test.tsx`
around lines 121 - 135, The test misses asserting that
FlowService.getFlowDiagram is invoked in placeholder-removal mode; after
triggering the GRAPH_LAYOUT_END_EVENT (i.e., after calling
eventListenerCallback) add an expectation that FlowService.getFlowDiagram was
called with the placeholder-filtering flag (for example verifying a call
containing { removePlaceholders: true } or the specific arg your mock expects)
so the test ensures the export path enables placeholder filtering; locate this
in ExportDocumentPreviewModal.test.tsx around the GRAPH_LAYOUT_END_EVENT trigger
and add the expect for FlowService.getFlowDiagram accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/Visualization/Canvas/flow.service.ts`:
- Around line 48-57: Filtering out placeholders can leave a group with zero
visible children while the original pre-filter structure still implies fallback
edges, causing missing prev->group edges; to fix, capture the original children
list before filtering (via vizNodeParam.getChildren()), then when
removePlaceholder is true and filtered children is empty but original
children.length > 0 and vizNodeParam.data.isGroup, still derive/append the
fallback edges for the group (call getEdgesFromVizNode() / appendEdges or invoke
appendNodesAndEdges on the group itself) so the prev->group edge is preserved;
apply the same change pattern wherever children are filtered (the blocks around
appendNodesAndEdges, getEdgesFromVizNode references) so empty-after-filter
groups retain their fallback edges.

In
`@packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocument.test.tsx`:
- Around line 49-57: The two waitFor blocks in ExportDocument.test.tsx that call
wrapper.findAllByTestId('export-document-preview-h1') and
wrapper.findAllByTestId('export-document-preview-table') drop their promises so
assertions may run after the test ends; fix by removing the outer waitFor and
directly awaiting the findAllByTestId calls (e.g., const headers = await
wrapper.findAllByTestId('export-document-preview-h1');
expect(headers).toHaveLength(3); and similarly for tables) so the test properly
awaits the assertions.

In
`@packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.tsx`:
- Around line 68-74: The code creates object URLs with URL.createObjectURL in
handleBlobGenerated and in the download logic but never revokes them; update
handleBlobGenerated (and the download URL creation path) to revoke any previous
object URL before creating a new one (keep the current image URL in state, e.g.
flowImageUrl) and call URL.revokeObjectURL(prevUrl), revoke the generated
preview URL when the modal closes/unmounts, and revoke the temporary download
URL immediately after triggering the download (or after a short timeout) so
blobs are not retained; touch handleBlobGenerated, the download handler (where
downloadUrl is created), and the modal cleanup/unmount logic to implement this
revoke behavior.

---

Nitpick comments:
In
`@packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.test.tsx`:
- Around line 121-135: The test misses asserting that FlowService.getFlowDiagram
is invoked in placeholder-removal mode; after triggering the
GRAPH_LAYOUT_END_EVENT (i.e., after calling eventListenerCallback) add an
expectation that FlowService.getFlowDiagram was called with the
placeholder-filtering flag (for example verifying a call containing {
removePlaceholders: true } or the specific arg your mock expects) so the test
ensures the export path enables placeholder filtering; locate this in
ExportDocumentPreviewModal.test.tsx around the GRAPH_LAYOUT_END_EVENT trigger
and add the expect for FlowService.getFlowDiagram accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 537caf28-35a6-45de-a62f-a34e81e658b1

📥 Commits

Reviewing files that changed from the base of the PR and between 89dd298 and 585b224.

⛔ Files ignored due to path filters (1)
  • packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (12)
  • packages/ui/src/components/Visualization/Canvas/flow.service.test.ts
  • packages/ui/src/components/Visualization/Canvas/flow.service.ts
  • packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocument.test.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocument.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.test.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/FlowExportImage.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/HiddenCanvas.test.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/HiddenCanvas.tsx
  • packages/ui/src/models/placeholder.constants.ts
  • packages/ui/src/services/documentation.service.test.ts
  • packages/ui/src/services/documentation.service.ts
💤 Files with no reviewable changes (2)
  • packages/ui/src/services/documentation.service.ts
  • packages/ui/src/services/documentation.service.test.ts

Comment thread packages/ui/src/components/Visualization/Canvas/flow.service.ts
@shivamG640 shivamG640 linked an issue Mar 12, 2026 that may be closed by this pull request
@lordrip lordrip marked this pull request as draft March 12, 2026 13:28
@lordrip lordrip force-pushed the feat/filter-placeholders branch from 585b224 to 17c2a29 Compare March 12, 2026 13:28
igarashitm
igarashitm previously approved these changes Mar 12, 2026
Currently, there are multiple placeholder nodes across the Canvas making
interacting with the flows easier. The downside is that when exporting
screenshots or documentation about the flow, all these placeholders are
included, making the picture more complicated than it needs to be.

The fix is to filter all placeholders before drawing the graph. In
addition to that, both `ExportDocumentPreviewModal` and `FlowExportImage` components should use the same `<HiddenCanvas>` component to have a consistent image.

A follow-up is needed to consolidate all `placeholder-related` logic,
because in some cases, the `placeholder` wording gets added to the path,
while in others not.

fix: KaotoIO#3029
@sonarqubecloud
Copy link
Copy Markdown

@lordrip lordrip marked this pull request as ready for review March 16, 2026 12:54

Cypress.Commands.add('documentationTableCompare', (routeName: string, expectedTableData: string[][]) => {
// Wait until the loading distractor is no longer in the document
cy.get('[data-testid="Loading markdown preview"]', { timeout: 30_000 }).should('not.exist');
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@tplevko I added this timeout here to wait a bit longer because the doc feature takes more time in firefox

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 (1)
packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.tsx (1)

91-107: Download URL cleanup is functional but could be more robust.

The setTimeout-based revocation (lines 103-106) works but the timeout handle isn't stored/cleared if the component unmounts during the 100ms window. This is a minor issue since the revocation will still occur, but a warning may appear if the component unmounts very quickly after download.

♻️ Optional: Track and clear the timeout on unmount
+ const downloadTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
+
+ useEffect(() => {
+   return () => {
+     if (downloadTimeoutRef.current) {
+       clearTimeout(downloadTimeoutRef.current);
+     }
+   };
+ }, []);

  const onDownload = async () => {
    // ...
    link.click();
    // Revoke the temporary download URL after a short timeout
-   setTimeout(() => {
+   downloadTimeoutRef.current = setTimeout(() => {
      URL.revokeObjectURL(downloadUrl);
    }, 100);
  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.tsx`
around lines 91 - 107, The onDownload handler revokes the temporary URL via
setTimeout which can fire after the component unmounts; store the timeout ID
(e.g., in a ref) when you call setTimeout inside
ExportDocumentPreviewModal.onDownload and clear it in a cleanup (useEffect
return) when the component unmounts, and also call
URL.revokeObjectURL(downloadUrl) immediately if possible after link.click() to
be safe; ensure the cleanup clears the stored timeout and revokes any remaining
downloadUrl to avoid post-unmount operations.
🤖 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/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.tsx`:
- Around line 91-107: The onDownload handler revokes the temporary URL via
setTimeout which can fire after the component unmounts; store the timeout ID
(e.g., in a ref) when you call setTimeout inside
ExportDocumentPreviewModal.onDownload and clear it in a cleanup (useEffect
return) when the component unmounts, and also call
URL.revokeObjectURL(downloadUrl) immediately if possible after link.click() to
be safe; ensure the cleanup clears the stored timeout and revokes any remaining
downloadUrl to avoid post-unmount operations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 420368b6-340c-4493-9247-315c411b7f58

📥 Commits

Reviewing files that changed from the base of the PR and between 585b224 and dac90f6.

⛔ Files ignored due to path filters (1)
  • packages/ui/src/components/Visualization/Canvas/__snapshots__/flow.service.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (13)
  • packages/ui-tests/cypress/support/next-commands/nodeConfiguration.ts
  • packages/ui/src/components/Visualization/Canvas/flow.service.test.ts
  • packages/ui/src/components/Visualization/Canvas/flow.service.ts
  • packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocument.test.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocument.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.test.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/ExportDocument/ExportDocumentPreviewModal.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/FlowExportImage.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/HiddenCanvas.test.tsx
  • packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/HiddenCanvas.tsx
  • packages/ui/src/models/placeholder.constants.ts
  • packages/ui/src/services/documentation.service.test.ts
  • packages/ui/src/services/documentation.service.ts
💤 Files with no reviewable changes (2)
  • packages/ui/src/services/documentation.service.ts
  • packages/ui/src/services/documentation.service.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/ui/src/components/Visualization/ContextToolbar/FlowExportImage/FlowExportImage.tsx
  • packages/ui/src/models/placeholder.constants.ts
  • packages/ui/src/components/Visualization/Canvas/flow.service.test.ts

@lordrip lordrip merged commit 9f381b8 into KaotoIO:main Mar 16, 2026
15 checks passed
@lordrip lordrip deleted the feat/filter-placeholders branch March 16, 2026 13:01
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.

Let ExportDocument component also use HiddenCanvas Exclude placeholder nodes from exported canvas images

2 participants