Commit 378c507c authored by Jose Ivan Vargas's avatar Jose Ivan Vargas Committed by Clement Ho

Add empty chart component

This merge request adds an empty chart component
which will load in case of queries not having
data to display, this will override the current
logic, which hides all the graphs that have no
data to show
parent 974bb23f
<script>
import chartEmptyStateIllustration from '@gitlab/svgs/dist/illustrations/chart-empty-state.svg';
import { chartHeight } from '../../constants';
export default {
props: {
graphTitle: {
type: String,
required: true,
},
},
data() {
return {
height: chartHeight,
};
},
computed: {
svgContainerStyle() {
return {
height: `${this.height}px`,
};
},
},
created() {
this.chartEmptyStateIllustration = chartEmptyStateIllustration;
},
};
</script>
<template>
<div class="prometheus-graph col-12 col-lg-6 d-flex flex-column justify-content-center">
<div class="prometheus-graph-header">
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphTitle }}</h5>
</div>
<div
class="prepend-top-8 svg-w-100 d-flex align-items-center"
:style="svgContainerStyle"
v-html="chartEmptyStateIllustration"
></div>
<h5 class="text-center prepend-top-8">{{ __('No data to display') }}</h5>
</div>
</template>
...@@ -157,9 +157,6 @@ export default { ...@@ -157,9 +157,6 @@ export default {
'multipleDashboardsEnabled', 'multipleDashboardsEnabled',
'additionalPanelTypesEnabled', 'additionalPanelTypesEnabled',
]), ]),
groupsWithData() {
return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0);
},
selectedDashboardText() { selectedDashboardText() {
return this.currentDashboard || (this.allDashboards[0] && this.allDashboards[0].display_name); return this.currentDashboard || (this.allDashboards[0] && this.allDashboards[0].display_name);
}, },
...@@ -256,6 +253,9 @@ export default { ...@@ -256,6 +253,9 @@ export default {
setTimeWindowParameter(key) { setTimeWindowParameter(key) {
return `?time_window=${key}`; return `?time_window=${key}`;
}, },
groupHasData(group) {
return this.chartsWithData(group.metrics).length > 0;
},
}, },
addMetric: { addMetric: {
title: s__('Metrics|Add metric'), title: s__('Metrics|Add metric'),
...@@ -369,14 +369,15 @@ export default { ...@@ -369,14 +369,15 @@ export default {
</div> </div>
<div v-if="!showEmptyState"> <div v-if="!showEmptyState">
<graph-group <graph-group
v-for="groupData in groupsWithData" v-for="groupData in groups"
:key="`${groupData.group}.${groupData.priority}`" :key="`${groupData.group}.${groupData.priority}`"
:name="groupData.group" :name="groupData.group"
:show-panels="showPanels" :show-panels="showPanels"
:collapse-group="groupHasData(groupData)"
> >
<template v-if="additionalPanelTypesEnabled"> <template v-if="additionalPanelTypesEnabled">
<panel-type <panel-type
v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)" v-for="(graphData, graphIndex) in groupData.metrics"
:key="`panel-type-${graphIndex}`" :key="`panel-type-${graphIndex}`"
:graph-data="graphData" :graph-data="graphData"
:dashboard-width="elWidth" :dashboard-width="elWidth"
......
...@@ -10,6 +10,10 @@ export default { ...@@ -10,6 +10,10 @@ export default {
required: false, required: false,
default: true, default: true,
}, },
collapseGroup: {
type: Boolean,
required: true,
},
}, },
}; };
</script> </script>
...@@ -19,7 +23,7 @@ export default { ...@@ -19,7 +23,7 @@ export default {
<div class="card-header"> <div class="card-header">
<h4>{{ name }}</h4> <h4>{{ name }}</h4>
</div> </div>
<div class="card-body prometheus-graph-group"><slot></slot></div> <div v-if="collapseGroup" class="card-body prometheus-graph-group"><slot></slot></div>
</div> </div>
<div v-else class="prometheus-graph-group"><slot></slot></div> <div v-else class="prometheus-graph-group"><slot></slot></div>
</template> </template>
...@@ -3,11 +3,13 @@ import { mapState } from 'vuex'; ...@@ -3,11 +3,13 @@ import { mapState } from 'vuex';
import _ from 'underscore'; import _ from 'underscore';
import MonitorAreaChart from './charts/area.vue'; import MonitorAreaChart from './charts/area.vue';
import MonitorSingleStatChart from './charts/single_stat.vue'; import MonitorSingleStatChart from './charts/single_stat.vue';
import MonitorEmptyChart from './charts/empty_chart.vue';
export default { export default {
components: { components: {
MonitorAreaChart, MonitorAreaChart,
MonitorSingleStatChart, MonitorSingleStatChart,
MonitorEmptyChart,
}, },
props: { props: {
graphData: { graphData: {
...@@ -24,6 +26,9 @@ export default { ...@@ -24,6 +26,9 @@ export default {
alertWidgetAvailable() { alertWidgetAvailable() {
return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData; return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData;
}, },
graphDataHasMetrics() {
return this.graphData.queries[0].result.length > 0;
},
}, },
methods: { methods: {
getGraphAlerts(queries) { getGraphAlerts(queries) {
...@@ -41,9 +46,12 @@ export default { ...@@ -41,9 +46,12 @@ export default {
}; };
</script> </script>
<template> <template>
<monitor-single-stat-chart v-if="isPanelType('single-stat')" :graph-data="graphData" /> <monitor-single-stat-chart
v-if="isPanelType('single-stat') && graphDataHasMetrics"
:graph-data="graphData"
/>
<monitor-area-chart <monitor-area-chart
v-else v-else-if="graphDataHasMetrics"
:graph-data="graphData" :graph-data="graphData"
:deployment-data="deploymentData" :deployment-data="deploymentData"
:project-path="projectPath" :project-path="projectPath"
...@@ -59,4 +67,5 @@ export default { ...@@ -59,4 +67,5 @@ export default {
@setAlerts="setAlerts" @setAlerts="setAlerts"
/> />
</monitor-area-chart> </monitor-area-chart>
<monitor-empty-chart v-else :graph-title="graphData.title" />
</template> </template>
---
title: Add empty chart component
merge_request: 30682
author:
type: fixed
...@@ -6917,6 +6917,9 @@ msgstr "" ...@@ -6917,6 +6917,9 @@ msgstr ""
msgid "No data found" msgid "No data found"
msgstr "" msgstr ""
msgid "No data to display"
msgstr ""
msgid "No details available" msgid "No details available"
msgstr "" msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import EmptyChart from '~/monitoring/components/charts/empty_chart.vue';
describe('Empty Chart component', () => {
let emptyChart;
const graphTitle = 'Memory Usage';
beforeEach(() => {
emptyChart = shallowMount(EmptyChart, {
propsData: {
graphTitle,
},
});
});
afterEach(() => {
emptyChart.destroy();
});
it('render the chart title', () => {
expect(emptyChart.find({ ref: 'graphTitle' }).text()).toBe(graphTitle);
});
describe('Computed props', () => {
it('sets the height for the svg container', () => {
expect(emptyChart.vm.svgContainerStyle.height).toBe('300px');
});
});
});
import { shallowMount } from '@vue/test-utils';
import PanelType from '~/monitoring/components/panel_type.vue';
import EmptyChart from '~/monitoring/components/charts/empty_chart.vue';
import { graphDataPrometheusQueryRange } from './mock_data';
describe('Panel Type component', () => {
let panelType;
const dashboardWidth = 100;
describe('When no graphData is available', () => {
let glEmptyChart;
const graphDataNoResult = graphDataPrometheusQueryRange;
graphDataNoResult.queries[0].result = [];
beforeEach(() => {
panelType = shallowMount(PanelType, {
propsData: {
dashboardWidth,
graphData: graphDataNoResult,
},
});
});
afterEach(() => {
panelType.destroy();
});
describe('Empty Chart component', () => {
beforeEach(() => {
glEmptyChart = panelType.find(EmptyChart);
});
it('is a Vue instance', () => {
expect(glEmptyChart.isVueInstance()).toBe(true);
});
it('it receives a graph title', () => {
const props = glEmptyChart.props();
expect(props.graphTitle).toBe(panelType.vm.graphData.title);
});
});
});
});
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