Skip to content

fix(export): PNG/SVG export leaks + wire Export PNG action (#872) - #873

Merged
alfredo1996 merged 2 commits into
release/1.0from
fix/issue-872-png-svg-export
May 21, 2026
Merged

fix(export): PNG/SVG export leaks + wire Export PNG action (#872)#873
alfredo1996 merged 2 commits into
release/1.0from
fix/issue-872-png-svg-export

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented May 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fixes the dispose() leak in exportChartToSvgsvgInstance declared outside try, dispose() moved into finally with null-check so it runs on every throw path (HIGH)
  • Wires a new Export PNG widget action alongside Export SVG. Uses getDataURL({ type: "png", pixelRatio: 2, backgroundColor }) with #0a0f1e (dark) / #ffffff (light) to match the on-screen theme (HIGH)
  • Rounds SVG dimensions via Math.round before passing to echarts.init (MEDIUM)
  • New exportChartToPng helper in component/src/charts/base-chart.tsx, exported via @neoboard/components
  • 8 new unit tests covering both helpers: success / no-instance / setOption-throws / no-svg / dimension rounding / PNG light + dark backgrounds (MEDIUM)
  • 1 new E2E spec verifying both export actions appear on an ECharts widget and the PNG click triggers a .png download

Defers LOW items (font-embed tooltip, generic triggerDownload mime tightening) — out of scope per drill.

Why

exportChartToSvg leaked the offscreen ECharts instance (canvas, ResizeObserver, animation frames) whenever setOption or serializeToString threw — the finally only removed the offscreen DOM node. Repeated failed exports slowly leaked memory.

PNG is the format users reach for first (paste into doc/email/Slack). triggerPngDownload was exported but no UI wired it up. Now it does.

Test plan

  • Unit: 8 new tests in component/src/charts/__tests__/chart-export.test.ts
  • Unit: full component suite (1320 passed) + full app suite (2757 passed)
  • Lint clean
  • Production build clean
  • E2E: new app/e2e/chart-export.spec.ts passes
  • E2E: full Playwright sweep (283 passed, 25 flaky/pre-existing, 0 failed)
  • Manual: open Movie Analytics, action menu shows Export PNG + Export SVG; PNG downloads with theme-matched background; theme toggle changes the next PNG's background

Closes #872

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Summary by CodeRabbit

Release Notes

  • New Features

    • Added PNG export support for dashboard charts, enabling users to export widgets as PNG files alongside existing SVG/CSV formats
    • PNG exports automatically apply appropriate colors based on the current light or dark theme
  • Tests

    • Added comprehensive test coverage for chart export functionality

Review Change Stack

- Fix dispose() leak in exportChartToSvg: declare svgInstance outside try,
  move dispose() into finally with null-check so it runs on every throw path
- Round SVG dimensions before passing to echarts.init (avoids fractional
  width/height that some SVG viewers handle inconsistently)
- Add exportChartToPng helper using getDataURL with pixelRatio 2 and theme-
  matched background (#0a0f1e dark / #ffffff light)
- Wire Export PNG widget action alongside existing Export SVG, gated by
  capabilities.isECharts
- 8 unit tests covering both helpers: success / no-instance / setOption-
  throws / no-svg / dimension rounding / PNG light + dark backgrounds
- E2E test: ECharts widget exposes both Export PNG and Export SVG actions
  and PNG click triggers a .png download
- Refresh package-lock.json to match release/1.0 versions (1.0.0)

Closes #872

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alfredo1996 alfredo1996 added bug Something isn't working pkg:app Next.js application package pkg:component UI component library area:widgets Widget system area:charts Chart rendering labels May 21, 2026
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@alfredo1996 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 26 minutes and 59 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dfb61905-f63d-4505-9935-a55466efc06f

📥 Commits

Reviewing files that changed from the base of the PR and between f108c61 and 5096a71.

📒 Files selected for processing (1)
  • app/src/components/dashboard-container.tsx

Walkthrough

Adds PNG export support to ECharts widgets by fixing an SVG instance disposal leak, implementing a new PNG export function that reads from the existing canvas renderer, wiring the PNG action into the widget menu, and validating both export paths with unit and E2E tests.

Changes

Chart PNG Export Feature

Layer / File(s) Summary
SVG and PNG export functions with instance cleanup
component/src/charts/base-chart.tsx, component/src/charts/index.ts
Refactored exportChartToSvg to declare the SVG instance outside the try block and move disposal into a finally block, ensuring cleanup even if setOption or serializeToString throws. Rounds offscreen dimensions to integers before passing to echarts.init. Added exportChartToPng which extracts a PNG data URL from the canvas-rendered instance using a theme-aware background color and 2× pixel ratio. Updated module exports.
PNG export action wiring in dashboard
app/src/components/dashboard-container.tsx
Extended imports to include PNG export utilities. Implemented exportWidgetPng function that queries the widget's chart element, exports it to PNG, builds a filename, and triggers download. Added "Export PNG" menu action alongside existing CSV and SVG exports for ECharts-capable widgets.
Export function unit test coverage
component/src/charts/__tests__/chart-export.test.ts
Mocked ECharts init, instance getters, and PNG/SVG helpers with deterministic chart dimensions. Tests for exportChartToSvg cover success path with disposal, null return when no instance exists, guaranteed disposal when setOption or serializeToString throws, null return when no SVG element renders, and dimension rounding before echarts.init. Tests for exportChartToPng verify PNG data URL generation with correct pixelRatio: 2 and theme-dependent background colors, and null return when no chart instance exists.
End-to-end PNG export workflow
app/e2e/chart-export.spec.ts
Playwright E2E test navigates to the "Movie Analytics" dashboard, locates an ECharts widget, verifies both "Export PNG" and "Export SVG" menu items are visible, clicks PNG export, waits for a download event, and asserts the filename ends with .png.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • alfredo1996/neoboard#720: Both PRs modify ECharts export actions in dashboard-container.tsx and the chart export pipeline in base-chart.tsx; the retrieved PR introduces exportChartToSvg, while this PR extends it with exportChartToPng and fixes the instance disposal leak.

Suggested labels

testing

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 title directly addresses the main changes: fixing PNG/SVG export leaks and wiring the Export PNG action, matching the PR's primary objectives.
Linked Issues check ✅ Passed The PR fully addresses all HIGH and MEDIUM objectives from #872: fixes the dispose() leak, wires Export PNG action, rounds SVG dimensions, and adds comprehensive unit and E2E tests.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #872 objectives. No extraneous modifications detected; the PR includes only the export leak fix, PNG action implementation, dimension rounding, and related tests.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-872-png-svg-export

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/e2e/chart-export.spec.ts (1)

12-13: ⚡ Quick win

Assert dashboard API response success before parsing JSON.

Add an expect(res.ok()).toBeTruthy() check before res.json() to fail fast with a clearer cause when the request is unauthorized or fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/e2e/chart-export.spec.ts` around lines 12 - 13, Before parsing the
dashboard response, assert the HTTP success on the Response object returned by
page.request.get("/api/dashboards"); add an expectation like
expect(res.ok()).toBeTruthy() immediately after obtaining res so the test fails
fast with a clear authorization/error message before calling res.json() and
assigning to dashboards.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/e2e/chart-export.spec.ts`:
- Around line 12-13: Before parsing the dashboard response, assert the HTTP
success on the Response object returned by page.request.get("/api/dashboards");
add an expectation like expect(res.ok()).toBeTruthy() immediately after
obtaining res so the test fails fast with a clear authorization/error message
before calling res.json() and assigning to dashboards.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d6d91fbb-d99e-465b-89b2-a4898b5e8aa4

📥 Commits

Reviewing files that changed from the base of the PR and between fa0be8c and f108c61.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • app/e2e/chart-export.spec.ts
  • app/src/components/dashboard-container.tsx
  • component/src/charts/__tests__/chart-export.test.ts
  • component/src/charts/base-chart.tsx
  • component/src/charts/index.ts

…7778)

SonarCloud flagged sequential actions.push() calls in dashboard-container.tsx
as a MINOR maintainability issue (Array#push called multiple times).
Combine the PNG + SVG action pushes into a single call with both args.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@alfredo1996
alfredo1996 merged commit f1f2e79 into release/1.0 May 21, 2026
32 of 34 checks passed
@alfredo1996
alfredo1996 deleted the fix/issue-872-png-svg-export branch May 21, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:charts Chart rendering area:widgets Widget system bug Something isn't working pkg:app Next.js application package pkg:component UI component library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants