feat(react-ui): add D3Charts alongside the Recharts charts - #1248
Merged
Merged
Conversation
Bring the D3-based chart set into react-ui as components/D3Charts: Area, Line, Bar (vertical/horizontal), Pie, Radial, Radar, Scatter, Heatmap, Funnel, SegmentedBar, CalendarHeatmap, plus StackedLegend and the legend store. Same data contract as the Recharts charts (Array<Record<string, string | number>> + categoryKey/dataKey), so the genui-lib wrappers can switch over later without prompt changes. - Classes compose from CHART_CLASS_PREFIX / _prefix.scss, set to `openui-d3` so they don't collide with the Recharts `openui-*` classes while both ship in components.css. - Scroll-button rules are nested under their container so they beat IconButton's single-class rules regardless of stylesheet order. - Only `data-openui-chart` is emitted for the PPTX exporter. - Adds d3-scale, d3-selection, d3-shape. - tsconfig.build.json excludes colocated *.test.ts(x) so tests are not emitted into dist (vitest was picking the dist copies up). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Stacked areas keep one running total per row (Recharts' default
stackOffset): a negative value lowers the stack instead of dropping
below zero. Bars keep stacking by sign (Recharts' stackOffset="sign").
The stack offset is threaded through the stack, the y domain, the scale
and the y-axis width measurement.
- Pie and Radial get legendVariant ("default" | "stacked", default
"stacked"): the chart with a built-in stacked legend showing each item's
share, side by side from 400px wide, below the chart when narrower. It
reuses the legend store, so hover and toggle work both ways; an external
legendKey still takes over.
- SegmentedBar defaults to the compact legend, as SingleStackedBar does.
- Radar gets variant ("line" | "area", default "line") and areaOpacity
(default 0.2); dots are off by default; the grid is solid in the
interactive border tone. fillOpacity still overrides.
- Stacked legend swatch boxes fit their row, so a list that fits no longer
reads as overflowing (scroll arrows on five items).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… stories - MiniAreaChart, MiniBarChart, MiniLineChart: the Recharts minis' props and look — the most recent values that fit, square frame (size, min 100px), area gradient / 1.5px line, 8px right-aligned bars with rounded tops and the inner line, 2px line. Geometry matches the Recharts minis point for point, including recharts-scale's 5-tick "nice" value range (it rounds above d3's .nice(), and without an axis that shows as mark height). Unit tests pin the measured Recharts coordinates. - Stories: every Recharts chart story (124 in 15 files) copied under Components/D3Charts/… with only the chart import and the title changed. The import points at stories/rechartsStoryAdapters, which exports the Recharts chart names typed with the Recharts props and renders the D3 replacement, so each story renders the D3 chart with its args unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Condensed Area/Bar/Line charts always angled their x-axis labels and took the label height out of the chart's fixed height, so long category names left no room for the plot (flat lines, no bars). The chart height now stays fixed and the label band never takes more than half of it; labels that don't fit are truncated with an ellipsis and show their full text on hover. Condensed charts default to single-line horizontal labels like the Recharts condensed charts, `tickVariant="angled"` rotates them 45° when they don't fit, and only every n-th label is drawn when categories are too narrow to read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ankit-thesys
added this pull request to stack #1267
September 29, 2026 12:27
4 of 5 tasks
2 of 4 tasks
This was referenced Sep 30, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the D3-based chart set to react-ui as
components/D3Charts, next to the existing RechartsCharts/. This is the first step toward replacing the Recharts charts: the D3 charts take the same data contract (Array<Record<string, string | number>>+categoryKey/dataKey), so thegenui-libchart wrappers can switch over in a follow-up without prompt or schema changes. Nothing that exists today changes behaviour.Changes
src/components/D3Charts/: Area, Line, Bar (vertical and horizontal), Pie, Radial, Radar, Scatter, Heatmap, Funnel, SegmentedBar, CalendarHeatmap and MiniArea / MiniBar / MiniLine, plusStackedLegend/LegendStoreProvider, with their unit tests (36 files, 349 tests). They use react-ui's ownThemeProvider,Button,IconButton,SeparatorandPrintContextthrough internal imports.Class prefix: every class, keyframe and CSS variable is composed from
CHART_CLASS_PREFIX/_prefix.scss, set toopenui-d3. With plainopenui, 65 class names would collide with the Recharts charts (legend, tooltip, scroll buttons, containers), because both stylesheets ship incomponents.css. When Recharts is removed, switching toopenuiis a two-line change.classPrefix.test.tsfails if anything hardcodes the prefix.Scroll buttons: their rules are nested under the buttons container (specificity 0,2,0), so they win over
IconButton's single-class rules whatever order the stylesheets load in. The generatedcomponents/index.scssforwardsD3ChartsbeforeIconButton.PPTX export: charts emit
data-openui-chart, the same contract as the Recharts charts.Defaults match the Recharts charts, so the D3 chart renders what the Recharts one does for the same props:
stackOffset), so a negative value lowers the stack rather than dropping below zero. Bars keep stacking by sign, as Recharts'stackOffset="sign"does.legendVariant("default" | "stacked", default"stacked"): a built-in stacked legend with each item's share, side by side from 400px wide and below the chart when narrower. An externallegendKeystill takes over.variant("line" | "area", default"line") andareaOpacity(default 0.2), dots are off by default, and the grid is solid.X-axis labels (Area, Line, Bar): the chart keeps its height whatever the labels are. The label band takes at most half of it, and labels that don't fit are truncated with an ellipsis, with the full text on hover.
tickVariant="angled"is now accepted: it rotates labels 45° when they don't fit horizontally.This deliberately differs from Recharts, which makes the chart taller to fit tilted labels (668px in the big-labels story) and so breaks fixed-height layouts such as grids.
Mini charts:
MiniAreaChart,MiniBarChart,MiniLineCharttake the Recharts minis' props and match them point for point: the same most-recent-values-that-fit rule, square frame, gradient area, right-aligned 8px bars with the inner line, and 2px line. That includes Recharts' own rounding of the value range (recharts-scale), which rounds higher than d3's.nice()and shows directly as mark height without an axis. Unit tests pin coordinates measured from the Recharts minis.Stories: every Recharts chart story (124 stories in 15 files) is copied under
Components/D3Charts/…, changing only the chart import and the title. The import points atstories/rechartsStoryAdapters, which exports the Recharts chart names typed with the Recharts props and renders the D3 replacement. So each story renders the D3 chart with its args unchanged, and every copy type-checks against the Recharts props. This adds roughly 15k lines of copied story code; stories aren't part of the build.Dependencies:
d3-scale,d3-selection,d3-shape(+@types). The lockfile diff contains only these entries.tsconfig.build.json: excludes colocated*.test.ts(x). They were being compiled intodist/, and vitest then ran the compiled copies too. This also affects the existingCharts/shared/LineInBarShapetest.Exports: not added to the root
index.ts. The charts are reachable through the existing./*subpath (import { AreaChart } from "@openuidev/react-ui/D3Charts"), and their styles are incomponents.cssandstyles/d3Charts.css.Changeset:
@openuidev/react-uipatch.Test Plan
pnpm install --frozen-lockfile,pnpm run build:packagesandpnpm --filter "./packages/**" run ciall pass. Lint output is warnings only, all of which already exist onmain; none are inD3Charts.tsc --noEmitis clean with bothtsconfig.jsonandtsconfig.test.json.react-ui vitest: 395 tests pass, 349 of them new.
Storybook parity (QA
apps/visualization-bench,storybookproject): each of the 124 Recharts stories is paired with its D3 copy. There are no page or console errors on the D3 side, marks are drawn wherever Recharts draws them, no Area/Bar/Line plot is squeezed below half its chart's height, and the legend and series names match in every story. Screenshots of both sides were reviewed.Recharts parity (QA
apps/visualization-bench,/parity): 19 Recharts/D3 pairs in light and dark, fed the props react-ui's genui-lib wrappers pass. Both sides render with no errors and show the same categories and series in the same order. All 19 pairs pass, and the Mini charts now have D3 counterparts.CSS parity: the compiled
d3Charts.cssis identical to the original implementation's stylesheet apart from the prefix. I rendered 15 cases in Storybook and in the original implementation with the same props: all 11 charts, plus a 40-point scrolling line chart, a 14-series overflowing legend and a 16-slice pie withStackedLegend. Computed styles and boxes of all 1,622 elements match in both light and dark mode.Load order: I audited every chart element that is also a react-ui
Button,IconButtonorSeparatorin default, hover and disabled states, comparing the bundled order with chart CSS loaded last. The only value that still depends on order is the background of disabled scroll buttons, which arevisibility: hidden.browser-bundle: none of the D3Charts JS ends up in
openui-bundle.min.js.openui-styles.cssgrows by about 28 KB because it now includes the D3 styles.Not applicable (explain why)
Verified locally
Checklist
genui-liband the root exports are untouchedFollow-ups (not in this PR)
Differences the story copies make visible, not changed here:
themepalettes (theme="emerald"etc.) are ignored, so colours come from the ThemeProvider palette;"step"variant uses step-after (Recharts steps at the midpoint);Point the
genui-lib/Chartswrappers at D3Charts.The Recharts
Charts/scroll buttons have the same load-order issue (their background resolves to transparent incomponents.css).components/ChartsV2/onmaincontains only a staleARCHITECTURE.md.🤖 Generated with Claude Code