Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(xychart): add BarStack tests #866

Merged
merged 4 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions packages/visx-xychart/src/hooks/useDimensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Dimensions>(INITIAL_DIMENSIONS);
export default function useDimensions(
initialDimensions?: Partial<Dimensions>,
): [Dimensions, (dims: Dimensions) => void] {
const [dimensions, privateSetDimensions] = useState<Dimensions>({
...INITIAL_DIMENSIONS,
...initialDimensions,
});

// expose a setter with better memoization logic
const publicSetDimensions = useCallback(
Expand Down
6 changes: 4 additions & 2 deletions packages/visx-xychart/src/providers/DataProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ 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. */
export type DataProviderProps<
XScaleConfig extends ScaleConfig<AxisScaleOutput, any, any>,
YScaleConfig extends ScaleConfig<AxisScaleOutput, any, any>
> = {
initialDimensions?: Partial<Dimensions>;
theme?: XYChartTheme;
xScale: XScaleConfig;
yScale: YScaleConfig;
Expand All @@ -26,6 +27,7 @@ export default function DataProvider<
YScaleConfig extends ScaleConfig<AxisScaleOutput>,
Datum extends object
>({
initialDimensions,
theme: propsTheme,
xScale: xScaleConfig,
yScale: yScaleConfig,
Expand All @@ -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);

Expand Down
83 changes: 83 additions & 0 deletions packages/visx-xychart/test/components/BarStack.test.tsx
Original file line number Diff line number Diff line change
@@ -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('<BarStack />', () => {
it('should be defined', () => {
expect(BarSeries).toBeDefined();
});

it('should render rects', () => {
const wrapper = mount(
<DataProvider {...providerProps}>
<svg>
<BarStack horizontal>
<BarSeries dataKey={series1.key} {...series1} />
<BarSeries dataKey={series2.key} {...series2} />
</BarStack>
</svg>
</DataProvider>,
);
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(
<DataProvider {...providerProps}>
<svg>
<BarStack>
<BarSeries dataKey={series1.key} {...series1} />
<BarSeries
dataKey={series2.key}
{...series2}
data={[
{ x: 10, y: 5 },
{ x: 7, y: -20 },
]}
/>
</BarStack>
</svg>
<Assertion />
</DataProvider>,
);
});
});
16 changes: 9 additions & 7 deletions packages/visx-xychart/test/mocks/getDataContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ const height = 10;
const margin = { top: 0, right: 0, bottom: 0, left: 0 };
const noOp = () => {};

const getDataContext = (
entries?: Parameters<typeof DataRegistry.prototype.registerData>[0],
): DataContextType<any, any, any> => {
function getDataContext(entries?: Parameters<typeof DataRegistry.prototype.registerData>[0]) {
const dataRegistry = new DataRegistry();
if (entries) dataRegistry.registerData(entries);

const mockContext = {
const mockContext: DataContextType<any, any, any> = {
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'] }),
Expand All @@ -31,6 +33,6 @@ const getDataContext = (
};

return mockContext;
};
}

export default getDataContext;
51 changes: 51 additions & 0 deletions packages/visx-xychart/test/utils/combineBarStackData.test.tsx
Original file line number Diff line number Diff line change
@@ -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 = [
<BarSeries key={series1.dataKey} {...series1} />,
<BarSeries key={series2.dataKey} {...series2} />,
];

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 },
]);
});
});
28 changes: 27 additions & 1 deletion packages/visx-xychart/test/utils/findNearestDatum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

Expand Down Expand Up @@ -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<AxisScale, AxisScale>
>,
[d1, d2],
true,
)!.datum,
).toEqual(d2); // nearest datum index=1
});
});