Commit e0d8efbb authored by Andrei Stoicescu's avatar Andrei Stoicescu Committed by Mark Florian

Add tabular legend support to metrics dashboards

  - apply tabular layout to legends in area charts,
line charts and stacked column charts
parent 6caa2277
......@@ -2,7 +2,8 @@
import { GlResizeObserverDirective } from '@gitlab/ui';
import { GlStackedColumnChart } from '@gitlab/ui/dist/charts';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import { chartHeight } from '../../constants';
import { chartHeight, legendLayoutTypes } from '../../constants';
import { s__ } from '~/locale';
import { graphDataValidatorForValues } from '../../utils';
import { getTimeAxisOptions, axisTypes } from './options';
import { timezones } from '../../format_date';
......@@ -25,6 +26,31 @@ export default {
required: false,
default: timezones.LOCAL,
},
legendLayout: {
type: String,
required: false,
default: legendLayoutTypes.table,
},
legendAverageText: {
type: String,
required: false,
default: s__('Metrics|Avg'),
},
legendCurrentText: {
type: String,
required: false,
default: s__('Metrics|Current'),
},
legendMaxText: {
type: String,
required: false,
default: s__('Metrics|Max'),
},
legendMinText: {
type: String,
required: false,
default: s__('Metrics|Min'),
},
},
data() {
return {
......@@ -119,6 +145,11 @@ export default {
:width="width"
:height="height"
:series-names="seriesNames"
:legend-layout="legendLayout"
:legend-average-text="legendAverageText"
:legend-current-text="legendCurrentText"
:legend-max-text="legendMaxText"
:legend-min-text="legendMinText"
/>
</div>
</template>
......@@ -5,7 +5,7 @@ import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/ch
import { s__ } from '~/locale';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import Icon from '~/vue_shared/components/icon.vue';
import { panelTypes, chartHeight, lineTypes, lineWidths } from '../../constants';
import { panelTypes, chartHeight, lineTypes, lineWidths, legendLayoutTypes } from '../../constants';
import { getYAxisOptions, getTimeAxisOptions, getChartGrid, getTooltipFormatter } from './options';
import { annotationsYAxis, generateAnnotationsSeries } from './annotations';
import { makeDataSeries } from '~/helpers/monitor_helper';
......@@ -75,16 +75,31 @@ export default {
required: false,
default: () => [],
},
legendLayout: {
type: String,
required: false,
default: legendLayoutTypes.table,
},
legendAverageText: {
type: String,
required: false,
default: s__('Metrics|Avg'),
},
legendCurrentText: {
type: String,
required: false,
default: s__('Metrics|Current'),
},
legendMaxText: {
type: String,
required: false,
default: s__('Metrics|Max'),
},
legendMinText: {
type: String,
required: false,
default: s__('Metrics|Min'),
},
groupId: {
type: String,
required: false,
......@@ -368,8 +383,11 @@ export default {
:thresholds="thresholds"
:width="width"
:height="height"
:average-text="legendAverageText"
:max-text="legendMaxText"
:legend-layout="legendLayout"
:legend-average-text="legendAverageText"
:legend-current-text="legendCurrentText"
:legend-max-text="legendMaxText"
:legend-min-text="legendMinText"
@created="onChartCreated"
@updated="onChartUpdated"
>
......
......@@ -135,6 +135,19 @@ export const linkTypes = {
GRAFANA: 'grafana',
};
/**
* These are the supported values for the GitLab-UI
* chart legend layout.
*
* Currently defined in
* https://gitlab.com/gitlab-org/gitlab-ui/-/blob/master/src/utils/charts/constants.js
*
*/
export const legendLayoutTypes = {
inline: 'inline',
table: 'table',
};
/**
* These Vuex store properties are allowed to be
* replaced dynamically after component has been created
......
---
title: Change legends in monitor dashboards to tabular layout
merge_request: 30131
author:
type: changed
......@@ -78,7 +78,7 @@ RSpec.describe 'Cluster Health board', :js, :kubeclient, :use_clean_rails_memory
expect(page).to have_css('.prometheus-graph')
expect(page).to have_css('.prometheus-graph-title')
expect(page).to have_css('[_echarts_instance_]')
expect(page).to have_content('Avg:')
expect(page).to have_content('Avg')
end
end
......
......@@ -14081,6 +14081,9 @@ msgstr ""
msgid "Metrics|Create metric"
msgstr ""
msgid "Metrics|Current"
msgstr ""
msgid "Metrics|Delete metric"
msgstr ""
......@@ -14131,6 +14134,9 @@ msgstr ""
msgid "Metrics|Max"
msgstr ""
msgid "Metrics|Min"
msgstr ""
msgid "Metrics|Must be a valid PromQL query."
msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import { shallowMount, mount } from '@vue/test-utils';
import timezoneMock from 'timezone-mock';
import { cloneDeep } from 'lodash';
import { GlStackedColumnChart } from '@gitlab/ui/dist/charts';
import { GlStackedColumnChart, GlChartLegend } from '@gitlab/ui/dist/charts';
import StackedColumnChart from '~/monitoring/components/charts/stacked_column.vue';
import { stackedColumnMockedData } from '../../mock_data';
......@@ -11,16 +11,25 @@ jest.mock('~/lib/utils/icon_utils', () => ({
describe('Stacked column chart component', () => {
let wrapper;
const findChart = () => wrapper.find(GlStackedColumnChart);
const findLegend = () => wrapper.find(GlChartLegend);
const createWrapper = (props = {}) => {
wrapper = shallowMount(StackedColumnChart, {
const createWrapper = (props = {}, mountingMethod = shallowMount) =>
mountingMethod(StackedColumnChart, {
propsData: {
graphData: stackedColumnMockedData,
...props,
},
stubs: {
GlPopover: true,
},
attachToDocument: true,
});
};
beforeEach(() => {
wrapper = createWrapper({}, mount);
});
describe('when graphData is present', () => {
beforeEach(() => {
......@@ -130,4 +139,54 @@ describe('Stacked column chart component', () => {
expect(findChart().exists()).toBe(true);
});
});
describe('legend', () => {
beforeEach(() => {
wrapper = createWrapper({}, mount);
});
it('allows user to override legend label texts using props', () => {
const legendRelatedProps = {
legendMinText: 'legendMinText',
legendMaxText: 'legendMaxText',
legendAverageText: 'legendAverageText',
legendCurrentText: 'legendCurrentText',
};
wrapper.setProps({
...legendRelatedProps,
});
return wrapper.vm.$nextTick().then(() => {
expect(findChart().props()).toMatchObject(legendRelatedProps);
});
});
it('should render a tabular legend layout by default', () => {
expect(findLegend().props('layout')).toBe('table');
});
describe('when inline legend layout prop is set', () => {
beforeEach(() => {
wrapper.setProps({
legendLayout: 'inline',
});
});
it('should render an inline legend layout', () => {
expect(findLegend().props('layout')).toBe('inline');
});
});
describe('when table legend layout prop is set', () => {
beforeEach(() => {
wrapper.setProps({
legendLayout: 'table',
});
});
it('should render a tabular legend layout', () => {
expect(findLegend().props('layout')).toBe('table');
});
});
});
});
......@@ -55,6 +55,7 @@ describe('Time series component', () => {
stubs: {
GlPopover: true,
},
attachToDocument: true,
});
};
......@@ -87,19 +88,23 @@ describe('Time series component', () => {
return wrapper.vm.$nextTick();
});
it('allows user to override max value label text using prop', () => {
wrapper.setProps({ legendMaxText: 'legendMaxText' });
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.props().legendMaxText).toBe('legendMaxText');
});
afterEach(() => {
wrapper.destroy();
});
it('allows user to override average value label text using prop', () => {
wrapper.setProps({ legendAverageText: 'averageText' });
it('allows user to override legend label texts using props', () => {
const legendRelatedProps = {
legendMinText: 'legendMinText',
legendMaxText: 'legendMaxText',
legendAverageText: 'legendAverageText',
legendCurrentText: 'legendCurrentText',
};
wrapper.setProps({
...legendRelatedProps,
});
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.props().legendAverageText).toBe('averageText');
expect(findChart().props()).toMatchObject(legendRelatedProps);
});
});
......@@ -744,4 +749,45 @@ describe('Time series component', () => {
});
});
});
describe('legend layout', () => {
const findLegend = () => wrapper.find(GlChartLegend);
beforeEach(() => {
createWrapper(mockGraphData, mount);
return wrapper.vm.$nextTick();
});
afterEach(() => {
wrapper.destroy();
});
it('should render a tabular legend layout by default', () => {
expect(findLegend().props('layout')).toBe('table');
});
describe('when inline legend layout prop is set', () => {
beforeEach(() => {
wrapper.setProps({
legendLayout: 'inline',
});
});
it('should render an inline legend layout', () => {
expect(findLegend().props('layout')).toBe('inline');
});
});
describe('when table legend layout prop is set', () => {
beforeEach(() => {
wrapper.setProps({
legendLayout: 'table',
});
});
it('should render a tabular legend layout', () => {
expect(findLegend().props('layout')).toBe('table');
});
});
});
});
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