Commit 2125551c authored by Miguel Rincon's avatar Miguel Rincon

Add constants for empty dashboard states

This change adds constants to designate specific global empty states.

It also adds tests to ensure empty state change work correctly.
parent 0ccea59d
<script> <script>
import { GlEmptyState } from '@gitlab/ui'; import { GlEmptyState } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { dashboardEmptyStates } from '../constants';
export default { export default {
components: { components: {
...@@ -54,7 +55,7 @@ export default { ...@@ -54,7 +55,7 @@ export default {
data() { data() {
return { return {
states: { states: {
gettingStarted: { [dashboardEmptyStates.GETTING_STARTED]: {
svgUrl: this.emptyGettingStartedSvgPath, svgUrl: this.emptyGettingStartedSvgPath,
title: __('Get started with performance monitoring'), title: __('Get started with performance monitoring'),
description: __(`Stay updated about the performance and health description: __(`Stay updated about the performance and health
...@@ -64,7 +65,7 @@ export default { ...@@ -64,7 +65,7 @@ export default {
secondaryButtonText: __('Configure existing installation'), secondaryButtonText: __('Configure existing installation'),
secondaryButtonPath: this.settingsPath, secondaryButtonPath: this.settingsPath,
}, },
loading: { [dashboardEmptyStates.LOADING]: {
svgUrl: this.emptyLoadingSvgPath, svgUrl: this.emptyLoadingSvgPath,
title: __('Waiting for performance data'), title: __('Waiting for performance data'),
description: __(`Creating graphs uses the data from the Prometheus server. description: __(`Creating graphs uses the data from the Prometheus server.
...@@ -74,7 +75,7 @@ export default { ...@@ -74,7 +75,7 @@ export default {
secondaryButtonText: '', secondaryButtonText: '',
secondaryButtonPath: '', secondaryButtonPath: '',
}, },
noData: { [dashboardEmptyStates.NO_DATA]: {
svgUrl: this.emptyNoDataSvgPath, svgUrl: this.emptyNoDataSvgPath,
title: __('No data found'), title: __('No data found'),
description: __(`You are connected to the Prometheus server, but there is currently description: __(`You are connected to the Prometheus server, but there is currently
...@@ -84,7 +85,7 @@ export default { ...@@ -84,7 +85,7 @@ export default {
secondaryButtonText: '', secondaryButtonText: '',
secondaryButtonPath: '', secondaryButtonPath: '',
}, },
unableToConnect: { [dashboardEmptyStates.UNABLE_TO_CONNECT]: {
svgUrl: this.emptyUnableToConnectSvgPath, svgUrl: this.emptyUnableToConnectSvgPath,
title: __('Unable to connect to Prometheus server'), title: __('Unable to connect to Prometheus server'),
description: __( description: __(
......
export const PROMETHEUS_TIMEOUT = 120000; // TWO_MINUTES export const PROMETHEUS_TIMEOUT = 120000; // TWO_MINUTES
export const dashboardEmptyStates = {
GETTING_STARTED: 'gettingStarted',
LOADING: 'loading',
NO_DATA: 'noData',
UNABLE_TO_CONNECT: 'unableToConnect',
};
/** /**
* States and error states in Prometheus Queries (PromQL) for metrics * States and error states in Prometheus Queries (PromQL) for metrics
*/ */
......
...@@ -4,7 +4,7 @@ import * as types from './mutation_types'; ...@@ -4,7 +4,7 @@ import * as types from './mutation_types';
import { mapToDashboardViewModel, normalizeQueryResponseData } from './utils'; import { mapToDashboardViewModel, normalizeQueryResponseData } from './utils';
import httpStatusCodes from '~/lib/utils/http_status'; import httpStatusCodes from '~/lib/utils/http_status';
import { BACKOFF_TIMEOUT } from '../../lib/utils/common_utils'; import { BACKOFF_TIMEOUT } from '../../lib/utils/common_utils';
import { endpointKeys, initialStateKeys, metricStates } from '../constants'; import { dashboardEmptyStates, endpointKeys, initialStateKeys, metricStates } from '../constants';
import { optionsFromSeriesData } from './variable_mapping'; import { optionsFromSeriesData } from './variable_mapping';
/** /**
...@@ -58,7 +58,7 @@ export default { ...@@ -58,7 +58,7 @@ export default {
* Dashboard panels structure and global state * Dashboard panels structure and global state
*/ */
[types.REQUEST_METRICS_DASHBOARD](state) { [types.REQUEST_METRICS_DASHBOARD](state) {
state.emptyState = 'loading'; state.emptyState = dashboardEmptyStates.LOADING;
state.showEmptyState = true; state.showEmptyState = true;
}, },
[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, dashboardYML) { [types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, dashboardYML) {
...@@ -71,11 +71,13 @@ export default { ...@@ -71,11 +71,13 @@ export default {
state.links = links; state.links = links;
if (!state.dashboard.panelGroups.length) { if (!state.dashboard.panelGroups.length) {
state.emptyState = 'noData'; state.emptyState = dashboardEmptyStates.NO_DATA;
} }
}, },
[types.RECEIVE_METRICS_DASHBOARD_FAILURE](state, error) { [types.RECEIVE_METRICS_DASHBOARD_FAILURE](state, error) {
state.emptyState = error ? 'unableToConnect' : 'noData'; state.emptyState = error
? dashboardEmptyStates.UNABLE_TO_CONNECT
: dashboardEmptyStates.NO_DATA;
state.showEmptyState = true; state.showEmptyState = true;
}, },
...@@ -151,6 +153,8 @@ export default { ...@@ -151,6 +153,8 @@ export default {
metric.loading = false; metric.loading = false;
state.showEmptyState = false; state.showEmptyState = false;
state.emptyState = null;
if (!data.result || data.result.length === 0) { if (!data.result || data.result.length === 0) {
metric.state = metricStates.NO_DATA; metric.state = metricStates.NO_DATA;
metric.result = null; metric.result = null;
...@@ -180,11 +184,12 @@ export default { ...@@ -180,11 +184,12 @@ export default {
state.timeRange = timeRange; state.timeRange = timeRange;
}, },
[types.SET_GETTING_STARTED_EMPTY_STATE](state) { [types.SET_GETTING_STARTED_EMPTY_STATE](state) {
state.emptyState = 'gettingStarted'; state.showEmptyState = true;
state.emptyState = dashboardEmptyStates.GETTING_STARTED;
}, },
[types.SET_NO_DATA_EMPTY_STATE](state) { [types.SET_NO_DATA_EMPTY_STATE](state) {
state.showEmptyState = true; state.showEmptyState = true;
state.emptyState = 'noData'; state.emptyState = dashboardEmptyStates.NO_DATA;
}, },
[types.SET_ALL_DASHBOARDS](state, dashboards) { [types.SET_ALL_DASHBOARDS](state, dashboards) {
state.allDashboards = dashboards || []; state.allDashboards = dashboards || [];
......
import invalidUrl from '~/lib/utils/invalid_url'; import invalidUrl from '~/lib/utils/invalid_url';
import { timezones } from '../format_date'; import { timezones } from '../format_date';
import { dashboardEmptyStates } from '../constants';
export default () => ({ export default () => ({
// API endpoints // API endpoints
...@@ -20,7 +21,7 @@ export default () => ({ ...@@ -20,7 +21,7 @@ export default () => ({
// Dashboard data // Dashboard data
hasDashboardValidationWarnings: false, hasDashboardValidationWarnings: false,
emptyState: 'gettingStarted', emptyState: dashboardEmptyStates.GETTING_STARTED,
showEmptyState: true, showEmptyState: true,
showErrorBanner: true, showErrorBanner: true,
isUpdatingStarredValue: false, isUpdatingStarredValue: false,
......
...@@ -24,6 +24,18 @@ exports[`EmptyState shows loading state 1`] = ` ...@@ -24,6 +24,18 @@ exports[`EmptyState shows loading state 1`] = `
/> />
`; `;
exports[`EmptyState shows noData state 1`] = `
<gl-empty-state-stub
description="You are connected to the Prometheus server, but there is currently no data to display."
primarybuttonlink="/settingsPath"
primarybuttontext="Configure Prometheus"
secondarybuttonlink=""
secondarybuttontext=""
svgpath="/path/to/no-data.svg"
title="No data found"
/>
`;
exports[`EmptyState shows unableToConnect state 1`] = ` exports[`EmptyState shows unableToConnect state 1`] = `
<gl-empty-state-stub <gl-empty-state-stub
description="Ensure connectivity is available from the GitLab server to the Prometheus server" description="Ensure connectivity is available from the GitLab server to the Prometheus server"
......
...@@ -6,7 +6,7 @@ import { objectToQuery } from '~/lib/utils/url_utility'; ...@@ -6,7 +6,7 @@ import { objectToQuery } from '~/lib/utils/url_utility';
import VueDraggable from 'vuedraggable'; import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { metricStates } from '~/monitoring/constants'; import { dashboardEmptyStates, metricStates } from '~/monitoring/constants';
import Dashboard from '~/monitoring/components/dashboard.vue'; import Dashboard from '~/monitoring/components/dashboard.vue';
import DashboardHeader from '~/monitoring/components/dashboard_header.vue'; import DashboardHeader from '~/monitoring/components/dashboard_header.vue';
...@@ -126,13 +126,13 @@ describe('Dashboard', () => { ...@@ -126,13 +126,13 @@ describe('Dashboard', () => {
}); });
it('shows up a loading state', () => { it('shows up a loading state', () => {
store.state.monitoringDashboard.emptyState = 'loading'; store.state.monitoringDashboard.emptyState = dashboardEmptyStates.LOADING;
createShallowWrapper({ hasMetrics: true }); createShallowWrapper({ hasMetrics: true });
return wrapper.vm.$nextTick().then(() => { return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(EmptyState).exists()).toBe(true); expect(wrapper.find(EmptyState).exists()).toBe(true);
expect(wrapper.find(EmptyState).props('selectedState')).toBe('loading'); expect(wrapper.find(EmptyState).props('selectedState')).toBe(dashboardEmptyStates.LOADING);
}); });
}); });
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { dashboardEmptyStates } from '~/monitoring/constants';
import EmptyState from '~/monitoring/components/empty_state.vue'; import EmptyState from '~/monitoring/components/empty_state.vue';
function createComponent(props) { function createComponent(props) {
...@@ -20,7 +21,7 @@ function createComponent(props) { ...@@ -20,7 +21,7 @@ function createComponent(props) {
describe('EmptyState', () => { describe('EmptyState', () => {
it('shows gettingStarted state', () => { it('shows gettingStarted state', () => {
const wrapper = createComponent({ const wrapper = createComponent({
selectedState: 'gettingStarted', selectedState: dashboardEmptyStates.GETTING_STARTED,
}); });
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
...@@ -28,7 +29,7 @@ describe('EmptyState', () => { ...@@ -28,7 +29,7 @@ describe('EmptyState', () => {
it('shows loading state', () => { it('shows loading state', () => {
const wrapper = createComponent({ const wrapper = createComponent({
selectedState: 'loading', selectedState: dashboardEmptyStates.LOADING,
}); });
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
...@@ -36,7 +37,15 @@ describe('EmptyState', () => { ...@@ -36,7 +37,15 @@ describe('EmptyState', () => {
it('shows unableToConnect state', () => { it('shows unableToConnect state', () => {
const wrapper = createComponent({ const wrapper = createComponent({
selectedState: 'unableToConnect', selectedState: dashboardEmptyStates.UNABLE_TO_CONNECT,
});
expect(wrapper.element).toMatchSnapshot();
});
it('shows noData state', () => {
const wrapper = createComponent({
selectedState: dashboardEmptyStates.NO_DATA,
}); });
expect(wrapper.element).toMatchSnapshot(); expect(wrapper.element).toMatchSnapshot();
......
...@@ -3,7 +3,7 @@ import httpStatusCodes from '~/lib/utils/http_status'; ...@@ -3,7 +3,7 @@ import httpStatusCodes from '~/lib/utils/http_status';
import mutations from '~/monitoring/stores/mutations'; import mutations from '~/monitoring/stores/mutations';
import * as types from '~/monitoring/stores/mutation_types'; import * as types from '~/monitoring/stores/mutation_types';
import state from '~/monitoring/stores/state'; import state from '~/monitoring/stores/state';
import { metricStates } from '~/monitoring/constants'; import { dashboardEmptyStates, metricStates } from '~/monitoring/constants';
import { deploymentData, dashboardGitResponse, storeTextVariables } from '../mock_data'; import { deploymentData, dashboardGitResponse, storeTextVariables } from '../mock_data';
import { metricsDashboardPayload } from '../fixture_data'; import { metricsDashboardPayload } from '../fixture_data';
...@@ -15,6 +15,15 @@ describe('Monitoring mutations', () => { ...@@ -15,6 +15,15 @@ describe('Monitoring mutations', () => {
stateCopy = state(); stateCopy = state();
}); });
describe('REQUEST_METRICS_DASHBOARD', () => {
it('sets an empty loading state', () => {
mutations[types.REQUEST_METRICS_DASHBOARD](stateCopy);
expect(stateCopy.emptyState).toBe(dashboardEmptyStates.LOADING);
expect(stateCopy.showEmptyState).toBe(true);
});
});
describe('RECEIVE_METRICS_DASHBOARD_SUCCESS', () => { describe('RECEIVE_METRICS_DASHBOARD_SUCCESS', () => {
let payload; let payload;
const getGroups = () => stateCopy.dashboard.panelGroups; const getGroups = () => stateCopy.dashboard.panelGroups;
...@@ -23,6 +32,18 @@ describe('Monitoring mutations', () => { ...@@ -23,6 +32,18 @@ describe('Monitoring mutations', () => {
stateCopy.dashboard.panelGroups = []; stateCopy.dashboard.panelGroups = [];
payload = metricsDashboardPayload; payload = metricsDashboardPayload;
}); });
it('sets an empty noData state when the dashboard is empty', () => {
const emptyDashboardPayload = {
...payload,
panel_groups: [],
};
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](stateCopy, emptyDashboardPayload);
const groups = getGroups();
expect(groups).toEqual([]);
expect(stateCopy.emptyState).toBe(dashboardEmptyStates.NO_DATA);
});
it('adds a key to the group', () => { it('adds a key to the group', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](stateCopy, payload); mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](stateCopy, payload);
const groups = getGroups(); const groups = getGroups();
...@@ -72,6 +93,22 @@ describe('Monitoring mutations', () => { ...@@ -72,6 +93,22 @@ describe('Monitoring mutations', () => {
}); });
}); });
describe('RECEIVE_METRICS_DASHBOARD_FAILURE', () => {
it('sets an empty noData state when an empty error occurs', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_FAILURE](stateCopy);
expect(stateCopy.emptyState).toBe(dashboardEmptyStates.NO_DATA);
expect(stateCopy.showEmptyState).toBe(true);
});
it('sets an empty unableToConnect state when an error occurs', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_FAILURE](stateCopy, 'myerror');
expect(stateCopy.emptyState).toBe(dashboardEmptyStates.UNABLE_TO_CONNECT);
expect(stateCopy.showEmptyState).toBe(true);
});
});
describe('Dashboard starring mutations', () => { describe('Dashboard starring mutations', () => {
it('REQUEST_DASHBOARD_STARRING', () => { it('REQUEST_DASHBOARD_STARRING', () => {
stateCopy = { isUpdatingStarredValue: false }; stateCopy = { isUpdatingStarredValue: false };
...@@ -283,6 +320,7 @@ describe('Monitoring mutations', () => { ...@@ -283,6 +320,7 @@ describe('Monitoring mutations', () => {
}); });
expect(stateCopy.showEmptyState).toBe(false); expect(stateCopy.showEmptyState).toBe(false);
expect(stateCopy.emptyState).toBe(null);
}); });
it('adds results to the store', () => { it('adds results to the store', () => {
......
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