Commit bfbfac4b authored by Miguel Rincon's avatar Miguel Rincon

Display y-axis range matching data

Instead of anchoring the range of the y axis at 0, the chart now
displays the data in the relevent range.

This moves the deployment data to another series so it does not
interfere with the data axis.
parent cd4a5674
......@@ -18,6 +18,17 @@ import {
import { makeDataSeries } from '~/helpers/monitor_helper';
import { graphDataValidatorForValues } from '../../utils';
/**
* A "virtual" coordinates system for the deployment icons.
* Deployment icons are displayed along the [min, max]
* range at height `pos`.
*/
const deploymentYAxisCoords = {
min: 0,
pos: 3, // 3% height of chart's grid
max: 100,
};
const THROTTLED_DATAZOOM_WAIT = 1000; // miliseconds
const timestampToISODate = timestamp => new Date(timestamp).toISOString();
......@@ -145,10 +156,33 @@ export default {
}, []);
},
chartOptionSeries() {
return (this.option.series || []).concat(this.scatterSeries ? [this.scatterSeries] : []);
return (this.option.series || []).concat(
this.deploymentSeries ? [this.deploymentSeries] : [],
);
},
chartOptions() {
const option = omit(this.option, 'series');
const dataYAxis = {
name: this.yAxisLabel,
nameGap: 50, // same as gitlab-ui's default
nameLocation: 'center', // same as gitlab-ui's default
boundaryGap: [0.1, 0.1],
scale: true,
axisLabel: {
formatter: num => roundOffFloat(num, 3).toString(),
},
};
const deploymentsYAxis = {
show: false,
min: deploymentYAxisCoords.min,
max: deploymentYAxisCoords.max,
axisLabel: {
// formatter fn required to trigger tooltip re-positioning
formatter: () => {},
},
};
return {
series: this.chartOptionSeries,
xAxis: {
......@@ -161,12 +195,7 @@ export default {
snap: true,
},
},
yAxis: {
name: this.yAxisLabel,
axisLabel: {
formatter: num => roundOffFloat(num, 3).toString(),
},
},
yAxis: [dataYAxis, deploymentsYAxis],
dataZoom: [this.dataZoomConfig],
...option,
};
......@@ -228,10 +257,16 @@ export default {
return acc;
}, []);
},
scatterSeries() {
deploymentSeries() {
return {
type: graphTypes.deploymentData,
data: this.recentDeployments.map(deployment => [deployment.createdAt, 0]),
yAxisIndex: 1, // deploymentsYAxis index
data: this.recentDeployments.map(deployment => [
deployment.createdAt,
deploymentYAxisCoords.pos,
]),
symbol: this.svgs.rocket,
symbolSize: symbolSizes.default,
itemStyle: {
......@@ -265,6 +300,7 @@ export default {
formatTooltipText(params) {
this.tooltip.title = dateFormat(params.value, dateFormats.default);
this.tooltip.content = [];
params.seriesData.forEach(dataPoint => {
if (dataPoint.value) {
const [xVal, yVal] = dataPoint.value;
......
---
title: Display the y-axis on the range of data value in the chart
merge_request: 24953
author:
type: added
......@@ -74,6 +74,8 @@ describe('Time series component', () => {
describe('general functions', () => {
let timeSeriesChart;
const findChart = () => timeSeriesChart.find({ ref: 'chart' });
beforeEach(done => {
timeSeriesChart = makeTimeSeriesChart(mockGraphData, 'area-chart');
timeSeriesChart.vm.$nextTick(done);
......@@ -109,8 +111,6 @@ describe('Time series component', () => {
let startValue;
let endValue;
const findChart = () => timeSeriesChart.find({ ref: 'chart' });
beforeEach(done => {
eChartMock = {
handlers: {},
......@@ -285,6 +285,8 @@ describe('Time series component', () => {
});
describe('computed', () => {
const getChartOptions = () => findChart().props('option');
describe('chartData', () => {
let chartData;
const seriesData = () => chartData[0];
......@@ -329,7 +331,7 @@ describe('Time series component', () => {
});
return timeSeriesChart.vm.$nextTick().then(() => {
expect(timeSeriesChart.vm.chartOptions).toEqual(expect.objectContaining(mockOption));
expect(getChartOptions()).toEqual(expect.objectContaining(mockOption));
});
});
......@@ -345,7 +347,7 @@ describe('Time series component', () => {
});
return timeSeriesChart.vm.$nextTick().then(() => {
const optionSeries = timeSeriesChart.vm.chartOptions.series;
const optionSeries = getChartOptions().series;
expect(optionSeries.length).toEqual(2);
expect(optionSeries[0].name).toEqual(mockSeriesName);
......@@ -354,33 +356,58 @@ describe('Time series component', () => {
});
describe('yAxis formatter', () => {
let format;
let dataFormatter;
let deploymentFormatter;
beforeEach(() => {
format = timeSeriesChart.vm.chartOptions.yAxis.axisLabel.formatter;
dataFormatter = getChartOptions().yAxis[0].axisLabel.formatter;
deploymentFormatter = getChartOptions().yAxis[1].axisLabel.formatter;
});
it('rounds to 3 decimal places', () => {
expect(format(0.88888)).toBe('0.889');
expect(dataFormatter(0.88888)).toBe('0.889');
});
it('deployment formatter is set as is required to display a tooltip', () => {
expect(deploymentFormatter).toEqual(expect.any(Function));
});
});
});
describe('scatterSeries', () => {
describe('deploymentSeries', () => {
it('utilizes deployment data', () => {
expect(timeSeriesChart.vm.scatterSeries.data).toEqual([
['2019-07-16T10:14:25.589Z', 0],
['2019-07-16T11:14:25.589Z', 0],
['2019-07-16T12:14:25.589Z', 0],
expect(timeSeriesChart.vm.deploymentSeries.yAxisIndex).toBe(1); // same as deployment y axis
expect(timeSeriesChart.vm.deploymentSeries.data).toEqual([
['2019-07-16T10:14:25.589Z', expect.any(Number)],
['2019-07-16T11:14:25.589Z', expect.any(Number)],
['2019-07-16T12:14:25.589Z', expect.any(Number)],
]);
expect(timeSeriesChart.vm.scatterSeries.symbolSize).toBe(14);
expect(timeSeriesChart.vm.deploymentSeries.symbolSize).toBe(14);
});
});
describe('yAxisLabel', () => {
it('y axis is configured correctly', () => {
const { yAxis } = getChartOptions();
expect(yAxis).toHaveLength(2);
const [dataAxis, deploymentAxis] = yAxis;
expect(dataAxis.boundaryGap).toHaveLength(2);
expect(dataAxis.scale).toBe(true);
expect(deploymentAxis.show).toBe(false);
expect(deploymentAxis.min).toEqual(expect.any(Number));
expect(deploymentAxis.max).toEqual(expect.any(Number));
expect(deploymentAxis.min).toBeLessThan(deploymentAxis.max);
});
it('constructs a label for the chart y-axis', () => {
expect(timeSeriesChart.vm.yAxisLabel).toBe('Memory Used per Pod');
const { yAxis } = getChartOptions();
expect(yAxis[0].name).toBe('Memory Used per Pod');
});
});
});
......@@ -405,7 +432,7 @@ describe('Time series component', () => {
glChartComponents.forEach(dynamicComponent => {
describe(`GitLab UI: ${dynamicComponent.chartType}`, () => {
let timeSeriesAreaChart;
const findChart = () => timeSeriesAreaChart.find(dynamicComponent.component);
const findChartComponent = () => timeSeriesAreaChart.find(dynamicComponent.component);
beforeEach(done => {
timeSeriesAreaChart = makeTimeSeriesChart(mockGraphData, dynamicComponent.chartType);
......@@ -417,12 +444,12 @@ describe('Time series component', () => {
});
it('is a Vue instance', () => {
expect(findChart().exists()).toBe(true);
expect(findChart().isVueInstance()).toBe(true);
expect(findChartComponent().exists()).toBe(true);
expect(findChartComponent().isVueInstance()).toBe(true);
});
it('receives data properties needed for proper chart render', () => {
const props = findChart().props();
const props = findChartComponent().props();
expect(props.data).toBe(timeSeriesAreaChart.vm.chartData);
expect(props.option).toBe(timeSeriesAreaChart.vm.chartOptions);
......@@ -435,9 +462,9 @@ describe('Time series component', () => {
timeSeriesAreaChart.vm.tooltip.title = mockTitle;
timeSeriesAreaChart.vm.$nextTick(() => {
expect(shallowWrapperContainsSlotText(findChart(), 'tooltipTitle', mockTitle)).toBe(
true,
);
expect(
shallowWrapperContainsSlotText(findChartComponent(), 'tooltipTitle', mockTitle),
).toBe(true);
done();
});
});
......@@ -452,9 +479,9 @@ describe('Time series component', () => {
});
it('uses deployment title', () => {
expect(shallowWrapperContainsSlotText(findChart(), 'tooltipTitle', 'Deployed')).toBe(
true,
);
expect(
shallowWrapperContainsSlotText(findChartComponent(), 'tooltipTitle', 'Deployed'),
).toBe(true);
});
it('renders clickable commit sha in tooltip content', done => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment