@gooddata/react-components
Version:
GoodData.UI - A powerful JavaScript library for building analytical applications
586 lines (538 loc) • 20.3 kB
text/typescript
// (C) 2019 GoodData Corporation
import {
adjustTicks,
ALIGNED,
alignToBaseAxis,
customAdjustTickAmount,
getDirection,
getSelectionRange,
getYAxisScore,
MOVE_ZERO_LEFT,
MOVE_ZERO_RIGHT,
preventDataCutOff,
shouldBeHandledByHighcharts,
Y_AXIS_SCORE,
} from "../adjustTickAmount";
import { IHighchartsAxisExtend } from "../../../../../../interfaces/HighchartsExtend";
import { VisualizationTypes } from "../../../../../../constants/visualizationTypes";
describe("adjustTickAmount - general", () => {
const Y_AXIS = {
hasData: () => true,
coll: "yAxis",
options: {
startOnTick: true,
endOnTick: true,
},
};
it("should not adjust tick on no-data axis", () => {
const axis: IHighchartsAxisExtend = {
hasData: () => false,
};
const result = customAdjustTickAmount.call(axis);
expect(result).toBeFalsy();
});
describe("should adjust tick amount without user-input min/max", () => {
const chart: any = {
axes: [],
userOptions: {
yAxis: [{ isUserMinMax: false }, { isUserMinMax: false }],
},
};
// min/max are calculated in 'getZeroAlignConfiguration'
const getLeftAxis = () => ({
...Y_AXIS,
chart,
tickAmount: 8,
tickInterval: 300,
tickPositions: [-1200, -900, -600, -300, 0, 300, 600, 900, 1200], // 9 ticks
opposite: false,
dataMin: 137,
dataMax: 986,
min: -986,
max: 986,
});
const getRightAxis = () => ({
...Y_AXIS,
chart,
tickAmount: 8,
tickInterval: 3000,
tickPositions: [-12000, -9000, -6000, -3000, 0, 3000, 6000, 9000, 12000], // 9 ticks
opposite: true,
dataMin: -8895,
dataMax: -1239,
min: -8895,
max: 8895,
});
it.each([
["left", getLeftAxis(), [-900, -600, -300, 0, 300, 600, 900, 1200]], // reduced to 8 ticks
["right", getRightAxis(), [-12000, -9000, -6000, -3000, 0, 3000, 6000, 9000]], // reduced to 8 ticks
])(
"should return %s tick positions aligned zero with opposite",
(_side: string, axis: IHighchartsAxisExtend, expectation: number) => {
customAdjustTickAmount.call(axis);
expect(axis.tickPositions).toEqual(expectation);
},
);
it.each([["left", getLeftAxis(), -900, 1200], ["right", getRightAxis(), -12000, 9000]])(
"should min/max be updated on %s axis",
(_side: string, axis: IHighchartsAxisExtend, min: number, max: number) => {
customAdjustTickAmount.call(axis);
expect(axis.min).toBe(min);
expect(axis.max).toBe(max);
},
);
});
describe("should adjust tick amount with user-input min/max", () => {
const chart: any = {
axes: [],
userOptions: {
yAxis: [{ isUserMinMax: true }, { isUserMinMax: true }],
},
};
const leftAxis = {
...Y_AXIS,
chart,
tickAmount: 8,
tickInterval: 15,
tickPositions: [-90, -75, -60, -45, -30, -15, 0], // 7 ticks
opposite: false,
dataMin: -656,
dataMax: 986,
min: -100,
max: 0,
};
const rightAxis = {
...Y_AXIS,
chart,
tickAmount: 8,
tickInterval: 800,
tickPositions: [-4800, -4000, -3200, -2400, -1600, -800, 0], // 7 ticks
opposite: true,
dataMin: -8895,
dataMax: 7661,
min: 0,
max: -4500,
};
beforeAll(() => {
chart.axes = [leftAxis, rightAxis];
customAdjustTickAmount.call(leftAxis);
customAdjustTickAmount.call(rightAxis);
});
it.each([
["left", leftAxis, [-105, -90, -75, -60, -45, -30, -15, 0]], // adjusted to 8 ticks
["right", rightAxis, [-5600, -4800, -4000, -3200, -2400, -1600, -800, 0]], // adjusted to 8 ticks
])(
"should return %s tick positions aligned zero with opposite",
(_side: string, axis: IHighchartsAxisExtend, expectation: number) => {
expect(axis.tickPositions).toEqual(expectation);
},
);
it.each([["left", leftAxis, -105, 0], ["right", rightAxis, -5600, 0]])(
"should min/max be updated on %s axis",
(_side: string, axis: IHighchartsAxisExtend, min: number, max: number) => {
expect(axis.min).toBe(min);
expect(axis.max).toBe(max);
},
);
it("should two axes be zero aligned", () => {
expect(leftAxis.tickPositions.indexOf(0)).toBe(rightAxis.tickPositions.indexOf(0));
});
});
});
describe("adjustTickAmount - detail", () => {
describe("adjustTicks", () => {
it("should not adjust any tick", () => {
const axis: IHighchartsAxisExtend = {
tickAmount: 1,
tickPositions: [100],
};
expect(adjustTicks(axis)).toBeFalsy();
});
it("should add tick with negative data", () => {
const axis: IHighchartsAxisExtend = {
tickInterval: 10,
tickAmount: 3,
tickPositions: [-20, -10],
dataMin: -20,
dataMax: -10,
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([-30, -20, -10]);
});
it("should add tick with negative data and positive min/max", () => {
// tick should be added with direction from data set
const axis: IHighchartsAxisExtend = {
tickInterval: 5,
tickAmount: 3,
tickPositions: [10, 15],
min: 5,
max: 15,
dataMin: -20,
dataMax: -10,
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([5, 10, 15]);
});
it("should add tick with positive data", () => {
const axis: IHighchartsAxisExtend = {
tickInterval: 10,
tickAmount: 3,
tickPositions: [10, 20],
dataMin: 10,
dataMax: 20,
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([10, 20, 30]);
});
it("should add tick to first with negative-to-positive data and negative min/max", () => {
// tick should be added with direction from data set
const axis: IHighchartsAxisExtend = {
tickInterval: 5,
tickAmount: 3,
tickPositions: [-15, -10],
min: -20,
max: -10,
dataMin: -10,
dataMax: 20,
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([-20, -15, -10]);
});
it("should add tick to end with negative-to-positive data and positive min/max", () => {
// tick should be added with direction from data set
const axis: IHighchartsAxisExtend = {
tickInterval: 5,
tickAmount: 3,
tickPositions: [-15, -10],
min: 20,
max: 10,
dataMin: -10,
dataMax: 20,
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([-15, -10, -5]);
});
it("should add tick to last where min starts from 0", () => {
const axis: IHighchartsAxisExtend = {
tickInterval: 50,
tickAmount: 4,
tickPositions: [0, 50, 100],
dataMin: -50,
dataMax: 200,
min: 0,
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([0, 50, 100, 150]);
});
it("should add tick to first by default", () => {
const axis: IHighchartsAxisExtend = {
tickInterval: 100,
tickAmount: 3,
tickPositions: [-50, 50],
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([-150, -50, 50]);
});
it("should reduce tick at the start", () => {
const axis: IHighchartsAxisExtend = {
tickInterval: 100,
tickAmount: 3,
tickPositions: [0, 100, 200, 300, 400],
dataMin: 50,
dataMax: 350,
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([200, 300, 400]);
});
it("should reduce tick at the end", () => {
const axis: IHighchartsAxisExtend = {
tickInterval: 100,
tickAmount: 3,
tickPositions: [-400, -300, -200, -100, 0],
dataMin: -350,
dataMax: -50,
};
adjustTicks(axis);
expect(axis.tickPositions).toEqual([-400, -300, -200]);
});
});
describe("alignToBaseAxis", () => {
it("should not handle base axis", () => {
const leftAxis: IHighchartsAxisExtend = {
opposite: false,
tickPositions: [-2, -1, 0, 1, 2, 3],
chart: {},
};
const rightAxis: IHighchartsAxisExtend = {
opposite: true,
tickPositions: [-200, -100, 0, 100, 200, 300],
chart: {},
};
expect(alignToBaseAxis(leftAxis, rightAxis)).toBeFalsy();
});
it("should do nothing with aligned axes", () => {
const leftAxis: IHighchartsAxisExtend = {
opposite: false,
tickPositions: [-2, -1, 0, 1, 2, 3],
chart: {},
};
const rightAxis: IHighchartsAxisExtend = {
opposite: true,
tickPositions: [-200, -100, 0, 100, 200, 300],
chart: {},
};
alignToBaseAxis(leftAxis, rightAxis);
expect(rightAxis.tickPositions).toEqual([-200, -100, 0, 100, 200, 300]);
});
it("should zero move right on right axis", () => {
const leftAxis: IHighchartsAxisExtend = {
coll: "yAxis",
opposite: false,
tickPositions: [-2, -1, 0, 1, 2, 3],
chart: {},
};
const rightAxis: IHighchartsAxisExtend = {
coll: "yAxis",
opposite: true,
tickInterval: 100,
tickPositions: [0, 100, 200, 300, 400, 500],
chart: {},
};
alignToBaseAxis(rightAxis, leftAxis);
expect(rightAxis.tickPositions).toEqual([-200, -100, 0, 100, 200, 300]);
expect(leftAxis.tickPositions).toEqual([-2, -1, 0, 1, 2, 3]);
});
it("should zero move left on right axis", () => {
const leftAxis: IHighchartsAxisExtend = {
coll: "yAxis",
opposite: false,
tickPositions: [-2, -1, 0, 1, 2, 3],
chart: {},
};
const rightAxis: IHighchartsAxisExtend = {
coll: "yAxis",
opposite: true,
tickInterval: 100,
tickPositions: [-400, -300, -200, -100, 0, 100],
chart: {},
};
alignToBaseAxis(rightAxis, leftAxis);
expect(rightAxis.tickPositions).toEqual([-200, -100, 0, 100, 200, 300]);
expect(leftAxis.tickPositions).toEqual([-2, -1, 0, 1, 2, 3]);
});
});
describe("preventDataCutOff", () => {
it("should not run with chart having user-input min/max", () => {
const axis = {
opposite: true,
chart: {
userOptions: {
yAxis: [{ isUserMinMax: true }, { isUserMinMax: true }],
},
},
};
expect(preventDataCutOff(axis)).toBeFalsy();
});
it("should not run with non-cut-off chart", () => {
const axis = {
opposite: true,
chart: {
userOptions: {
yAxis: [{ isUserMinMax: false }, { isUserMinMax: false }],
},
},
min: 0,
max: 100,
dataMin: 10,
dataMax: 90,
};
expect(preventDataCutOff(axis)).toBeFalsy();
});
it("should double tick interval and tick positions to zoom out axis", () => {
const axis = {
opposite: true,
options: {
startOnTick: true,
endOnTick: true,
},
chart: {
userOptions: {
yAxis: [{ isUserMinMax: false }, { isUserMinMax: false }],
},
},
min: 20,
max: 80,
dataMin: 10,
dataMax: 90,
tickInterval: 10,
tickPositions: [0, 10, 20, 30, 40],
};
preventDataCutOff(axis);
expect(axis.min).toBe(0);
expect(axis.max).toBe(80);
expect(axis.tickInterval).toBe(20);
expect(axis.tickPositions).toEqual([0, 20, 40, 60, 80]);
});
});
describe("shouldBeHandledByHighcharts", () => {
it("should let Highcharts handle X axis", () => {
const xAxis = {
coll: "xAxis",
};
expect(shouldBeHandledByHighcharts(xAxis)).toBeTruthy();
});
it("should let Highcharts handle single Y", () => {
const yAxis = {
coll: "yAxis",
chart: {
axes: [
{
coll: "yAxis",
},
],
},
};
expect(shouldBeHandledByHighcharts(yAxis)).toBeTruthy();
});
it("should let Highcharts handle line chart", () => {
const yAxis: Partial<IHighchartsAxisExtend> = {
coll: "yAxis",
series: [],
chart: {
axes: [
{
coll: "yAxis",
},
{
coll: "yAxis",
},
],
userOptions: {
chart: {
type: VisualizationTypes.LINE,
},
},
},
};
expect(shouldBeHandledByHighcharts(yAxis)).toBeTruthy();
});
it("should let Highcharts handle combo line-line chart", () => {
const yAxis = {
coll: "yAxis",
series: [
{
type: VisualizationTypes.LINE,
},
],
chart: {
axes: [
{
coll: "yAxis",
},
{
coll: "yAxis",
},
],
},
};
// use cast here because mock object does not have enough required props
expect(shouldBeHandledByHighcharts(yAxis as Partial<IHighchartsAxisExtend>)).toBeTruthy();
});
it("should let Highcharts handle if any Y axis is disable", () => {
const yAxis: Partial<IHighchartsAxisExtend> = {
coll: "yAxis",
series: [],
chart: {
axes: [
{
coll: "yAxis",
visible: false,
},
{
coll: "yAxis",
},
],
},
};
expect(shouldBeHandledByHighcharts(yAxis)).toBeTruthy();
});
});
describe("getYAxisScore", () => {
it.each([
[Y_AXIS_SCORE.NO_DATA, 0, 0],
[Y_AXIS_SCORE.ONLY_NEGATIVE_OR_POSITIVE_DATA, 5, 10],
[Y_AXIS_SCORE.ONLY_NEGATIVE_OR_POSITIVE_DATA, -10, -5],
[Y_AXIS_SCORE.NEGATIVE_AND_POSITIVE_DATA, -10, 10],
])("should score equal to %s", (score: number, dataMin: number, dataMax: number) => {
const axis: IHighchartsAxisExtend = {
dataMin,
dataMax,
};
expect(getYAxisScore(axis)).toBe(score);
});
});
describe("getDirection", () => {
it.each([["left", null, {}], ["right", {}, null]])(
"should return ALIGN when %s axis is null",
(_axisSide: string, primaryAxis: IHighchartsAxisExtend, secondaryAxis: IHighchartsAxisExtend) => {
expect(getDirection(primaryAxis, secondaryAxis)).toBe(ALIGNED);
},
);
it("should return ALIGN when both axes are empty", () => {
const axis: IHighchartsAxisExtend = { tickPositions: [] };
expect(getDirection(axis, axis)).toBe(ALIGNED);
});
it("should return ALIGN when both axes are aligned", () => {
const axis: IHighchartsAxisExtend = { tickPositions: [-2, -1, 0, 1, 2] };
expect(getDirection(axis, axis)).toBe(ALIGNED);
});
it("should return MOVE_ZERO_RIGHT", () => {
const primaryAxis: IHighchartsAxisExtend = { tickPositions: [-2, -1, 0, 1, 2] };
const secondaryAxis: IHighchartsAxisExtend = { tickPositions: [-1, 0, 1, 2, 3] };
expect(getDirection(primaryAxis, secondaryAxis)).toBe(MOVE_ZERO_RIGHT);
});
it("should return MOVE_ZERO_LEFT", () => {
const primaryAxis: IHighchartsAxisExtend = { tickPositions: [-2, -1, 0, 1, 2] };
const secondaryAxis: IHighchartsAxisExtend = { tickPositions: [-3, -2, -1, 0, 1] };
expect(getDirection(primaryAxis, secondaryAxis)).toBe(MOVE_ZERO_LEFT);
});
});
describe("getSelectionRange", () => {
it("should return range with data min >= 0", () => {
const axis: IHighchartsAxisExtend = {
dataMin: 10,
dataMax: 50,
tickAmount: 4,
tickPositions: [0, 20, 40, 60, 80],
};
expect(getSelectionRange(axis)).toEqual([1, 5]);
});
it("should return range with data max <= 0", () => {
const axis: IHighchartsAxisExtend = {
dataMin: -50,
dataMax: -10,
tickAmount: 4,
tickPositions: [-80, -60, -40, -20, 0],
};
expect(getSelectionRange(axis)).toEqual([0, 4]);
});
it("should return range trimmed from negative", () => {
const axis: IHighchartsAxisExtend = {
dataMin: -70,
dataMax: 30,
tickAmount: 6,
tickPositions: [-80, -60, -40, -20, 0, 20, 40],
};
expect(getSelectionRange(axis)).toEqual([1, 7]);
});
it("should return range trimmed from positive", () => {
const axis: IHighchartsAxisExtend = {
dataMin: -30,
dataMax: 70,
tickAmount: 6,
tickPositions: [-40, -20, 0, 20, 40, 60, 80],
};
expect(getSelectionRange(axis)).toEqual([0, 6]);
});
});
});