[RFC] TypedDataKey#6867
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis pull request introduces a typed data access layer to replace generic Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/chart/Sankey.tsx (1)
491-503: Update line 502 to usegetTypedValuefor consistency and type safety.The tooltip payload construction in the 'source'/'target' branch uses
getValueByDataKey(payload, 'value')while the 'node' branch and other similar extractions (lines 491-492, 496-497) usegetTypedValue. Change line 502 togetTypedValue(payload, 'value')to complete the migration to the more type-safe function and maintain consistency.
🤖 Fix all issues with AI agents
In @src/polar/RadialBar.tsx:
- Around line 635-643: computeRadialBarDataItems currently returns nulls from
its map callback but is typed as ReadonlyArray<RadialBarDataItem>; change its
return type to ReadonlyArray<RadialBarDataItem | null> (update the function
signature for computeRadialBarDataItems) so the type matches possible nulls, and
update selectRadialBarSectors to filter out nulls before returning (e.g., append
.filter(Boolean) to the array returned by selectRadialBarSectors) so consumers
like RadialBarSectors and RadialBarLabelListProvider receive only non-null
RadialBarDataItem entries.
In @src/util/scale/getNiceTickValues.ts:
- Around line 179-186: The branch handling infinite cormin/cormax uses
Array(tickCount - 1) which can throw if tickCount <= 0; replace uses of
tickCount with the already-normalized count (from const count =
Math.max(tickCount, 2)) so the filled arrays are built with Array(count - 1) and
the returned values respect the normalized tick count; update the two spread
constructions that currently use Array(tickCount - 1) to use Array(count - 1)
(keeping the existing cormin/cormax placement and the min > max reverse logic).
🧹 Nitpick comments (3)
src/util/scale/getNiceTickValues.ts (1)
88-96: Loop-based tick generation looks behavior-preserving; minor type nitThis should match the previous “generate indices then map” behavior. Minor: prefer
const ticks: number[] = [];(same meaning, more idiomatic).test/cartesian/Area.typed.spec.tsx (1)
33-39: Consider addingvi.useFakeTimers()for timer-dependent tests.Per coding guidelines, tests should use
vi.useFakeTimers()due to Redux autoBatchEnhancer dependency on timers. While these tests may work without it, adding fake timers ensures consistent behavior.🔧 Suggested addition
describe('Area with strong typing', () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + describe('with all implicit types', () => {src/cartesian/Area.tsx (1)
121-172: GenericAreaPropsenables type-safe dataKey usage.The generic parameters
DataPointTypeandValueAxisTypeallow consumers to specify their data shape and get compile-time validation ofdataKey. ThedataKeyprop on line 171 usesTypedDataKey<DataPointType, ValueAxisType>to enforce the relationship.The commented-out line 172 (
// dataKey: DataKey<T>;) appears to be leftover from development.🧹 Remove commented code
dataKey: TypedDataKey<DataPointType, ValueAxisType>; - // dataKey: DataKey<T>;
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
eslint.config.mjssrc/cartesian/Area.tsxsrc/cartesian/Bar.tsxsrc/chart/Sankey.tsxsrc/component/LabelList.tsxsrc/component/Text.tsxsrc/polar/Pie.tsxsrc/polar/RadialBar.tsxsrc/state/chartDataSlice.tssrc/state/selectors/areaSelectors.tssrc/state/types/AreaSettings.tssrc/util/ChartUtils.tssrc/util/getTypedValue.tssrc/util/scale/getNiceTickValues.tssrc/util/scale/util/utils.tssrc/util/types.tstest/cartesian/Area.spec.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/axisSelectors.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/state/selectors/selectors.spec.tsxtest/util/ChartUtils/truncateByDomain.spec.tstest/util/getTypedValue.spec.tsxtest/util/scale/utils.spec.ts
💤 Files with no reviewable changes (2)
- src/util/scale/util/utils.ts
- test/util/scale/utils.spec.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*.{ts,tsx}: Never useanytype (implicit or explicit) in TypeScript code
Preferunknownoveranyand refine the type in TypeScript
Type function parameters and return values explicitly in TypeScript, do not rely on implicit any or inference; exceptions are React components and trivial functions
Do not useastype assertions in TypeScript; the only exception isas constAll imports from
rechartsmust use the public API entry point (e.g.,import { TooltipIndex } from 'recharts'). Imports from internal paths likerecharts/types/*orrecharts/src/*are not allowed and will fail the linter.
Files:
src/state/selectors/areaSelectors.tstest/cartesian/Area.spec.tsxsrc/util/types.tssrc/chart/Sankey.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/util/ChartUtils/truncateByDomain.spec.tssrc/polar/RadialBar.tsxsrc/util/getTypedValue.tssrc/cartesian/Bar.tsxsrc/state/types/AreaSettings.tstest/state/selectors/selectors.spec.tsxsrc/component/Text.tsxtest/state/selectors/axisSelectors.spec.tsxsrc/state/chartDataSlice.tssrc/util/scale/getNiceTickValues.tssrc/component/LabelList.tsxsrc/cartesian/Area.tsxsrc/util/ChartUtils.tssrc/polar/Pie.tsxtest/util/getTypedValue.spec.tsx
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Ensure code lints by running
npm run lintand follows Airbnb's Style Guide
Files:
src/state/selectors/areaSelectors.tstest/cartesian/Area.spec.tsxsrc/util/types.tssrc/chart/Sankey.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/util/ChartUtils/truncateByDomain.spec.tssrc/polar/RadialBar.tsxsrc/util/getTypedValue.tssrc/cartesian/Bar.tsxsrc/state/types/AreaSettings.tstest/state/selectors/selectors.spec.tsxsrc/component/Text.tsxtest/state/selectors/axisSelectors.spec.tsxsrc/state/chartDataSlice.tssrc/util/scale/getNiceTickValues.tssrc/component/LabelList.tsxsrc/cartesian/Area.tsxsrc/util/ChartUtils.tssrc/polar/Pie.tsxtest/util/getTypedValue.spec.tsx
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Do not hardcode any strings or formatting choices in library code; users should provide localized strings as desired
Files:
src/state/selectors/areaSelectors.tssrc/util/types.tssrc/chart/Sankey.tsxsrc/polar/RadialBar.tsxsrc/util/getTypedValue.tssrc/cartesian/Bar.tsxsrc/state/types/AreaSettings.tssrc/component/Text.tsxsrc/state/chartDataSlice.tssrc/util/scale/getNiceTickValues.tssrc/component/LabelList.tsxsrc/cartesian/Area.tsxsrc/util/ChartUtils.tssrc/polar/Pie.tsx
test/**/*.spec.{ts,tsx}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Aim for 100% unit test code coverage when writing new code
Files:
test/cartesian/Area.spec.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/util/ChartUtils/truncateByDomain.spec.tstest/state/selectors/selectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsxtest/util/getTypedValue.spec.tsx
test/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (test/README.md)
test/**/*.{test,spec}.{ts,tsx}: Aim for 100% unit test code coverage when writing new code
Prefer to use thecreateSelectorTestCasehelper function when writing or modifying tests
Use theexpectLastCalledWithhelper function instead ofexpect(spy).toHaveBeenLastCalledWith(...)for better typing and autocompletion
Verify the number of selector calls using the spy object fromcreateSelectorTestCaseto spot unnecessary re-renders and improve performance
MockgetBoundingClientRectin tests using the helper function provided intest/helper/MockGetBoundingClientRect.ts
Usevi.useFakeTimers()in all tests due to Redux autoBatchEnhancer dependency on timers andrequestAnimationFrame
Callvi.runOnlyPendingTimers()to advance timers after renders when not usingcreateSelectorTestCasehelper, and avoidvi.runAllTimers()to prevent infinite loops
UseuserEvent.setup({ advanceTimers: vi.runOnlyPendingTimers })or theuserEventSetuphelper function fromtest/helper/userEventSetup.tswhen creating userEvent instances
When testing tooltips on hover, usevi.runOnlyPendingTimers()after eachuserEvent.hover()call or use theshowTooltiphelper function fromtooltipTestHelpers.tsto account for requestAnimationFrame delays
Files:
test/cartesian/Area.spec.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/util/ChartUtils/truncateByDomain.spec.tstest/state/selectors/selectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsxtest/util/getTypedValue.spec.tsx
**/*.spec.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
When running unit tests, prefer to run a single test file using
npm run test -- path/to/TestFile.spec.tsxrather than running all tests withnpm testUnit tests should be placed in the
testdirectory, with some tests also allowed inwww/test. Test files follow the naming convention*.spec.tsx.
Files:
test/cartesian/Area.spec.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/util/ChartUtils/truncateByDomain.spec.tstest/state/selectors/selectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsxtest/util/getTypedValue.spec.tsx
🧠 Learnings (30)
📓 Common learnings
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6750
File: src/state/selectors/axisSelectors.ts:593-602
Timestamp: 2025-12-08T08:23:26.261Z
Learning: In the recharts codebase, `DataKey<any>` is an intentional exception to the "no any" rule while proper typing is being developed. This should not be flagged in reviews.
📚 Learning: 2025-12-16T08:12:06.809Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6783
File: test/util/ChartUtils.spec.tsx:15-16
Timestamp: 2025-12-16T08:12:06.809Z
Learning: In tests (files under any test directory, e.g., test/, __tests__/, etc.), allow importing internal module paths (e.g., ../../src/...) and do not require imports to use the public API entry point (src/index.ts). The public API import restriction should apply only to non-test code. During reviews, accept internal imports in test files and enforce the public API pattern only for non-test code.
Applied to files:
test/cartesian/Area.spec.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/state/selectors/selectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsxtest/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Verify the number of selector calls using the spy object from `createSelectorTestCase` to spot unnecessary re-renders and improve performance
Applied to files:
test/cartesian/Area.spec.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/state/selectors/selectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsxtest/util/getTypedValue.spec.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Applies to **/*.spec.{ts,tsx} : Unit tests should be placed in the `test` directory, with some tests also allowed in `www/test`. Test files follow the naming convention `*.spec.tsx`.
Applied to files:
test/cartesian/Area.spec.tsxtest/cartesian/Area.typed.spec.tsxtest/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Mock `getBoundingClientRect` in tests using the helper function provided in `test/helper/MockGetBoundingClientRect.ts`
Applied to files:
test/cartesian/Area.spec.tsxtest/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to storybook/stories/**/*.stories.tsx : Update Storybook stories when APIs have been changed to ensure they work as expected
Applied to files:
test/cartesian/Area.spec.tsxtest/state/selectors/selectors.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Use `vi.useFakeTimers()` in all tests due to Redux autoBatchEnhancer dependency on timers and `requestAnimationFrame`
Applied to files:
test/cartesian/Area.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/state/selectors/selectors.spec.tsxtest/util/getTypedValue.spec.tsx
📚 Learning: 2026-01-06T22:33:55.414Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6855
File: www/src/docs/api/AreaChartAPI.tsx:422-438
Timestamp: 2026-01-06T22:33:55.414Z
Learning: In Recharts 3, components can render inside any chart type as long as the parent provides the necessary context. For example, Funnel can render inside AreaChart, BarChart, ComposedChart, LineChart, and ScatterChart because they all provide cartesian context. This is a change from Recharts 2, which had stricter parent-child component limitations.
Applied to files:
test/cartesian/Area.spec.tsxtest/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/state/selectors/selectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsxsrc/cartesian/Area.tsx
📚 Learning: 2025-12-08T08:23:26.261Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6750
File: src/state/selectors/axisSelectors.ts:593-602
Timestamp: 2025-12-08T08:23:26.261Z
Learning: In the recharts codebase, `DataKey<any>` is an intentional exception to the "no any" rule while proper typing is being developed. This should not be flagged in reviews.
Applied to files:
src/util/types.tstest/cartesian/Area.typed.spec.tsxsrc/cartesian/Bar.tsxsrc/state/types/AreaSettings.tssrc/state/chartDataSlice.tssrc/cartesian/Area.tsxsrc/util/ChartUtils.tstest/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-23T13:30:10.395Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6669
File: www/src/docs/exampleComponents/ScatterChart/ScatterChartWithLabels.tsx:2-2
Timestamp: 2025-11-23T13:30:10.395Z
Learning: The `TooltipIndex` type from recharts is defined in `src/state/tooltipSlice.ts` but is not currently exported from the public API entry points. It should not be imported from `recharts/types/state/tooltipSlice` as this is an internal implementation path. An ESLint rule is needed to prevent regressions.
Applied to files:
src/chart/Sankey.tsxtest/state/selectors/dataSelectors.spec.tsxsrc/state/chartDataSlice.tssrc/polar/Pie.tsx
📚 Learning: 2025-12-14T13:58:49.197Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6771
File: src/shape/Curve.tsx:39-40
Timestamp: 2025-12-14T13:58:49.197Z
Learning: In the recharts codebase, hooks like useAppSelector and other hooks (e.g., useChartLayout()) return undefined when used outside Redux Provider context, instead of throwing. This enables components that call these hooks, such as Curve, to operate in standalone mode by falling back to prop values. During code reviews, ensure TSX files gracefully handle undefined results from hooks and implement fallbacks (e.g., via default props or explicit prop-based values) rather than assuming the hook is always within Provider.
Applied to files:
src/chart/Sankey.tsxsrc/polar/RadialBar.tsxsrc/cartesian/Bar.tsxsrc/component/Text.tsxsrc/component/LabelList.tsxsrc/cartesian/Area.tsxsrc/polar/Pie.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to test/component/**/*.spec.tsx : Use React Testing Library for testing component interactions and behavior upon rendering
Applied to files:
test/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxsrc/cartesian/Area.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Applies to **/*.{ts,tsx} : All imports from `recharts` must use the public API entry point (e.g., `import { TooltipIndex } from 'recharts'`). Imports from internal paths like `recharts/types/*` or `recharts/src/*` are not allowed and will fail the linter.
Applied to files:
test/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxsrc/cartesian/Area.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Applies to test-vr/**/*.spec.ts : Visual regression tests should follow the naming convention `*.spec.ts` and be placed in the `test-vr` directory. When updating snapshots, new files created in `test-vr/__snapshots__` should be committed to the repository.
Applied to files:
test/cartesian/Area.typed.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Use the `expectLastCalledWith` helper function instead of `expect(spy).toHaveBeenLastCalledWith(...)` for better typing and autocompletion
Applied to files:
test/cartesian/Area.typed.spec.tsxtest/state/selectors/selectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsxtest/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to test/**/*.spec.{ts,tsx} : Aim for 100% unit test code coverage when writing new code
Applied to files:
test/cartesian/Area.typed.spec.tsxtest/util/getTypedValue.spec.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: For release testing, use `yalc` to test Recharts changes in a test package as close as possible to the results of `npm publish` to ensure no unintended breaking changes are published.
Applied to files:
test/cartesian/Area.typed.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{ts,tsx} : Type function parameters and return values explicitly in TypeScript, do not rely on implicit any or inference; exceptions are React components and trivial functions
Applied to files:
test/cartesian/Area.typed.spec.tsxsrc/cartesian/Area.tsxtest/util/getTypedValue.spec.tsxeslint.config.mjs
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Prefer to use the `createSelectorTestCase` helper function when writing or modifying tests
Applied to files:
test/cartesian/Area.typed.spec.tsxtest/state/selectors/dataSelectors.spec.tsxtest/state/selectors/selectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsxtest/util/getTypedValue.spec.tsx
📚 Learning: 2025-12-14T13:58:58.361Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6771
File: src/shape/Curve.tsx:39-40
Timestamp: 2025-12-14T13:58:58.361Z
Learning: In the recharts codebase, `useAppSelector` and hooks like `useChartLayout()` are designed to return `undefined` when used outside Redux Provider context, rather than throwing errors. This allows components like `Curve` that call these hooks to work standalone by falling back to prop values.
Applied to files:
test/state/selectors/dataSelectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to storybook/stories/**/*.stories.tsx : Use Storybook for smoke tests and add play functions with assertions for actual tests
Applied to files:
test/state/selectors/dataSelectors.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Call `vi.runOnlyPendingTimers()` to advance timers after renders when not using `createSelectorTestCase` helper, and avoid `vi.runAllTimers()` to prevent infinite loops
Applied to files:
test/state/selectors/dataSelectors.spec.tsxtest/state/selectors/axisSelectors.spec.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Applies to **/*.stories.{ts,tsx} : When adding new Storybook stories, prioritize high-fidelity examples that you want published on the website and in the Storybook UI. Use low-fidelity tests in unit tests or visual regression tests instead to avoid exceeding the Chromatic open source plan limits.
Applied to files:
test/state/selectors/dataSelectors.spec.tsx
📚 Learning: 2025-12-16T08:12:13.355Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6783
File: test/util/ChartUtils.spec.tsx:15-16
Timestamp: 2025-12-16T08:12:13.355Z
Learning: In the recharts codebase, files in the `test` folder are allowed to import from internal paths (e.g., `../../src/state/cartesianAxisSlice`) and do not need to use the public API entry point (`src/index.ts`). The public API import restriction applies only to non-test code.
Applied to files:
test/util/ChartUtils/truncateByDomain.spec.tssrc/cartesian/Bar.tsxsrc/cartesian/Area.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Aim for 100% unit test code coverage when writing new code
Applied to files:
test/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{ts,tsx} : Never use `any` type (implicit or explicit) in TypeScript code
Applied to files:
eslint.config.mjs
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{js,ts,tsx} : Ensure code lints by running `npm run lint` and follows Airbnb's Style Guide
Applied to files:
eslint.config.mjs
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{ts,tsx} : Prefer `unknown` over `any` and refine the type in TypeScript
Applied to files:
eslint.config.mjs
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{ts,tsx} : Do not use `as` type assertions in TypeScript; the only exception is `as const`
Applied to files:
eslint.config.mjs
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Enable ESLint and Prettier configuration in your IDE for consistent development experience.
Applied to files:
eslint.config.mjs
🧬 Code graph analysis (17)
src/util/types.ts (1)
src/util/getTypedValue.ts (1)
TypedDataKey(8-20)
src/chart/Sankey.tsx (1)
src/util/getTypedValue.ts (1)
getTypedValue(49-67)
test/cartesian/Area.typed.spec.tsx (3)
test/helper/expectAreaCurve.ts (2)
ExpectedArea(4-6)expectAreaCurve(8-14)test/helper/createSelectorTestCase.tsx (1)
rechartsTestRender(172-184)src/cartesian/Area.tsx (1)
Area(1111-1113)
test/state/selectors/dataSelectors.spec.tsx (1)
test/_data.ts (1)
PageData(4-11)
test/util/ChartUtils/truncateByDomain.spec.ts (2)
src/util/ChartUtils.ts (1)
truncateByDomain(296-325)test/cartesian/ReferenceLine/getEndPoints.spec.tsx (1)
domain(9-11)
src/polar/RadialBar.tsx (2)
src/util/ChartUtils.ts (1)
truncateByDomain(296-325)src/util/getTypedValue.ts (1)
getTypedValue(49-67)
src/util/getTypedValue.ts (1)
src/util/DataUtils.ts (2)
isNullish(180-182)isNumOrStr(27-27)
src/cartesian/Bar.tsx (2)
src/util/ChartUtils.ts (2)
getBaseValueOfBar(565-585)truncateByDomain(296-325)src/util/getTypedValue.ts (1)
getTypedValue(49-67)
src/state/types/AreaSettings.ts (4)
src/state/graphicalItemsSlice.ts (1)
BaseCartesianGraphicalItemSettings(43-57)src/state/types/StackedGraphicalItem.ts (1)
MaybeStackedGraphicalItem(9-15)src/cartesian/Area.tsx (1)
BaseValue(64-64)src/util/getTypedValue.ts (1)
TypedDataKey(8-20)
test/state/selectors/selectors.spec.tsx (2)
src/cartesian/Area.tsx (2)
render(777-830)Area(1111-1113)test/_data.ts (1)
PageData(4-11)
src/component/Text.tsx (2)
src/index.ts (1)
RenderableText(28-28)src/util/DataUtils.ts (1)
isNullish(180-182)
test/state/selectors/axisSelectors.spec.tsx (2)
src/cartesian/Area.tsx (1)
Area(1111-1113)src/index.ts (1)
Area(88-88)
src/util/scale/getNiceTickValues.ts (1)
storybook/stories/data/Tick.ts (1)
ticks(16-16)
src/cartesian/Area.tsx (2)
src/util/getTypedValue.ts (2)
TypedDataKey(8-20)getTypedValue(49-67)src/state/chartDataSlice.ts (1)
ChartData(15-15)
src/util/ChartUtils.ts (2)
test/cartesian/ReferenceLine/getEndPoints.spec.tsx (1)
domain(9-11)src/util/DataUtils.ts (1)
isNumber(24-25)
src/polar/Pie.tsx (2)
src/util/getTypedValue.ts (1)
getTypedValue(49-67)storybook/stories/API/props/CartesianComponentShared.ts (1)
dataKey(3-10)
test/util/getTypedValue.spec.tsx (1)
src/util/getTypedValue.ts (2)
TypedDataKey(8-20)getTypedValue(49-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build, Test, Pack
🔇 Additional comments (32)
src/state/selectors/areaSelectors.ts (1)
27-27: Type change appropriately widens value property.The change from
[number, number]toReadonlyArray<unknown>aligns with the PR's typed data access goals, enabling more flexible value extraction while preserving readonly semantics.test/state/selectors/dataSelectors.spec.tsx (1)
54-59: Test data correctly updated for typed dataKey pattern.The change from primitive arrays to object arrays with explicit
dataKeyprops properly demonstrates the typed data access pattern introduced in this PR.src/state/chartDataSlice.ts (1)
15-15: Generic type parameter enhances type safety while maintaining backward compatibility.The addition of the generic parameter with default
T = unknownenables better type inference for consumers while preserving all existing usage patterns.test/state/selectors/axisSelectors.spec.tsx (1)
170-170: Test data systematically updated for typed dataKey pattern.All Area components have been consistently updated to use object-based data with explicit
dataKeyprops, properly testing the new typed data access pattern while maintaining comprehensive coverage of axis selector behavior.Also applies to: 235-235, 470-470, 1414-1415, 1464-1464
test/util/ChartUtils/truncateByDomain.spec.ts (1)
13-17: Test expectations correctly updated for new return type.The changes properly verify that
truncateByDomainnow returnsundefinedfor invalid or NaN domains, aligning with the updated function signature[number, number] | undefined.Also applies to: 56-60
test/state/selectors/selectors.spec.tsx (1)
1230-1249: LGTM! Test data extraction improves readability.Extracting the inline data arrays into named variables
data1anddata2makes the test more readable and maintainable. The test logic remains unchanged.src/util/types.ts (1)
37-37: LGTM! Core type alias for typed data access.The change from the previous
DataKey<T>definition to aliasingTypedDataKey<T>is the foundation for the typed data key functionality described in the PR objectives. This enables TypeScript to enforce the relationship between data and dataKey within components.Also applies to: 94-94
eslint.config.mjs (1)
473-486: LGTM! Strict typing enforcement for scale utilities.The new
neverAnyESLint configuration enforces stricter TypeScript rules for thesrc/util/scale/**directory. This prevents the use ofany, TypeScript comment pragmas, and type assertions, ensuring proper typing in utility code.As per coding guidelines, this aligns with the requirement to avoid
anytypes in TypeScript code.Also applies to: 515-515
src/polar/Pie.tsx (1)
66-66: LGTM! Adoption of typed data value extraction.The change from
getValueByDataKeytogetTypedValuealigns with the PR's systematic replacement of data extraction utilities with the new typed approach. This maintains the same runtime behavior while enabling better TypeScript support for label rendering.Also applies to: 607-607
test/cartesian/Area.spec.tsx (1)
652-652: LGTM! Test data aligned with typed data access pattern.The test data structure has been updated from primitive arrays
[1, 2, 3]to object arrays[{ value: 1 }, { value: 2 }, { value: 3 }], which is necessary for thedataKey="value"extraction to work correctly with the new typed data access approach. Both occurrences are updated consistently.Also applies to: 704-704
src/state/types/AreaSettings.ts (1)
5-5: LGTM! Type upgrade aligns with PR objectives.The change from
DataKey<any>toTypedDataKeyimplements the stricter typing described in the PR. This enables IDE autocomplete and type safety for thedataKeyproperty while maintaining backward compatibility.Also applies to: 12-12
src/polar/RadialBar.tsx (1)
645-645: LGTM! Data extraction updated to use typed utility.The replacement of
getValueByDataKeywithgetTypedValuealigns with the PR's goal of introducing stricter typing while maintaining functional equivalence.src/component/Text.tsx (1)
77-79: LGTM! Runtime type guard complements compile-time types.The new
isRenderableTextguard correctly validates values against theRenderableTexttype, enabling safer runtime checks in rendering paths. The implementation properly covers all union members: nullish values (viaisNullish) and string/number/boolean primitives.test/util/getTypedValue.spec.tsx (1)
1-235: LGTM! Comprehensive test coverage for new utility.The test suite thoroughly covers
getTypedValuefunctionality:
- Object and array data access (string/numeric keys, nested paths)
- Default value fallbacks
- Edge cases (undefined, null, NaN)
- Function-based accessors
- Compile-time type inference validation
The type inference tests are particularly valuable for verifying TypeScript behavior with
TypedDataKey.src/cartesian/Bar.tsx (2)
89-89: LGTM!Import of
getTypedValueis correct and aligns with the PR's goal of migrating to typed data access.
987-1014: Type-safe data extraction and null-safety improvements look good.The changes properly:
- Update
stackedDomaintoReadonlyArray<unknown> | nullto match the updatedtruncateByDomainsignature- Add explicit null check after
truncateByDomain(lines 1002-1004) since it now returnsundefinedfor invalid domains- Use
getTypedValuefor raw value extraction in the non-stacked pathOne consideration:
value[1]on line 1016 is nowunknownrather than a number. TheminPointSizeCallbackincludes explicit runtime validation—it checks that the value is numeric or null/undefined and throws an invariant assertion if not. This validation ensures correctness at runtime, though TypeScript loses type information at the call site.test/cartesian/Area.typed.spec.tsx (4)
1-4: LGTM!Imports are correct. The test file properly uses the public API entry point for
AreaandAreaChartas required by coding guidelines, while internal test helpers are imported from relative paths (which is allowed for test files per learnings).
22-40: Well-structured backward compatibility tests.The test suite appropriately documents the limitations of TypeScript's contextual typing—when data is on AreaChart and dataKey is on Area, type checking cannot connect them. The comments clearly explain this is expected behavior for backward compatibility.
132-148: Good use of @ts-expect-error for compile-time validation.The tests at lines 140-143 correctly use
@ts-expect-errorto verify that TypeScript catches mismatched dataKeys when data is defined directly on Area. This validates the PR's typing improvements work as intended.
151-240: Comprehensive explicit type parameter tests.The tests for
<Area<ExampleDataPoint>>and<Area<ExampleDataPoint, number>>validate the new generic type parameters work correctly. The@ts-expect-errorassertions confirm TypeScript catches type mismatches at compile time.src/util/ChartUtils.ts (2)
44-46: Good deprecation notice.The
@deprecatedJSDoc appropriately guides users to the newgetTypedValueutility. This supports the gradual migration path mentioned in the PR objectives.
296-324: Breaking change intruncateByDomainreturn type correctly implemented.The function now returns
undefinedinstead of the original value when the domain is invalid. This is a safer approach that forces callers to handle the error case explicitly.The changes are:
domainparameter now acceptsReadonlyArray<unknown> | null | undefined- Return type changed to
[number, number] | undefined- Invalid domain returns
undefined(line 301)All callers have been updated to handle the new return type:
src/cartesian/Bar.tsx:1002checksif (truncated == null)before using the valuesrc/polar/RadialBar.tsx:640checksif (truncated == null)before using the valuesrc/component/LabelList.tsx (4)
11-12: LGTM!Imports correctly bring in the new typed utilities for value extraction and renderability checking.
19-19: Broadeningvaluetype tounknownis appropriate.This change acknowledges that the value can come from arbitrary user data. The runtime
isRenderableTextchecks (lines 155, 197) ensure only valid text types are rendered. This is a safer pattern than assuming the type at compile time.
151-159: DefensivedefaultAccessorimplementation.The accessor now properly:
- Handles array values by extracting the last element
- Validates renderability before returning
- Returns
undefinedfor non-renderable valuesThis prevents runtime errors from attempting to render objects or other non-text types.
193-199: Type-safe value extraction with renderability guard.The updated logic:
- Uses
getTypedValuefor typed data access whendataKeyis provided- Guards against non-renderable values with
isRenderableTextcheck- Returns
nullearly to skip rendering invalid valuesThis is a robust pattern that prevents rendering failures.
src/util/getTypedValue.ts (3)
4-20: Well-designedTypedDataKeytype.The conditional type handles multiple scenarios elegantly:
- Falls back to loose typing when
Tisanyorunknown- Supports legacy string-indexed types
- Provides strict key inference for well-typed objects
- Supports array index access via template literal
`${number}`- Allows function accessors
The use of
anyon line 8 is appropriate per the codebase's intentional exception forDataKey<any>while proper typing is being developed. Based on learnings, this should not be flagged.
22-48: Comprehensive function overloads for type inference.The overloads cover all practical use cases:
- Direct property access (
keyof T)- Function-based extraction
- Array index access
- Generic fallback with explicit return type
This enables TypeScript to infer the correct return type based on how the function is called, improving IDE autocomplete and type safety.
49-67: Clean implementation with proper null-safety.The implementation:
- Returns
defaultValuefor nullish inputs (defensive)- Uses
es-toolkit/compat/getfor safe nested property access- Invokes function keys directly
- Falls back gracefully for unsupported key types
This matches the behavior of the deprecated
getValueByDataKeywhile providing better type information.src/cartesian/Area.tsx (3)
15-16: LGTM!Import changes correctly replace
getValueByDataKeyusage withgetTypedValueand addTypedDataKeyfor the typed dataKey prop.
1000-1017: Type-safe value extraction incomputeArea.The changes properly:
- Type
valueAsArrayasReadonlyArray<unknown> | undefinedto match the typed approach- Use
getTypedValue(entry, dataKey)for extraction- Handle array vs scalar values appropriately
- Update
isBreakPointcalculation to usegetTypedValueThis maintains the same runtime behavior while improving type awareness.
1111-1115: Type assertion for generic memoized component.The
asassertion on the memoized component is necessary becauseReact.memodoesn't preserve generic type parameters. This pattern is standard for generic React components that need memoization.The
@ts-expect-erroron line 1114 fordisplayNameis acceptable since TypeScript doesn't understand that the cast type can have additional properties.
| const untruncatedValue = stackedData[index + dataStartIndex]; | ||
| if (untruncatedValue == null) { | ||
| return null; | ||
| } | ||
| const truncated = truncateByDomain(untruncatedValue, stackedDomain); | ||
| if (truncated == null) { | ||
| return null; | ||
| } | ||
| value = truncated; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for all usages of computeRadialBarDataItems
echo "=== Usages of computeRadialBarDataItems ==="
rg -n -C5 'computeRadialBarDataItems' src/
# Search for selectRadialBarSectors implementation
echo -e "\n=== selectRadialBarSectors definition ==="
rg -n -A20 'selectRadialBarSectors\s*=' src/
# Search for how sectors is used in RadialBarSectors
echo -e "\n=== RadialBarSectors component ==="
rg -n -B5 -A15 'const RadialBarSectors' src/Repository: recharts/recharts
Length of output: 2742
🏁 Script executed:
#!/bin/bash
# Get the selectRadialBarSectors function
echo "=== selectRadialBarSectors function ==="
rg -n 'selectRadialBarSectors' src/state/selectors/radialBarSelectors.ts -A 30
# Get RadialBarSectors component
echo -e "\n=== RadialBarSectors component ==="
rg -n 'function RadialBarSectors|const RadialBarSectors' src/polar/RadialBar.tsx -A 30
# Check how the sectors data is used - look for .map on sectors
echo -e "\n=== Sectors usage in rendering ==="
rg -n 'sectors\.' src/polar/RadialBar.tsx -B2 -A2Repository: recharts/recharts
Length of output: 3611
🏁 Script executed:
#!/bin/bash
# Get the full selectRadialBarSectors selector implementation
rg -n 'selectRadialBarSectors' src/state/selectors/radialBarSelectors.ts -A 60 | head -80Repository: recharts/recharts
Length of output: 2430
🏁 Script executed:
#!/bin/bash
# Read the specific section of the file
sed -n '414,450p' src/state/selectors/radialBarSelectors.tsRepository: recharts/recharts
Length of output: 979
🏁 Script executed:
#!/bin/bash
# Get the function signature and return statement
sed -n '581,650p' src/polar/RadialBar.tsx | head -80Repository: recharts/recharts
Length of output: 1869
🏁 Script executed:
#!/bin/bash
# Get the complete function signature
sed -n '581,625p' src/polar/RadialBar.tsxRepository: recharts/recharts
Length of output: 1153
Fix return type annotation and add null filtering to selectRadialBarSectors.
The function computeRadialBarDataItems now returns null values from its map callback (lines 635-643) but the return type is still declared as ReadonlyArray<RadialBarDataItem>, creating a type safety violation.
The selector selectRadialBarSectors (src/state/selectors/radialBarSelectors.ts:395) directly returns this result without filtering nulls. Consumers in RadialBarSectors (line 139) and RadialBarLabelListProvider (line 91-92) iterate the array with non-null type assertions and will fail at runtime if null entries are present.
Update the return type of computeRadialBarDataItems to ReadonlyArray<RadialBarDataItem | null>, and add .filter(Boolean) in selectRadialBarSectors to remove null entries before returning.
🤖 Prompt for AI Agents
In @src/polar/RadialBar.tsx around lines 635 - 643, computeRadialBarDataItems
currently returns nulls from its map callback but is typed as
ReadonlyArray<RadialBarDataItem>; change its return type to
ReadonlyArray<RadialBarDataItem | null> (update the function signature for
computeRadialBarDataItems) so the type matches possible nulls, and update
selectRadialBarSectors to filter out nulls before returning (e.g., append
.filter(Boolean) to the array returned by selectRadialBarSectors) so consumers
like RadialBarSectors and RadialBarLabelListProvider receive only non-null
RadialBarDataItem entries.
| if (cormin === -Infinity || cormax === Infinity) { | ||
| const values = | ||
| const values: Array<number> = | ||
| cormax === Infinity | ||
| ? [cormin, ...range(0, tickCount - 1).map(() => Infinity)] | ||
| : [...range(0, tickCount - 1).map(() => -Infinity), cormax]; | ||
| ? [cormin, ...Array(tickCount - 1).fill(Infinity)] | ||
| : [...Array(tickCount - 1).fill(-Infinity), cormax]; | ||
|
|
||
| return min > max ? values.reverse() : values; | ||
| } |
There was a problem hiding this comment.
Potential runtime crash: Array(tickCount - 1) can throw for small/invalid tickCount
You already normalize via const count = Math.max(tickCount, 2) (Line 176), but this branch still uses tickCount. If a caller passes tickCount <= 0, this will now throw RangeError: Invalid array length. Use count for the filled portion (and optionally for the returned length) to keep the “>= 2 ticks” contract.
Proposed fix
if (cormin === -Infinity || cormax === Infinity) {
- const values: Array<number> =
+ const values: number[] =
cormax === Infinity
- ? [cormin, ...Array(tickCount - 1).fill(Infinity)]
- : [...Array(tickCount - 1).fill(-Infinity), cormax];
+ ? [cormin, ...Array(count - 1).fill(Infinity)]
+ : [...Array(count - 1).fill(-Infinity), cormax];
return min > max ? values.reverse() : values;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (cormin === -Infinity || cormax === Infinity) { | |
| const values = | |
| const values: Array<number> = | |
| cormax === Infinity | |
| ? [cormin, ...range(0, tickCount - 1).map(() => Infinity)] | |
| : [...range(0, tickCount - 1).map(() => -Infinity), cormax]; | |
| ? [cormin, ...Array(tickCount - 1).fill(Infinity)] | |
| : [...Array(tickCount - 1).fill(-Infinity), cormax]; | |
| return min > max ? values.reverse() : values; | |
| } | |
| if (cormin === -Infinity || cormax === Infinity) { | |
| const values: number[] = | |
| cormax === Infinity | |
| ? [cormin, ...Array(count - 1).fill(Infinity)] | |
| : [...Array(count - 1).fill(-Infinity), cormax]; | |
| return min > max ? values.reverse() : values; | |
| } |
🤖 Prompt for AI Agents
In @src/util/scale/getNiceTickValues.ts around lines 179 - 186, The branch
handling infinite cormin/cormax uses Array(tickCount - 1) which can throw if
tickCount <= 0; replace uses of tickCount with the already-normalized count
(from const count = Math.max(tickCount, 2)) so the filled arrays are built with
Array(count - 1) and the returned values respect the normalized tick count;
update the two spread constructions that currently use Array(tickCount - 1) to
use Array(count - 1) (keeping the existing cormin/cormax placement and the min >
max reverse logic).
| }, | ||
| ]; | ||
|
|
||
| const neverAny = { |
There was a problem hiding this comment.
TypeScript does not have these settings so eslint has to fill that gap
| let isRange = false; | ||
|
|
||
| const points: ReadonlyArray<AreaPointItem> = displayedData.map((entry, index): AreaPointItem => { | ||
| let valueAsArray: [number, number] | undefined; |
There was a problem hiding this comment.
The [number, number] was a mistake - charts can have string domains on both axes and they work perfectly fine, so this should be unknowns. As long as we can feed it to the scale, everything is fair game.
| const value1 = valueAsArray?.[1] ?? null; | ||
|
|
||
| const isBreakPoint = value1 == null || (hasStack && !connectNulls && getValueByDataKey(entry, dataKey) == null); | ||
| const isBreakPoint = value1 == null || (hasStack && !connectNulls && getTypedValue(entry, dataKey) == null); |
There was a problem hiding this comment.
Usage is the same
| * @consumes CartesianChartContext | ||
| */ | ||
| export const Area: ComponentType<Props> = React.memo(AreaFn, propsAreEqual); | ||
| export const Area = React.memo(AreaFn, propsAreEqual) as <DataPointType = any, ValueAxisType = any>( |
There was a problem hiding this comment.
Defaults to any to keep existing behavior.
There was a problem hiding this comment.
I don't like this definition. If you can think of ways to improve it I am listening.
There was a problem hiding this comment.
This is probably the most interesting file to review @ckifer
| */ | ||
| const { container } = rechartsTestRender( | ||
| <AreaChart data={data} width={400} height={400}> | ||
| <Area<ExampleDataPoint> dataKey="value" /> |
| */ | ||
| const { container } = rechartsTestRender( | ||
| <AreaChart width={400} height={400}> | ||
| <Area data={data} dataKey={entry => entry.value} /> |
| const { container } = rechartsTestRender( | ||
| <AreaChart data={data} width={400} height={400}> | ||
| {/* @ts-expect-error TypeScript is correct here - the dataKey does not match and this should be an error */} | ||
| <Area<ExampleDataPoint, number> dataKey="name" /> |
There was a problem hiding this comment.
New (optional) syntax - it doesn't feel great.
There was a problem hiding this comment.
but better than not being possible
| <ComposedChart data={PageData} width={100} height={100}> | ||
| <Area dataKey="" /> | ||
| <Area dataKey="" data={[{ x: 10 }, { x: 20 }, { x: 30 }]} /> | ||
| <Area dataKey="x" data={[{ x: 10 }, { x: 20 }, { x: 30 }]} /> |
There was a problem hiding this comment.
This was incorrectly set up before (it didn't matter in the test) but TypeScript is highlighting it as a compile-time error now which I think is a great improvement!
f1f8ec2 to
0711f4c
Compare
|
Nice! Also not the biggest fan of the generic component syntax but whatcha gonna do, this is a nice step forward and if people want to make their charts strictly typed they'll need to make them uglier. We have to be careful not to break this once its in place though |
|
Hi!, dunno if its late or if it can help, but on my product codebase I've baked this solution for a typesafe rechart version, taking advantage of the new features from recharts 3 + the good old render prop pattern. This an example for ComposedChart: import {token} from '@translated/babele-core'
import * as C from 'recharts'
import {ChartContainer} from '@/ui/Charts'
import {ChartArea} from './ChartArea'
import {ChartBar} from './ChartBar'
import {ChartTooltip} from './ChartTooltip'
import {ChartXAxis} from './ChartXAxis'
import {ChartYAxis} from './ChartYAxis'
export declare namespace ComposedChart {
export interface Props<TPoint extends Record<string, unknown>> {
/**
* @todo cannot be readonly because of Recharts types
*/
data: TPoint[]
chartRef?: React.Ref<HTMLDivElement>
render: (props: {
Bar: typeof ChartBar<TPoint>
Area: typeof ChartArea<TPoint>
XAxis: typeof ChartXAxis<TPoint>
YAxis: typeof ChartYAxis<TPoint>
Tooltip: typeof ChartTooltip<TPoint>
}) => React.ReactNode
}
}
export function ComposedChart<TPoint extends Record<string, unknown>>(
props: ComposedChart.Props<TPoint>,
) {
return (
<ChartContainer ref={props.chartRef}>
<C.ComposedChart data={props.data}>
<C.CartesianGrid
vertical={false}
strokeDasharray="4 4"
stroke={token('colors.border.neutral.secondary')}
/>
{props.render({
Bar: ChartBar,
Area: ChartArea,
XAxis: ChartXAxis,
YAxis: ChartYAxis,
Tooltip: ChartTooltip,
})}
</C.ComposedChart>
</ChartContainer>
)
}Then the usage becomes: <ComposedChart
chartRef={chartRef}
data={points}
render={({Bar, XAxis, Tooltip}) => {
return (
<>
<XAxis
dataKey="order_date"
tickFormatter={(d) => formatChartDate(d, props.breakdownBy)} // d is of type Date!
/>
<Bar dataKey="my_kpi" tone={kpiToTone.quality} /> // Bar knows which keys are available thanks to the render prop wiring
<Tooltip
tone={kpiToTone.quality}
title={(p) => formatChartDate(p.order_date, props.breakdownBy)}
mainRow={(p) => {
if (p.ept_r1 == null) {
return
}
return `${formatEPT(p.ept_r1)} EPT`
}}
/>
</>
)
}}
/>I'm experimenting with this in my codebase, but I think its shaping in a good direction, its working good. On the topic TkDodo recently published an article: Building Type-Safe Compound Components, that shares the same principle, but with a factory function instead of the render prop |
|
Thanks @cloud-walker. I'm inclined towards the typed-function call (what the blog post calls "Component Factory Pattern") rather than renderProps because there's less runtime impact. I want to first test how it impacts bundle size and treeshaking though which is why I started with the explicit typing first. |
## Description Breaking down #6867 into smaller PRs that we can merge easier. The types tend to spread everywhere so I aim trying to isolate smaller chunks - this one is adding a generic to `ChartData` and `DataProvider` types. These generics are exposed to charts but ## Related Issue #6645 ## Types of changes New feature I think? - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist: I will add docs once it's done and ready for release - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have added tests to cover my changes. - [ ] I have added a storybook story or VR test, or extended an existing story or VR test to show my changes <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved type safety across chart components by introducing generic data-point typing and a standardized data-provider pattern for more reliable data handling. * **Stories & Tests** * Strengthened example stories and tests with explicit data types and runtime null-safety checks to reduce runtime errors and improve developer confidence. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
00dcef6 to
71b2129
Compare
71b2129 to
037fe46
Compare
037fe46 to
a54f522
Compare
a54f522 to
2589912
Compare
2589912 to
f20e9cd
Compare


Description
Proof of concept of how we could add strict typing to our chart data. Includes new types for DataKey and get getValueByDataKey, and demo usage on Area component.
There should be no change in behavior, all is just types.
I'm looking for opinions. Once we have agreed this is the way to go I will work on removing the old type definition completely.
Benefits:
anydataanddataKeyand check that the key matches the data shapeCons:
Related Issue
#6645
How Has This Been Tested?
All tests and types are passing - the build output and omnidoc are not, I will fix before merging.
Screenshots (if appropriate):
Types of changes
Checklist:
Summary by CodeRabbit
Release Notes
Refactor
Chores
✏️ Tip: You can customize this high-level summary in your review settings.