Commit 5f964fcf authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '202696-only-display-y-axis-of-data-value-ranges-for-charts' into 'master'

Display the y-axis on the range of data value in the chart

Closes #202696

See merge request gitlab-org/gitlab!24953
parents d9a86c0a bfbfac4b
...@@ -18,6 +18,17 @@ import { ...@@ -18,6 +18,17 @@ import {
import { makeDataSeries } from '~/helpers/monitor_helper'; import { makeDataSeries } from '~/helpers/monitor_helper';
import { graphDataValidatorForValues } from '../../utils'; 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 THROTTLED_DATAZOOM_WAIT = 1000; // miliseconds
const timestampToISODate = timestamp => new Date(timestamp).toISOString(); const timestampToISODate = timestamp => new Date(timestamp).toISOString();
...@@ -145,10 +156,33 @@ export default { ...@@ -145,10 +156,33 @@ export default {
}, []); }, []);
}, },
chartOptionSeries() { chartOptionSeries() {
return (this.option.series || []).concat(this.scatterSeries ? [this.scatterSeries] : []); return (this.option.series || []).concat(
this.deploymentSeries ? [this.deploymentSeries] : [],
);
}, },
chartOptions() { chartOptions() {
const option = omit(this.option, 'series'); 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 { return {
series: this.chartOptionSeries, series: this.chartOptionSeries,
xAxis: { xAxis: {
...@@ -161,12 +195,7 @@ export default { ...@@ -161,12 +195,7 @@ export default {
snap: true, snap: true,
}, },
}, },
yAxis: { yAxis: [dataYAxis, deploymentsYAxis],
name: this.yAxisLabel,
axisLabel: {
formatter: num => roundOffFloat(num, 3).toString(),
},
},
dataZoom: [this.dataZoomConfig], dataZoom: [this.dataZoomConfig],
...option, ...option,
}; };
...@@ -228,10 +257,16 @@ export default { ...@@ -228,10 +257,16 @@ export default {
return acc; return acc;
}, []); }, []);
}, },
scatterSeries() { deploymentSeries() {
return { return {
type: graphTypes.deploymentData, 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, symbol: this.svgs.rocket,
symbolSize: symbolSizes.default, symbolSize: symbolSizes.default,
itemStyle: { itemStyle: {
...@@ -265,6 +300,7 @@ export default { ...@@ -265,6 +300,7 @@ export default {
formatTooltipText(params) { formatTooltipText(params) {
this.tooltip.title = dateFormat(params.value, dateFormats.default); this.tooltip.title = dateFormat(params.value, dateFormats.default);
this.tooltip.content = []; this.tooltip.content = [];
params.seriesData.forEach(dataPoint => { params.seriesData.forEach(dataPoint => {
if (dataPoint.value) { if (dataPoint.value) {
const [xVal, yVal] = 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', () => { ...@@ -74,6 +74,8 @@ describe('Time series component', () => {
describe('general functions', () => { describe('general functions', () => {
let timeSeriesChart; let timeSeriesChart;
const findChart = () => timeSeriesChart.find({ ref: 'chart' });
beforeEach(done => { beforeEach(done => {
timeSeriesChart = makeTimeSeriesChart(mockGraphData, 'area-chart'); timeSeriesChart = makeTimeSeriesChart(mockGraphData, 'area-chart');
timeSeriesChart.vm.$nextTick(done); timeSeriesChart.vm.$nextTick(done);
...@@ -109,8 +111,6 @@ describe('Time series component', () => { ...@@ -109,8 +111,6 @@ describe('Time series component', () => {
let startValue; let startValue;
let endValue; let endValue;
const findChart = () => timeSeriesChart.find({ ref: 'chart' });
beforeEach(done => { beforeEach(done => {
eChartMock = { eChartMock = {
handlers: {}, handlers: {},
...@@ -285,6 +285,8 @@ describe('Time series component', () => { ...@@ -285,6 +285,8 @@ describe('Time series component', () => {
}); });
describe('computed', () => { describe('computed', () => {
const getChartOptions = () => findChart().props('option');
describe('chartData', () => { describe('chartData', () => {
let chartData; let chartData;
const seriesData = () => chartData[0]; const seriesData = () => chartData[0];
...@@ -329,7 +331,7 @@ describe('Time series component', () => { ...@@ -329,7 +331,7 @@ describe('Time series component', () => {
}); });
return timeSeriesChart.vm.$nextTick().then(() => { 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', () => { ...@@ -345,7 +347,7 @@ describe('Time series component', () => {
}); });
return timeSeriesChart.vm.$nextTick().then(() => { return timeSeriesChart.vm.$nextTick().then(() => {
const optionSeries = timeSeriesChart.vm.chartOptions.series; const optionSeries = getChartOptions().series;
expect(optionSeries.length).toEqual(2); expect(optionSeries.length).toEqual(2);
expect(optionSeries[0].name).toEqual(mockSeriesName); expect(optionSeries[0].name).toEqual(mockSeriesName);
...@@ -354,33 +356,58 @@ describe('Time series component', () => { ...@@ -354,33 +356,58 @@ describe('Time series component', () => {
}); });
describe('yAxis formatter', () => { describe('yAxis formatter', () => {
let format; let dataFormatter;
let deploymentFormatter;
beforeEach(() => { 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', () => { 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', () => { it('utilizes deployment data', () => {
expect(timeSeriesChart.vm.scatterSeries.data).toEqual([ expect(timeSeriesChart.vm.deploymentSeries.yAxisIndex).toBe(1); // same as deployment y axis
['2019-07-16T10:14:25.589Z', 0], expect(timeSeriesChart.vm.deploymentSeries.data).toEqual([
['2019-07-16T11:14:25.589Z', 0], ['2019-07-16T10:14:25.589Z', expect.any(Number)],
['2019-07-16T12:14:25.589Z', 0], ['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', () => { 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', () => { 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', () => { ...@@ -405,7 +432,7 @@ describe('Time series component', () => {
glChartComponents.forEach(dynamicComponent => { glChartComponents.forEach(dynamicComponent => {
describe(`GitLab UI: ${dynamicComponent.chartType}`, () => { describe(`GitLab UI: ${dynamicComponent.chartType}`, () => {
let timeSeriesAreaChart; let timeSeriesAreaChart;
const findChart = () => timeSeriesAreaChart.find(dynamicComponent.component); const findChartComponent = () => timeSeriesAreaChart.find(dynamicComponent.component);
beforeEach(done => { beforeEach(done => {
timeSeriesAreaChart = makeTimeSeriesChart(mockGraphData, dynamicComponent.chartType); timeSeriesAreaChart = makeTimeSeriesChart(mockGraphData, dynamicComponent.chartType);
...@@ -417,12 +444,12 @@ describe('Time series component', () => { ...@@ -417,12 +444,12 @@ describe('Time series component', () => {
}); });
it('is a Vue instance', () => { it('is a Vue instance', () => {
expect(findChart().exists()).toBe(true); expect(findChartComponent().exists()).toBe(true);
expect(findChart().isVueInstance()).toBe(true); expect(findChartComponent().isVueInstance()).toBe(true);
}); });
it('receives data properties needed for proper chart render', () => { 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.data).toBe(timeSeriesAreaChart.vm.chartData);
expect(props.option).toBe(timeSeriesAreaChart.vm.chartOptions); expect(props.option).toBe(timeSeriesAreaChart.vm.chartOptions);
...@@ -435,9 +462,9 @@ describe('Time series component', () => { ...@@ -435,9 +462,9 @@ describe('Time series component', () => {
timeSeriesAreaChart.vm.tooltip.title = mockTitle; timeSeriesAreaChart.vm.tooltip.title = mockTitle;
timeSeriesAreaChart.vm.$nextTick(() => { timeSeriesAreaChart.vm.$nextTick(() => {
expect(shallowWrapperContainsSlotText(findChart(), 'tooltipTitle', mockTitle)).toBe( expect(
true, shallowWrapperContainsSlotText(findChartComponent(), 'tooltipTitle', mockTitle),
); ).toBe(true);
done(); done();
}); });
}); });
...@@ -452,9 +479,9 @@ describe('Time series component', () => { ...@@ -452,9 +479,9 @@ describe('Time series component', () => {
}); });
it('uses deployment title', () => { it('uses deployment title', () => {
expect(shallowWrapperContainsSlotText(findChart(), 'tooltipTitle', 'Deployed')).toBe( expect(
true, shallowWrapperContainsSlotText(findChartComponent(), 'tooltipTitle', 'Deployed'),
); ).toBe(true);
}); });
it('renders clickable commit sha in tooltip content', done => { 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