Commit 31e85bbd authored by Miguel Rincon's avatar Miguel Rincon

Add action to view a detailed version of a panel

Adds a state in the dashboard where a single panel can be viewed
expanded for a detailed view. This state can be reached by pressing
a menu item in each chart.
parent 7974f2b9
...@@ -3,6 +3,8 @@ import { debounce, pickBy } from 'lodash'; ...@@ -3,6 +3,8 @@ import { debounce, pickBy } from 'lodash';
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import VueDraggable from 'vuedraggable'; import VueDraggable from 'vuedraggable';
import { import {
GlIcon,
GlButton,
GlDeprecatedButton, GlDeprecatedButton,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
...@@ -17,7 +19,6 @@ import { ...@@ -17,7 +19,6 @@ import {
import DashboardPanel from './dashboard_panel.vue'; import DashboardPanel from './dashboard_panel.vue';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue'; import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import { mergeUrlParams, redirectTo, updateHistory } from '~/lib/utils/url_utility'; import { mergeUrlParams, redirectTo, updateHistory } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url'; import invalidUrl from '~/lib/utils/invalid_url';
...@@ -39,6 +40,8 @@ export default { ...@@ -39,6 +40,8 @@ export default {
VueDraggable, VueDraggable,
DashboardPanel, DashboardPanel,
Icon, Icon,
GlIcon,
GlButton,
GlDeprecatedButton, GlDeprecatedButton,
GlDropdown, GlDropdown,
GlLoadingIcon, GlLoadingIcon,
...@@ -60,7 +63,6 @@ export default { ...@@ -60,7 +63,6 @@ export default {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
TrackEvent: TrackEventDirective, TrackEvent: TrackEventDirective,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
externalDashboardUrl: { externalDashboardUrl: {
type: String, type: String,
...@@ -197,7 +199,6 @@ export default { ...@@ -197,7 +199,6 @@ export default {
}, },
data() { data() {
return { return {
state: 'gettingStarted',
formIsValid: null, formIsValid: null,
selectedTimeRange: timeRangeFromUrl() || defaultTimeRange, selectedTimeRange: timeRangeFromUrl() || defaultTimeRange,
hasValidDates: true, hasValidDates: true,
...@@ -212,8 +213,8 @@ export default { ...@@ -212,8 +213,8 @@ export default {
'showEmptyState', 'showEmptyState',
'useDashboardEndpoint', 'useDashboardEndpoint',
'allDashboards', 'allDashboards',
'additionalPanelTypesEnabled',
'environmentsLoading', 'environmentsLoading',
'expandedPanel',
]), ]),
...mapGetters('monitoringDashboard', ['getMetricStates', 'filteredEnvironments']), ...mapGetters('monitoringDashboard', ['getMetricStates', 'filteredEnvironments']),
firstDashboard() { firstDashboard() {
...@@ -232,14 +233,6 @@ export default { ...@@ -232,14 +233,6 @@ export default {
this.firstDashboard === this.selectedDashboard this.firstDashboard === this.selectedDashboard
); );
}, },
hasHeaderButtons() {
return (
this.addingMetricsAvailable ||
this.showRearrangePanelsBtn ||
this.selectedDashboard.can_edit ||
this.externalDashboardUrl.length
);
},
shouldShowEnvironmentsDropdownNoMatchedMsg() { shouldShowEnvironmentsDropdownNoMatchedMsg() {
return !this.environmentsLoading && this.filteredEnvironments.length === 0; return !this.environmentsLoading && this.filteredEnvironments.length === 0;
}, },
...@@ -273,6 +266,8 @@ export default { ...@@ -273,6 +266,8 @@ export default {
'setInitialState', 'setInitialState',
'setPanelGroupMetrics', 'setPanelGroupMetrics',
'filterEnvironments', 'filterEnvironments',
'setExpandedPanel',
'clearExpandedPanel',
]), ]),
updatePanels(key, panels) { updatePanels(key, panels) {
this.setPanelGroupMetrics({ this.setPanelGroupMetrics({
...@@ -300,9 +295,13 @@ export default { ...@@ -300,9 +295,13 @@ export default {
this.selectedTimeRange = defaultTimeRange; this.selectedTimeRange = defaultTimeRange;
}, },
generateLink(group, title, yLabel) { generatePanelLink(group, graphData) {
if (!group || !graphData) {
return null;
}
const dashboard = this.currentDashboard || this.firstDashboard.path; const dashboard = this.currentDashboard || this.firstDashboard.path;
const params = pickBy({ dashboard, group, title, y_label: yLabel }, value => value != null); const { y_label, title } = graphData;
const params = pickBy({ dashboard, group, title, y_label }, value => value != null);
return mergeUrlParams(params, window.location.href); return mergeUrlParams(params, window.location.href);
}, },
hideAddMetricModal() { hideAddMetricModal() {
...@@ -366,11 +365,20 @@ export default { ...@@ -366,11 +365,20 @@ export default {
}); });
this.selectedTimeRange = { start, end }; this.selectedTimeRange = { start, end };
}, },
onExpandPanel(group, panel) {
this.setExpandedPanel({ group, panel });
},
onGoBack() {
this.clearExpandedPanel();
},
}, },
addMetric: { addMetric: {
title: s__('Metrics|Add metric'), title: s__('Metrics|Add metric'),
modalId: 'add-metric', modalId: 'add-metric',
}, },
i18n: {
goBackLabel: s__('Metrics|Go back'),
},
}; };
</script> </script>
...@@ -541,59 +549,88 @@ export default { ...@@ -541,59 +549,88 @@ export default {
</div> </div>
<div v-if="!showEmptyState"> <div v-if="!showEmptyState">
<graph-group <dashboard-panel
v-for="(groupData, index) in dashboard.panelGroups" v-show="expandedPanel.panel"
:key="`${groupData.group}.${groupData.priority}`" ref="expandedPanel"
:name="groupData.group" :clipboard-text="generatePanelLink(expandedPanel.group, expandedPanel.panel)"
:show-panels="showPanels" :graph-data="expandedPanel.panel"
:collapse-group="collapseGroup(groupData.key)" :alerts-endpoint="alertsEndpoint"
:height="600"
:prometheus-alerts-available="prometheusAlertsAvailable"
@timerangezoom="onTimeRangeZoom"
> >
<vue-draggable <template #topLeft>
v-if="!groupSingleEmptyState(groupData.key)" <gl-button
:value="groupData.panels" ref="goBackBtn"
group="metrics-dashboard" v-gl-tooltip
:component-data="{ attrs: { class: 'row mx-0 w-100' } }" class="mr-3 my-3"
:disabled="!isRearrangingPanels" :title="$options.i18n.goBackLabel"
@input="updatePanels(groupData.key, $event)" @click="onGoBack"
>
<gl-icon
name="arrow-left"
:aria-label="$options.i18n.goBackLabel"
class="text-secondary"
/>
</gl-button>
</template>
</dashboard-panel>
<div v-show="!expandedPanel.panel">
<graph-group
v-for="groupData in dashboard.panelGroups"
:key="`${groupData.group}.${groupData.priority}`"
:name="groupData.group"
:show-panels="showPanels"
:collapse-group="collapseGroup(groupData.key)"
> >
<div <vue-draggable
v-for="(graphData, graphIndex) in groupData.panels" v-if="!groupSingleEmptyState(groupData.key)"
:key="`dashboard-panel-${graphIndex}`" :value="groupData.panels"
class="col-12 col-lg-6 px-2 mb-2 draggable" group="metrics-dashboard"
:class="{ 'draggable-enabled': isRearrangingPanels }" :component-data="{ attrs: { class: 'row mx-0 w-100' } }"
:disabled="!isRearrangingPanels"
@input="updatePanels(groupData.key, $event)"
> >
<div class="position-relative draggable-panel js-draggable-panel"> <div
<div v-for="(graphData, graphIndex) in groupData.panels"
v-if="isRearrangingPanels" :key="`dashboard-panel-${graphIndex}`"
class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end" class="col-12 col-lg-6 px-2 mb-2 draggable"
@click="removePanel(groupData.key, groupData.panels, graphIndex)" :class="{ 'draggable-enabled': isRearrangingPanels }"
> >
<a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')"> <div class="position-relative draggable-panel js-draggable-panel">
<icon name="close" /> <div
</a> v-if="isRearrangingPanels"
</div> class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end"
@click="removePanel(groupData.key, groupData.panels, graphIndex)"
>
<a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')">
<icon name="close" />
</a>
</div>
<dashboard-panel <dashboard-panel
:clipboard-text="generateLink(groupData.group, graphData.title, graphData.y_label)" :clipboard-text="generatePanelLink(groupData.group, graphData)"
:graph-data="graphData" :graph-data="graphData"
:alerts-endpoint="alertsEndpoint" :alerts-endpoint="alertsEndpoint"
:prometheus-alerts-available="prometheusAlertsAvailable" :prometheus-alerts-available="prometheusAlertsAvailable"
:index="`${index}-${graphIndex}`" @timerangezoom="onTimeRangeZoom"
@timerangezoom="onTimeRangeZoom" @expand="onExpandPanel(groupData.group, graphData)"
/> />
</div>
</div> </div>
</vue-draggable>
<div v-else class="py-5 col col-sm-10 col-md-8 col-lg-7 col-xl-6">
<group-empty-state
ref="empty-group"
:documentation-path="documentationPath"
:settings-path="settingsPath"
:selected-state="groupSingleEmptyState(groupData.key)"
:svg-path="emptyNoDataSmallSvgPath"
/>
</div> </div>
</vue-draggable> </graph-group>
<div v-else class="py-5 col col-sm-10 col-md-8 col-lg-7 col-xl-6"> </div>
<group-empty-state
ref="empty-group"
:documentation-path="documentationPath"
:settings-path="settingsPath"
:selected-state="groupSingleEmptyState(groupData.key)"
:svg-path="emptyNoDataSmallSvgPath"
/>
</div>
</graph-group>
</div> </div>
<empty-state <empty-state
v-else v-else
......
...@@ -59,7 +59,8 @@ export default { ...@@ -59,7 +59,8 @@ export default {
}, },
graphData: { graphData: {
type: Object, type: Object,
required: true, required: false,
default: null,
}, },
groupId: { groupId: {
type: String, type: String,
...@@ -114,17 +115,13 @@ export default { ...@@ -114,17 +115,13 @@ export default {
}, },
}), }),
title() { title() {
return this.graphData.title || ''; return this.graphData?.title || '';
}, },
graphDataHasResult() { graphDataHasResult() {
return ( return this.graphData?.metrics?.[0]?.result?.length > 0;
this.graphData.metrics &&
this.graphData.metrics[0].result &&
this.graphData.metrics[0].result.length > 0
);
}, },
graphDataIsLoading() { graphDataIsLoading() {
const { metrics = [] } = this.graphData; const metrics = this.graphData?.metrics || [];
return metrics.some(({ loading }) => loading); return metrics.some(({ loading }) => loading);
}, },
logsPathWithTimeRange() { logsPathWithTimeRange() {
...@@ -136,7 +133,7 @@ export default { ...@@ -136,7 +133,7 @@ export default {
return null; return null;
}, },
csvText() { csvText() {
const chartData = this.graphData.metrics[0].result[0].values; const chartData = this.graphData?.metrics[0].result[0].values || [];
const yLabel = this.graphData.y_label; const yLabel = this.graphData.y_label;
const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/require-i18n-strings const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/require-i18n-strings
return chartData.reduce((csv, data) => { return chartData.reduce((csv, data) => {
...@@ -230,7 +227,7 @@ export default { ...@@ -230,7 +227,7 @@ export default {
return Object.values(this.getGraphAlerts(queries)); return Object.values(this.getGraphAlerts(queries));
}, },
isPanelType(type) { isPanelType(type) {
return this.graphData.type && this.graphData.type === type; return this.graphData?.type === type;
}, },
showToast() { showToast() {
this.$toast.show(__('Link copied')); this.$toast.show(__('Link copied'));
......
...@@ -89,6 +89,17 @@ export const setShowErrorBanner = ({ commit }, enabled) => { ...@@ -89,6 +89,17 @@ export const setShowErrorBanner = ({ commit }, enabled) => {
commit(types.SET_SHOW_ERROR_BANNER, enabled); commit(types.SET_SHOW_ERROR_BANNER, enabled);
}; };
export const setExpandedPanel = ({ commit }, { group, panel }) => {
commit(types.SET_EXPANDED_PANEL, { group, panel });
};
export const clearExpandedPanel = ({ commit }) => {
commit(types.SET_EXPANDED_PANEL, {
group: null,
panel: null,
});
};
// All Data // All Data
export const fetchData = ({ dispatch }) => { export const fetchData = ({ dispatch }) => {
......
...@@ -31,5 +31,5 @@ export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE' ...@@ -31,5 +31,5 @@ export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE'
export const SET_NO_DATA_EMPTY_STATE = 'SET_NO_DATA_EMPTY_STATE'; export const SET_NO_DATA_EMPTY_STATE = 'SET_NO_DATA_EMPTY_STATE';
export const SET_SHOW_ERROR_BANNER = 'SET_SHOW_ERROR_BANNER'; export const SET_SHOW_ERROR_BANNER = 'SET_SHOW_ERROR_BANNER';
export const SET_PANEL_GROUP_METRICS = 'SET_PANEL_GROUP_METRICS'; export const SET_PANEL_GROUP_METRICS = 'SET_PANEL_GROUP_METRICS';
export const SET_ENVIRONMENTS_FILTER = 'SET_ENVIRONMENTS_FILTER'; export const SET_ENVIRONMENTS_FILTER = 'SET_ENVIRONMENTS_FILTER';
export const SET_EXPANDED_PANEL = 'SET_EXPANDED_PANEL';
...@@ -134,6 +134,8 @@ export default { ...@@ -134,6 +134,8 @@ export default {
metric.loading = false; metric.loading = false;
metric.result = null; metric.result = null;
}, },
// Parameters and other information
[types.SET_INITIAL_STATE](state, initialState = {}) { [types.SET_INITIAL_STATE](state, initialState = {}) {
Object.assign(state, pick(initialState, initialStateKeys)); Object.assign(state, pick(initialState, initialStateKeys));
}, },
...@@ -163,4 +165,8 @@ export default { ...@@ -163,4 +165,8 @@ export default {
[types.SET_ENVIRONMENTS_FILTER](state, searchTerm) { [types.SET_ENVIRONMENTS_FILTER](state, searchTerm) {
state.environmentsSearchTerm = searchTerm; state.environmentsSearchTerm = searchTerm;
}, },
[types.SET_EXPANDED_PANEL](state, { group, panel }) {
state.expandedPanel.group = group;
state.expandedPanel.panel = panel;
},
}; };
...@@ -17,6 +17,21 @@ export default () => ({ ...@@ -17,6 +17,21 @@ export default () => ({
dashboard: { dashboard: {
panelGroups: [], panelGroups: [],
}, },
/**
* Panel that is currently "zoomed" in as
* a single panel in view.
*/
expandedPanel: {
/**
* {?String} Panel's group name.
*/
group: null,
/**
* {?Object} Panel content from `dashboard`
* null when no panel is expanded.
*/
panel: null,
},
allDashboards: [], allDashboards: [],
// Other project data // Other project data
......
---
title: View a details of a panel in 'full screen mode'
merge_request: 29902
author:
type: added
...@@ -13117,6 +13117,9 @@ msgstr "" ...@@ -13117,6 +13117,9 @@ msgstr ""
msgid "Metrics|For grouping similar metrics" msgid "Metrics|For grouping similar metrics"
msgstr "" msgstr ""
msgid "Metrics|Go back"
msgstr ""
msgid "Metrics|Invalid time range, please verify." msgid "Metrics|Invalid time range, please verify."
msgstr "" msgstr ""
......
...@@ -56,7 +56,7 @@ describe('Dashboard Panel', () => { ...@@ -56,7 +56,7 @@ describe('Dashboard Panel', () => {
const findTitle = () => wrapper.find({ ref: 'graphTitle' }); const findTitle = () => wrapper.find({ ref: 'graphTitle' });
const findContextualMenu = () => wrapper.find({ ref: 'contextualMenu' }); const findContextualMenu = () => wrapper.find({ ref: 'contextualMenu' });
const createWrapper = (props, options = {}) => { const createWrapper = (props, options) => {
wrapper = shallowMount(DashboardPanel, { wrapper = shallowMount(DashboardPanel, {
propsData: { propsData: {
graphData, graphData,
...@@ -108,24 +108,51 @@ describe('Dashboard Panel', () => { ...@@ -108,24 +108,51 @@ describe('Dashboard Panel', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('Empty Chart component', () => { it('renders the chart title', () => {
it('renders the chart title', () => { expect(findTitle().text()).toBe(graphDataEmpty.title);
expect(findTitle().text()).toBe(graphDataEmpty.title); });
});
it('renders the no download csv link', () => { it('renders no download csv link', () => {
expect(wrapper.find({ ref: 'downloadCsvLink' }).exists()).toBe(false); expect(wrapper.find({ ref: 'downloadCsvLink' }).exists()).toBe(false);
}); });
it('does not contain graph widgets', () => { it('does not contain graph widgets', () => {
expect(findContextualMenu().exists()).toBe(false); expect(findContextualMenu().exists()).toBe(false);
}); });
it('is a Vue instance', () => { it('The Empty Chart component is rendered and is a Vue instance', () => {
expect(wrapper.find(MonitorEmptyChart).exists()).toBe(true); expect(wrapper.find(MonitorEmptyChart).exists()).toBe(true);
expect(wrapper.find(MonitorEmptyChart).isVueInstance()).toBe(true); expect(wrapper.find(MonitorEmptyChart).isVueInstance()).toBe(true);
});
});
describe('When graphData is null', () => {
beforeEach(() => {
createWrapper({
graphData: null,
}); });
}); });
afterEach(() => {
wrapper.destroy();
});
it('renders no chart title', () => {
expect(findTitle().text()).toBe('');
});
it('renders no download csv link', () => {
expect(wrapper.find({ ref: 'downloadCsvLink' }).exists()).toBe(false);
});
it('does not contain graph widgets', () => {
expect(findContextualMenu().exists()).toBe(false);
});
it('The Empty Chart component is rendered and is a Vue instance', () => {
expect(wrapper.find(MonitorEmptyChart).exists()).toBe(true);
expect(wrapper.find(MonitorEmptyChart).isVueInstance()).toBe(true);
});
}); });
describe('When graphData is available', () => { describe('When graphData is available', () => {
......
...@@ -212,6 +212,97 @@ describe('Dashboard', () => { ...@@ -212,6 +212,97 @@ describe('Dashboard', () => {
}); });
}); });
describe('single panel expands to "full screen" mode', () => {
const findExpandedPanel = () => wrapper.find({ ref: 'expandedPanel' });
describe('when the panel is not expanded', () => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
setupStoreWithData(wrapper.vm.$store);
return wrapper.vm.$nextTick();
});
it('expanded panel is not visible', () => {
expect(findExpandedPanel().isVisible()).toBe(false);
});
it('can set a panel as expanded', () => {
const panel = wrapper.findAll(DashboardPanel).at(1);
jest.spyOn(store, 'dispatch');
panel.vm.$emit('expand');
const groupData = metricsDashboardViewModel.panelGroups[0];
expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/setExpandedPanel', {
group: groupData.group,
panel: expect.objectContaining({
id: groupData.panels[0].id,
}),
});
});
});
describe('when the panel is expanded', () => {
let group;
let panel;
const MockPanel = {
template: `<div><slot name="topLeft"/></div>`,
};
beforeEach(() => {
createShallowWrapper({ hasMetrics: true }, { stubs: { DashboardPanel: MockPanel } });
setupStoreWithData(wrapper.vm.$store);
const { panelGroups } = wrapper.vm.$store.state.monitoringDashboard.dashboard;
group = panelGroups[0].group;
[panel] = panelGroups[0].panels;
wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_EXPANDED_PANEL}`, {
group,
panel,
});
return wrapper.vm.$nextTick();
});
it('displays a single panel and others are hidden', () => {
const panels = wrapper.findAll(MockPanel);
const visiblePanels = panels.filter(w => w.isVisible());
expect(findExpandedPanel().isVisible()).toBe(true);
// v-show for hiding panels is more performant than v-if
// check for panels to be hidden.
expect(panels.length).toBe(metricsDashboardPanelCount + 1);
expect(visiblePanels.length).toBe(1);
});
it('sets a link to the expanded panel', () => {
const searchQuery =
'?group=System%20metrics%20(Kubernetes)&title=Memory%20Usage%20(Total)&y_label=Total%20Memory%20Used%20(GB)';
expect(findExpandedPanel().attributes('clipboard-text')).toEqual(
expect.stringContaining(searchQuery),
);
});
it('restores full dashboard by clicking `back`', () => {
const backBtn = wrapper.find({ ref: 'goBackBtn' });
expect(backBtn.exists()).toBe(true);
jest.spyOn(store, 'dispatch');
backBtn.vm.$emit('click');
expect(store.dispatch).toHaveBeenCalledWith(
'monitoringDashboard/clearExpandedPanel',
undefined,
);
});
});
});
describe('when one of the metrics is missing', () => { describe('when one of the metrics is missing', () => {
beforeEach(() => { beforeEach(() => {
createShallowWrapper({ hasMetrics: true }); createShallowWrapper({ hasMetrics: true });
...@@ -499,11 +590,12 @@ describe('Dashboard', () => { ...@@ -499,11 +590,12 @@ describe('Dashboard', () => {
describe('Clipboard text in panels', () => { describe('Clipboard text in panels', () => {
const currentDashboard = 'TEST_DASHBOARD'; const currentDashboard = 'TEST_DASHBOARD';
const panelIndex = 1; // skip expanded panel
const getClipboardTextAt = i => const getClipboardTextFirstPanel = () =>
wrapper wrapper
.findAll(DashboardPanel) .findAll(DashboardPanel)
.at(i) .at(panelIndex)
.props('clipboardText'); .props('clipboardText');
beforeEach(() => { beforeEach(() => {
...@@ -515,18 +607,18 @@ describe('Dashboard', () => { ...@@ -515,18 +607,18 @@ describe('Dashboard', () => {
}); });
it('contains a link to the dashboard', () => { it('contains a link to the dashboard', () => {
expect(getClipboardTextAt(0)).toContain(`dashboard=${currentDashboard}`); expect(getClipboardTextFirstPanel()).toContain(`dashboard=${currentDashboard}`);
expect(getClipboardTextAt(0)).toContain(`group=`); expect(getClipboardTextFirstPanel()).toContain(`group=`);
expect(getClipboardTextAt(0)).toContain(`title=`); expect(getClipboardTextFirstPanel()).toContain(`title=`);
expect(getClipboardTextAt(0)).toContain(`y_label=`); expect(getClipboardTextFirstPanel()).toContain(`y_label=`);
}); });
it('strips the undefined parameter', () => { it('strips the undefined parameter', () => {
wrapper.setProps({ currentDashboard: undefined }); wrapper.setProps({ currentDashboard: undefined });
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(getClipboardTextAt(0)).not.toContain(`dashboard=`); expect(getClipboardTextFirstPanel()).not.toContain(`dashboard=`);
expect(getClipboardTextAt(0)).toContain(`y_label=`); expect(getClipboardTextFirstPanel()).toContain(`y_label=`);
}); });
}); });
...@@ -534,8 +626,8 @@ describe('Dashboard', () => { ...@@ -534,8 +626,8 @@ describe('Dashboard', () => {
wrapper.setProps({ currentDashboard: null }); wrapper.setProps({ currentDashboard: null });
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(getClipboardTextAt(0)).not.toContain(`dashboard=`); expect(getClipboardTextFirstPanel()).not.toContain(`dashboard=`);
expect(getClipboardTextAt(0)).toContain(`y_label=`); expect(getClipboardTextFirstPanel()).toContain(`y_label=`);
}); });
}); });
}); });
......
...@@ -20,6 +20,8 @@ import { ...@@ -20,6 +20,8 @@ import {
fetchPrometheusMetric, fetchPrometheusMetric,
setInitialState, setInitialState,
filterEnvironments, filterEnvironments,
setExpandedPanel,
clearExpandedPanel,
setGettingStartedEmptyState, setGettingStartedEmptyState,
duplicateSystemDashboard, duplicateSystemDashboard,
} from '~/monitoring/stores/actions'; } from '~/monitoring/stores/actions';
...@@ -870,4 +872,43 @@ describe('Monitoring store actions', () => { ...@@ -870,4 +872,43 @@ describe('Monitoring store actions', () => {
}); });
}); });
}); });
describe('setExpandedPanel', () => {
let state;
beforeEach(() => {
state = storeState();
});
it('Sets a panel as expanded', () => {
const group = 'group_1';
const panel = { title: 'A Panel' };
return testAction(
setExpandedPanel,
{ group, panel },
state,
[{ type: types.SET_EXPANDED_PANEL, payload: { group, panel } }],
[],
);
});
});
describe('clearExpandedPanel', () => {
let state;
beforeEach(() => {
state = storeState();
});
it('Clears a panel as expanded', () => {
return testAction(
clearExpandedPanel,
undefined,
state,
[{ type: types.SET_EXPANDED_PANEL, payload: { group: null, panel: null } }],
[],
);
});
});
}); });
...@@ -342,4 +342,26 @@ describe('Monitoring mutations', () => { ...@@ -342,4 +342,26 @@ describe('Monitoring mutations', () => {
expect(stateCopy.allDashboards).toEqual(dashboardGitResponse); expect(stateCopy.allDashboards).toEqual(dashboardGitResponse);
}); });
}); });
describe('SET_EXPANDED_PANEL', () => {
it('no expanded panel is set initally', () => {
expect(stateCopy.expandedPanel.panel).toEqual(null);
expect(stateCopy.expandedPanel.group).toEqual(null);
});
it('sets a panel id as the expanded panel', () => {
const group = 'group_1';
const panel = { title: 'A Panel' };
mutations[types.SET_EXPANDED_PANEL](stateCopy, { group, panel });
expect(stateCopy.expandedPanel).toEqual({ group, panel });
});
it('clears panel as the expanded panel', () => {
mutations[types.SET_EXPANDED_PANEL](stateCopy, { group: null, panel: null });
expect(stateCopy.expandedPanel.group).toEqual(null);
expect(stateCopy.expandedPanel.panel).toEqual(null);
});
});
}); });
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