diff --git a/packages/visx-xychart/src/providers/DataProvider.tsx b/packages/visx-xychart/src/providers/DataProvider.tsx index 57655e90d..c28caa728 100644 --- a/packages/visx-xychart/src/providers/DataProvider.tsx +++ b/packages/visx-xychart/src/providers/DataProvider.tsx @@ -5,7 +5,7 @@ import createOrdinalScale from '@visx/scale/lib/scales/ordinal'; import { AxisScaleOutput } from '@visx/axis'; import { ResizeObserverPolyfill } from '@visx/responsive/lib/types'; -import { XYChartTheme } from '../types'; +import { DataContextType, XYChartTheme } from '../types'; import ThemeContext from '../context/ThemeContext'; import DataContext from '../context/DataContext'; import useDataRegistry from '../hooks/useDataRegistry'; @@ -13,10 +13,17 @@ import useDimensions, { Dimensions } from '../hooks/useDimensions'; import useScales from '../hooks/useScales'; import isDiscreteScale from '../utils/isDiscreteScale'; +export type XScale> = + ScaleConfigToD3Scale; + +export type YScale> = + ScaleConfigToD3Scale; + /** Props that can be passed to initialize/update the provider config. */ export type DataProviderProps< XScaleConfig extends ScaleConfig, YScaleConfig extends ScaleConfig, + Datum extends object = any, > = { /* Optionally define the initial dimensions. */ initialDimensions?: Partial; @@ -26,6 +33,8 @@ export type DataProviderProps< xScale: XScaleConfig; /* y-scale configuration whose shape depends on scale type. */ yScale: YScaleConfig; + /* Optionally specify the data registry to be used. This is useful if you want to sync domain/range of differently sized charts. */ + dataRegistry?: DataContextType, YScale, Datum>['dataRegistry']; /* Any React children. */ children: React.ReactNode; /* Determines whether Series will be plotted horizontally (e.g., horizontal bars). By default this will try to be inferred based on scale types. */ @@ -46,10 +55,11 @@ export default function DataProvider< theme: propsTheme, xScale: xScaleConfig, yScale: yScaleConfig, + dataRegistry: dataRegistryOverride, children, horizontal: initialHorizontal = 'auto', resizeObserverPolyfill, -}: DataProviderProps) { +}: DataProviderProps) { // `DataProvider` provides a theme so that `ThemeProvider` is not strictly needed. // `props.theme` takes precedent over `context.theme`, which has a default even if // a ThemeProvider is not present. @@ -59,18 +69,17 @@ export default function DataProvider< const innerWidth = Math.max(0, width - margin.left - margin.right); const innerHeight = Math.max(0, height - margin.top - margin.bottom); - type XScale = ScaleConfigToD3Scale; - type YScale = ScaleConfigToD3Scale; + const dataRegistry = + dataRegistryOverride ?? useDataRegistry, YScale, Datum>(); - const dataRegistry = useDataRegistry(); - - const { xScale, yScale }: { xScale?: XScale; yScale?: YScale } = useScales({ - dataRegistry, - xScaleConfig, - yScaleConfig, - xRange: [margin.left, Math.max(0, width - margin.right)], - yRange: [Math.max(0, height - margin.bottom), margin.top], - }); + const { xScale, yScale }: { xScale?: XScale; yScale?: YScale } = + useScales({ + dataRegistry, + xScaleConfig, + yScaleConfig, + xRange: [margin.left, Math.max(0, width - margin.right)], + yRange: [Math.max(0, height - margin.bottom), margin.top], + }); const registryKeys = dataRegistry.keys(); diff --git a/packages/visx-xychart/test/providers/DataProvider.test.tsx b/packages/visx-xychart/test/providers/DataProvider.test.tsx index 38c53d361..095bd35e2 100644 --- a/packages/visx-xychart/test/providers/DataProvider.test.tsx +++ b/packages/visx-xychart/test/providers/DataProvider.test.tsx @@ -1,12 +1,16 @@ import React, { useContext, useEffect } from 'react'; import { render } from '@testing-library/react'; -import { DataProvider, DataContext } from '../../src'; -import { DataProviderProps } from '../../lib/providers/DataProvider'; +import { DataProvider, DataContext, DataContextType } from '../../src'; +import { DataProviderProps, XScale, YScale } from '../../lib/providers/DataProvider'; +import useDataRegistry from '../../src/hooks/useDataRegistry'; + +const xScaleConfig = { type: 'linear' } as const; +const yScaleConfig = { type: 'linear' } as const; // eslint-disable-next-line @typescript-eslint/no-explicit-any -const getWrapper = (consumer: React.ReactNode, props?: DataProviderProps) => { +const getWrapper = (consumer: React.ReactNode, props?: Partial>) => { render( - + {consumer} , ); @@ -85,4 +89,52 @@ describe('', () => { getWrapper(); }); + + it('should use specified data registry', () => { + expect.assertions(1); + + const Wrapper = () => { + const dataRegistry = useDataRegistry< + XScale, + YScale, + any + >(); + return ( + + + + ); + }; + + const DataConsumer = ({ + dataRegistry, + }: { + dataRegistry: DataContextType< + XScale, + YScale, + any + >['dataRegistry']; + }) => { + const { registerData } = useContext(DataContext); + + useEffect(() => { + if (registerData) { + registerData({ + key: 'visx', + xAccessor: (d) => d.x, + yAccessor: (d) => d.y, + data: [ + { x: 0, y: 1 }, + { x: 5, y: 7 }, + ], + }); + expect(dataRegistry.keys()).toEqual(['visx']); + } + }, [registerData]); + + return null; + }; + + render(); + }); });