Skip to content

Commit

Permalink
[xy-chart][tests] finally mock out findClosestDatum and fix ordinal s…
Browse files Browse the repository at this point in the history
…cale bug
  • Loading branch information
williaster committed Dec 6, 2017
1 parent a1d7310 commit 6828e96
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/demo/examples/01-xy-chart/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ export default {
ariaLabel="Required label"
xScale={{ type: 'band' }}
yScale={{ type: 'linear' }}
eventTrigger="container"
>
<LinearGradient
id="aqua_lightaqua_gradient"
Expand Down
1 change: 0 additions & 1 deletion packages/xy-chart/src/chart/XYChart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ class XYChart extends React.PureComponent {
xScale,
yScale,
});

if (closestDatum || Object.keys(series).length > 0) {
event.persist();
const args = { event, datum: closestDatum, series };
Expand Down
7 changes: 2 additions & 5 deletions packages/xy-chart/src/utils/findClosestDatum.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,9 @@ export default function findClosestDatum({ data, getX, xScale, event }) {
// Ordinal scales don't have an invert function so we do it maually
const xDomain = xScale.domain();
const scaledXValues = xDomain.map(val => xScale(val));
const index = Math.min(
scaledXValues.length - 1,
d3BisectLeft(scaledXValues, mouseX),
);
const index = d3BisectLeft(scaledXValues, mouseX);
const d0 = data[index - 1];
const d1 = data[index] || {};
const d1 = data[index];
d = d0 || d1;
} else {
const dataX = xScale.invert(mouseX);
Expand Down
6 changes: 2 additions & 4 deletions packages/xy-chart/src/utils/findClosestDatums.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,15 @@ export default function findClosestDatums({
Children.forEach(children, (Child, childIndex) => {
const { disableMouseEvents, data, seriesKey } = Child.props;
if (isSeries(componentName(Child)) && !disableMouseEvents) {
const adjustedGetX = xScale.invert ? getX : d => getX(d) + ((xScale.barWidth || 0) / 2);

// @TODO data should be sorted, come up with a way to enforce+cache instead of relying on user
const datum = findClosestDatum({
data,
getX: adjustedGetX,
getX,
xScale,
event,
});

const deltaX = Math.abs(xScale(adjustedGetX(datum || {})) - mouseX);
const deltaX = Math.abs(xScale(getX(datum || {})) - mouseX);

if (datum && deltaX <= maxXDistancePx) {
const key = seriesKey || childIndex; // fall back to child index
Expand Down
81 changes: 81 additions & 0 deletions packages/xy-chart/test/utils/findClosestDatum.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import scaleLinear from '@vx/scale/build/scales/linear';
import scaleBand from '@vx/scale/build/scales/band';
import findClosestDatum from '../../src/utils/findClosestDatum';

describe('findClosestDatum', () => {
beforeAll(() => {
// mock prototype attributes for vx's localPoint
Element.prototype.getBoundingClientRect = jest.fn(() => ({
width: 10,
height: 10,
top: 0,
left: 0,
bottom: 0,
right: 0,
}));

Element.prototype.clientLeft = 0;
Element.prototype.clientTop = 0;
});

const node = document.createElement('g');
const event = {
// missing clientX
clientY: 0,
target: {
ownerSVGElement: {
firstChild: node,
},
},
};

test('it should be defined', () => {
expect(findClosestDatum).toBeDefined();
});

test('it should return the closest datum', () => {
const props = {
data: [{ x: 0 }, { x: 5 }, { x: 10 }],
getX: d => d.x,
xScale: scaleLinear({ domain: [0, 10], range: [0, 10] }),
};

expect(findClosestDatum({
...props,
event: { ...event, clientX: 1 },
})).toBe(props.data[0]);

expect(findClosestDatum({
...props,
event: { ...event, clientX: 6 },
})).toBe(props.data[1]);

expect(findClosestDatum({
...props,
event: { ...event, clientX: 9 },
})).toBe(props.data[2]);
});

test('it should work for ordinal scales', () => {
const props = {
data: [{ x: 'a' }, { x: 'b' }, { x: 'c' }],
getX: d => d.x,
xScale: scaleBand({ domain: ['a', 'b', 'c'], range: [0, 10] }),
};

expect(findClosestDatum({
...props,
event: { ...event, clientX: 0 },
})).toBe(props.data[0]);

expect(findClosestDatum({
...props,
event: { ...event, clientX: 5 },
})).toBe(props.data[1]);

expect(findClosestDatum({
...props,
event: { ...event, clientX: 10 },
})).toBe(props.data[2]);
});
});

0 comments on commit 6828e96

Please sign in to comment.