From 9612b6f06a63af4d6791663eca97c2c27e7a2c76 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Thu, 15 Oct 2020 16:03:13 -0700 Subject: [PATCH] test(xychart): add BarStack tests (#866) * tes(xychart/BarStack): add tests * test(xychart/combineBarStackData): add tests * test(xychart/findNearestStackDatum): add tests * internal(xychart): remove console logs in tests --- .../visx-xychart/src/hooks/useDimensions.ts | 11 ++- .../src/providers/DataProvider.tsx | 6 +- .../test/components/BarStack.test.tsx | 83 +++++++++++++++++++ .../visx-xychart/test/mocks/getDataContext.ts | 16 ++-- .../test/utils/combineBarStackData.test.tsx | 51 ++++++++++++ .../test/utils/findNearestDatum.test.ts | 28 ++++++- 6 files changed, 182 insertions(+), 13 deletions(-) create mode 100644 packages/visx-xychart/test/components/BarStack.test.tsx create mode 100644 packages/visx-xychart/test/utils/combineBarStackData.test.tsx diff --git a/packages/visx-xychart/src/hooks/useDimensions.ts b/packages/visx-xychart/src/hooks/useDimensions.ts index 47c9e16e8..ba79fad14 100644 --- a/packages/visx-xychart/src/hooks/useDimensions.ts +++ b/packages/visx-xychart/src/hooks/useDimensions.ts @@ -6,11 +6,16 @@ const INITIAL_DIMENSIONS = { margin: { top: 0, right: 0, bottom: 0, left: 0 }, }; -type Dimensions = typeof INITIAL_DIMENSIONS; +export type Dimensions = typeof INITIAL_DIMENSIONS; /** A hook for accessing and setting memoized width, height, and margin chart dimensions. */ -export default function useDimensions(): [Dimensions, (dims: Dimensions) => void] { - const [dimensions, privateSetDimensions] = useState(INITIAL_DIMENSIONS); +export default function useDimensions( + initialDimensions?: Partial, +): [Dimensions, (dims: Dimensions) => void] { + const [dimensions, privateSetDimensions] = useState({ + ...INITIAL_DIMENSIONS, + ...initialDimensions, + }); // expose a setter with better memoization logic const publicSetDimensions = useCallback( diff --git a/packages/visx-xychart/src/providers/DataProvider.tsx b/packages/visx-xychart/src/providers/DataProvider.tsx index 28139495c..afc559fd2 100644 --- a/packages/visx-xychart/src/providers/DataProvider.tsx +++ b/packages/visx-xychart/src/providers/DataProvider.tsx @@ -7,7 +7,7 @@ import { XYChartTheme } from '../types'; import ThemeContext from '../context/ThemeContext'; import DataContext from '../context/DataContext'; import useDataRegistry from '../hooks/useDataRegistry'; -import useDimensions from '../hooks/useDimensions'; +import useDimensions, { Dimensions } from '../hooks/useDimensions'; import useScales from '../hooks/useScales'; /** Props that can be passed to initialize/update the provider config. */ @@ -15,6 +15,7 @@ export type DataProviderProps< XScaleConfig extends ScaleConfig, YScaleConfig extends ScaleConfig > = { + initialDimensions?: Partial; theme?: XYChartTheme; xScale: XScaleConfig; yScale: YScaleConfig; @@ -26,6 +27,7 @@ export default function DataProvider< YScaleConfig extends ScaleConfig, Datum extends object >({ + initialDimensions, theme: propsTheme, xScale: xScaleConfig, yScale: yScaleConfig, @@ -36,7 +38,7 @@ export default function DataProvider< // a ThemeProvider is not present. const contextTheme = useContext(ThemeContext); const theme = propsTheme || contextTheme; - const [{ width, height, margin }, setDimensions] = useDimensions(); + const [{ width, height, margin }, setDimensions] = useDimensions(initialDimensions); const innerWidth = width - (margin?.left ?? 0) - (margin?.right ?? 0); const innerHeight = height - (margin?.top ?? 0) - (margin?.bottom ?? 0); diff --git a/packages/visx-xychart/test/components/BarStack.test.tsx b/packages/visx-xychart/test/components/BarStack.test.tsx new file mode 100644 index 000000000..1a71a3c75 --- /dev/null +++ b/packages/visx-xychart/test/components/BarStack.test.tsx @@ -0,0 +1,83 @@ +import React, { useContext } from 'react'; +import { mount } from 'enzyme'; +import { BarStack, BarSeries, DataProvider, DataContext } from '../../src'; + +const providerProps = { + initialDimensions: { width: 100, height: 100 }, + xScale: { type: 'linear' }, + yScale: { type: 'linear' }, +} as const; + +const accessors = { + xAccessor: (d: { x: number }) => d.x, + yAccessor: (d: { y: number }) => d.y, +}; + +const series1 = { + key: 'bar1', + data: [ + { x: 10, y: 5 }, + { x: 7, y: 5 }, + ], + ...accessors, +}; + +const series2 = { + key: 'bar2', + data: [ + { x: 10, y: 5 }, + { x: 7, y: 20 }, + ], + ...accessors, +}; + +describe('', () => { + it('should be defined', () => { + expect(BarSeries).toBeDefined(); + }); + + it('should render rects', () => { + const wrapper = mount( + + + + + + + + , + ); + expect(wrapper.find('rect')).toHaveLength(4); + }); + + it('should update scale domain to include stack sums including negative values', () => { + expect.hasAssertions(); + + function Assertion() { + const { yScale, dataRegistry } = useContext(DataContext); + if (yScale && dataRegistry?.keys().length === 2) { + expect(yScale.domain()).toEqual([-20, 10]); + } + return null; + } + + mount( + + + + + + + + + , + ); + }); +}); diff --git a/packages/visx-xychart/test/mocks/getDataContext.ts b/packages/visx-xychart/test/mocks/getDataContext.ts index ab3b6c4bc..c916c1f8a 100644 --- a/packages/visx-xychart/test/mocks/getDataContext.ts +++ b/packages/visx-xychart/test/mocks/getDataContext.ts @@ -8,16 +8,18 @@ const height = 10; const margin = { top: 0, right: 0, bottom: 0, left: 0 }; const noOp = () => {}; -const getDataContext = ( - entries?: Parameters[0], -): DataContextType => { +function getDataContext(entries?: Parameters[0]) { const dataRegistry = new DataRegistry(); if (entries) dataRegistry.registerData(entries); - const mockContext = { + const mockContext: DataContextType = { dataRegistry, - registerData: dataRegistry.registerData, - unregisterData: dataRegistry.unregisterData, + registerData: data => { + dataRegistry.registerData(data); + }, + unregisterData: keys => { + dataRegistry.unregisterData(keys); + }, xScale: scaleLinear({ domain: [0, 10], range: [0, width] }), yScale: scaleLinear({ domain: [0, 10], range: [0, height] }), colorScale: scaleOrdinal({ domain: ['sf', 'ny', 'la'], range: ['purple', 'violet', 'grape'] }), @@ -31,6 +33,6 @@ const getDataContext = ( }; return mockContext; -}; +} export default getDataContext; diff --git a/packages/visx-xychart/test/utils/combineBarStackData.test.tsx b/packages/visx-xychart/test/utils/combineBarStackData.test.tsx new file mode 100644 index 000000000..926d4a8d9 --- /dev/null +++ b/packages/visx-xychart/test/utils/combineBarStackData.test.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { BarSeries } from '../../src'; +import combineBarStackData from '../../src/utils/combineBarStackData'; + +const accessors = { + xAccessor: (d: { x: number }) => d.x, + yAccessor: (d: { y: number }) => d.y, +}; + +const series1 = { + dataKey: 'bar1', + data: [ + { x: 10, y: 5 }, + { x: 7, y: -5 }, + ], + ...accessors, +}; + +const series2 = { + dataKey: 'bar2', + data: [ + { x: 10, y: 5 }, + { x: 7, y: 20 }, + ], + ...accessors, +}; + +const seriesChildren = [ + , + , +]; + +describe('combineBarStackData', () => { + it('should be defined', () => { + expect(combineBarStackData).toBeDefined(); + }); + + it('should combine data by x stack value when horizontal=false', () => { + expect(combineBarStackData(seriesChildren)).toEqual([ + { stack: 7, bar1: -5, bar2: 20, positiveSum: 20, negativeSum: -5 }, + { stack: 10, bar1: 5, bar2: 5, positiveSum: 10, negativeSum: 0 }, + ]); + }); + it('should combine data by y stack value when horizontal=true', () => { + expect(combineBarStackData(seriesChildren, true)).toEqual([ + { stack: 5, bar1: 10, bar2: 10, positiveSum: 20, negativeSum: 0 }, + { stack: 20, bar1: undefined, bar2: 7, positiveSum: 7, negativeSum: 0 }, + { stack: -5, bar1: 7, bar2: undefined, positiveSum: 7, negativeSum: 0 }, + ]); + }); +}); diff --git a/packages/visx-xychart/test/utils/findNearestDatum.test.ts b/packages/visx-xychart/test/utils/findNearestDatum.test.ts index f36618d31..c70ed30c8 100644 --- a/packages/visx-xychart/test/utils/findNearestDatum.test.ts +++ b/packages/visx-xychart/test/utils/findNearestDatum.test.ts @@ -4,7 +4,8 @@ import findNearestDatumX from '../../src/utils/findNearestDatumX'; import findNearestDatumY from '../../src/utils/findNearestDatumY'; import findNearestDatumXY from '../../src/utils/findNearestDatumXY'; import findNearestDatumSingleDimension from '../../src/utils/findNearestDatumSingleDimension'; -import { NearestDatumArgs } from '../../lib'; +import findNearestStackDatum from '../../src/utils/findNearestStackDatum'; +import { BarStackDatum, NearestDatumArgs } from '../../src'; type Datum = { xVal: number; yVal: string }; @@ -109,3 +110,28 @@ describe('findNearestDatumSingleDimension', () => { ).toEqual({ xVal: 8, yVal: '8' }); }); }); + +describe('findNearestStackDatum', () => { + it('should be defined', () => { + expect(findNearestStackDatum).toBeDefined(); + }); + + it('should find the nearest datum', () => { + const d1 = { yVal: '🍌' }; + const d2 = { yVal: '🚀' }; + expect( + findNearestStackDatum( + ({ + ...params, + // type is not technically correct, but coerce for test + } as unknown) as NearestDatumArgs< + AxisScale, + AxisScale, + BarStackDatum + >, + [d1, d2], + true, + )!.datum, + ).toEqual(d2); // nearest datum index=1 + }); +});