Commit 51b45ac6 authored by Fatih Acet's avatar Fatih Acet

Merge branch '27835-move-and-resize-panels-in-dashboard-save-to-branch' into 'master'

Add property to enable metrics dashboards to be rearranged

See merge request gitlab-org/gitlab!16605
parents 88972e49 d0e486fb
<script> <script>
import _ from 'underscore';
import { mapActions, mapState } from 'vuex';
import VueDraggable from 'vuedraggable';
import { import {
GlButton, GlButton,
GlDropdown, GlDropdown,
...@@ -8,8 +11,6 @@ import { ...@@ -8,8 +11,6 @@ import {
GlModalDirective, GlModalDirective,
GlTooltipDirective, GlTooltipDirective,
} from '@gitlab/ui'; } from '@gitlab/ui';
import _ from 'underscore';
import { mapActions, mapState } from 'vuex';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { getParameterValues, mergeUrlParams } from '~/lib/utils/url_utility'; import { getParameterValues, mergeUrlParams } from '~/lib/utils/url_utility';
...@@ -26,6 +27,7 @@ let sidebarMutationObserver; ...@@ -26,6 +27,7 @@ let sidebarMutationObserver;
export default { export default {
components: { components: {
VueDraggable,
MonitorTimeSeriesChart, MonitorTimeSeriesChart,
MonitorSingleStatChart, MonitorSingleStatChart,
PanelType, PanelType,
...@@ -151,6 +153,11 @@ export default { ...@@ -151,6 +153,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
rearrangePanelsAvailable: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -160,6 +167,7 @@ export default { ...@@ -160,6 +167,7 @@ export default {
selectedTimeWindowKey: '', selectedTimeWindowKey: '',
formIsValid: null, formIsValid: null,
timeWindows: {}, timeWindows: {},
isRearrangingPanels: false,
}; };
}, },
computed: { computed: {
...@@ -183,6 +191,9 @@ export default { ...@@ -183,6 +191,9 @@ export default {
selectedDashboardText() { selectedDashboardText() {
return this.currentDashboard || this.firstDashboard.display_name; return this.currentDashboard || this.firstDashboard.display_name;
}, },
showRearrangePanelsBtn() {
return !this.showEmptyState && this.rearrangePanelsAvailable;
},
addingMetricsAvailable() { addingMetricsAvailable() {
return IS_EE && this.canAddMetrics && !this.showEmptyState; return IS_EE && this.canAddMetrics && !this.showEmptyState;
}, },
...@@ -274,6 +285,11 @@ export default { ...@@ -274,6 +285,11 @@ export default {
this.$toast.show(__('Link copied')); this.$toast.show(__('Link copied'));
}, },
// TODO: END // TODO: END
removeGraph(metrics, graphIndex) {
// At present graphs will not be removed, they should removed using the vuex store
// See https://gitlab.com/gitlab-org/gitlab/issues/27835
metrics.splice(graphIndex, 1);
},
generateLink(group, title, yLabel) { generateLink(group, title, yLabel) {
const dashboard = this.currentDashboard || this.firstDashboard.path; const dashboard = this.currentDashboard || this.firstDashboard.path;
const params = _.pick({ dashboard, group, title, y_label: yLabel }, value => value != null); const params = _.pick({ dashboard, group, title, y_label: yLabel }, value => value != null);
...@@ -287,6 +303,9 @@ export default { ...@@ -287,6 +303,9 @@ export default {
this.elWidth = this.$el.clientWidth; this.elWidth = this.$el.clientWidth;
}, sidebarAnimationDuration); }, sidebarAnimationDuration);
}, },
toggleRearrangingPanels() {
this.isRearrangingPanels = !this.isRearrangingPanels;
},
setFormValidity(isValid) { setFormValidity(isValid) {
this.formIsValid = isValid; this.formIsValid = isValid;
}, },
...@@ -389,15 +408,27 @@ export default { ...@@ -389,15 +408,27 @@ export default {
</template> </template>
<gl-form-group <gl-form-group
v-if="addingMetricsAvailable || externalDashboardUrl.length" v-if="addingMetricsAvailable || showRearrangePanelsBtn || externalDashboardUrl.length"
label-for="prometheus-graphs-dropdown-buttons" label-for="prometheus-graphs-dropdown-buttons"
class="dropdown-buttons col-lg d-lg-flex align-items-end" class="dropdown-buttons col-lg d-lg-flex align-items-end"
> >
<div id="prometheus-graphs-dropdown-buttons"> <div id="prometheus-graphs-dropdown-buttons">
<gl-button
v-if="showRearrangePanelsBtn"
:pressed="isRearrangingPanels"
new-style
variant="default"
class="mr-2 mt-1 js-rearrange-button"
@click="toggleRearrangingPanels"
>
{{ __('Arrange charts') }}
</gl-button>
<gl-button <gl-button
v-if="addingMetricsAvailable" v-if="addingMetricsAvailable"
v-gl-modal="$options.addMetric.modalId" v-gl-modal="$options.addMetric.modalId"
class="mr-2 mt-1 js-add-metric-button text-success border-success" new-style
variant="outline-success"
class="mr-2 mt-1 js-add-metric-button"
> >
{{ $options.addMetric.title }} {{ $options.addMetric.title }}
</gl-button> </gl-button>
...@@ -451,17 +482,42 @@ export default { ...@@ -451,17 +482,42 @@ export default {
:collapse-group="groupHasData(groupData)" :collapse-group="groupHasData(groupData)"
> >
<template v-if="additionalPanelTypesEnabled"> <template v-if="additionalPanelTypesEnabled">
<panel-type <vue-draggable
v-for="(graphData, graphIndex) in groupData.metrics" :list="groupData.metrics"
:key="`panel-type-${graphIndex}`" group="metrics-dashboard"
class="col-12 col-lg-6 pb-3" :component-data="{ attrs: { class: 'row mx-0 w-100' } }"
:clipboard-text="generateLink(groupData.group, graphData.title, graphData.y_label)" :disabled="!isRearrangingPanels"
:graph-data="graphData" >
:dashboard-width="elWidth" <div
:alerts-endpoint="alertsEndpoint" v-for="(graphData, graphIndex) in groupData.metrics"
:prometheus-alerts-available="prometheusAlertsAvailable" :key="`panel-type-${graphIndex}`"
:index="`${index}-${graphIndex}`" class="col-12 col-lg-6 px-2 mb-2 draggable"
/> :class="{ 'draggable-enabled': isRearrangingPanels }"
>
<div class="position-relative draggable-panel js-draggable-panel">
<div
v-if="isRearrangingPanels"
class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end"
@click="removeGraph(groupData.metrics, graphIndex)"
>
<a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')"
><icon name="close"
/></a>
</div>
<panel-type
:clipboard-text="
generateLink(groupData.group, graphData.title, graphData.y_label)
"
:graph-data="graphData"
:dashboard-width="elWidth"
:alerts-endpoint="alertsEndpoint"
:prometheus-alerts-available="prometheusAlertsAvailable"
:index="`${index}-${graphIndex}`"
/>
</div>
</div>
</vue-draggable>
</template> </template>
<template v-else> <template v-else>
<monitor-time-series-chart <monitor-time-series-chart
......
...@@ -52,7 +52,7 @@ export default { ...@@ -52,7 +52,7 @@ export default {
<div <div
v-if="collapseGroup" v-if="collapseGroup"
v-show="collapseGroup && showGroup" v-show="collapseGroup && showGroup"
class="card-body prometheus-graph-group" class="card-body prometheus-graph-group p-0"
> >
<slot></slot> <slot></slot>
</div> </div>
......
...@@ -15,6 +15,37 @@ ...@@ -15,6 +15,37 @@
} }
} }
.draggable {
&.draggable-enabled {
.draggable-panel {
border: $gray-200 1px solid;
border-radius: $border-radius-default;
margin: -1px;
cursor: grab;
}
.prometheus-graph {
// Make dragging easier by disabling use of chart
pointer-events: none;
}
}
&.sortable-chosen .draggable-panel {
background: $white-light;
box-shadow: 0 0 4px $gray-500;
}
.draggable-remove {
z-index: 1;
.draggable-remove-link {
cursor: pointer;
color: $gray-600;
background-color: $white-light;
}
}
}
.prometheus-panel { .prometheus-panel {
margin-top: 20px; margin-top: 20px;
} }
...@@ -22,11 +53,11 @@ ...@@ -22,11 +53,11 @@
.prometheus-graph-group { .prometheus-graph-group {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
padding: $gl-padding / 2; margin-top: $gl-padding-8;
} }
.prometheus-graph { .prometheus-graph {
padding: $gl-padding / 2; padding: $gl-padding-8;
} }
.prometheus-graph-embed { .prometheus-graph-embed {
......
---
title: Add property to enable metrics dashboards to be rearranged
merge_request: 16605
author:
type: changed
...@@ -1889,6 +1889,9 @@ msgstr "" ...@@ -1889,6 +1889,9 @@ msgstr ""
msgid "Are you sure? This will invalidate your registered applications and U2F devices." msgid "Are you sure? This will invalidate your registered applications and U2F devices."
msgstr "" msgstr ""
msgid "Arrange charts"
msgstr ""
msgid "Artifact ID" msgid "Artifact ID"
msgstr "" msgstr ""
......
import Vue from 'vue'; import Vue from 'vue';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlToast } from '@gitlab/ui'; import { GlToast } from '@gitlab/ui';
import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue'; import Dashboard from '~/monitoring/components/dashboard.vue';
import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants'; import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
...@@ -51,11 +52,6 @@ describe('Dashboard', () => { ...@@ -51,11 +52,6 @@ describe('Dashboard', () => {
<div class="layout-page"></div> <div class="layout-page"></div>
`); `);
window.gon = {
...window.gon,
ee: false,
};
store = createStore(); store = createStore();
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
DashboardComponent = Vue.extend(Dashboard); DashboardComponent = Vue.extend(Dashboard);
...@@ -378,7 +374,101 @@ describe('Dashboard', () => { ...@@ -378,7 +374,101 @@ describe('Dashboard', () => {
}); });
}); });
// https://gitlab.com/gitlab-org/gitlab-foss/issues/66922 describe('drag and drop function', () => {
let wrapper;
let expectedPanelCount; // also called metrics, naming to be improved: https://gitlab.com/gitlab-org/gitlab/issues/31565
const findDraggables = () => wrapper.findAll(VueDraggable);
const findEnabledDraggables = () => findDraggables().filter(f => !f.attributes('disabled'));
const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel');
const findRearrangeButton = () => wrapper.find('.js-rearrange-button');
beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
expectedPanelCount = metricsGroupsAPIResponse.data.reduce(
(acc, d) => d.metrics.length + acc,
0,
);
store.dispatch('monitoringDashboard/setFeatureFlags', { additionalPanelTypesEnabled: true });
wrapper = shallowMount(DashboardComponent, {
localVue,
sync: false,
propsData: { ...propsData, hasMetrics: true },
store,
});
// not using $nextTicket becuase we must wait for the dashboard
// to be populated with the mock data results.
setTimeout(done);
});
it('wraps vuedraggable', () => {
expect(findDraggablePanels().exists()).toBe(true);
expect(findDraggablePanels().length).toEqual(expectedPanelCount);
});
it('is disabled by default', () => {
expect(findRearrangeButton().exists()).toBe(false);
expect(findEnabledDraggables().length).toBe(0);
});
describe('when rearrange is enabled', () => {
beforeEach(done => {
wrapper.setProps({ rearrangePanelsAvailable: true });
wrapper.vm.$nextTick(done);
});
it('displays rearrange button', () => {
expect(findRearrangeButton().exists()).toBe(true);
});
describe('when rearrange button is clicked', () => {
const findFirstDraggableRemoveButton = () =>
findDraggablePanels()
.at(0)
.find('.js-draggable-remove');
beforeEach(done => {
findRearrangeButton().vm.$emit('click');
wrapper.vm.$nextTick(done);
});
it('it enables draggables', () => {
expect(findRearrangeButton().attributes('pressed')).toBeTruthy();
expect(findEnabledDraggables()).toEqual(findDraggables());
});
it('shows a remove button, which removes a panel', done => {
expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false);
expect(findDraggablePanels().length).toEqual(expectedPanelCount);
findFirstDraggableRemoveButton().trigger('click');
wrapper.vm.$nextTick(() => {
// At present graphs will not be removed in backend
// See https://gitlab.com/gitlab-org/gitlab/issues/27835
expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1);
done();
});
});
it('it disables draggables when clicked again', done => {
findRearrangeButton().vm.$emit('click');
wrapper.vm.$nextTick(() => {
expect(findRearrangeButton().attributes('pressed')).toBeFalsy();
expect(findEnabledDraggables().length).toBe(0);
done();
});
});
});
});
afterEach(() => {
wrapper.destroy();
});
});
// https://gitlab.com/gitlab-org/gitlab-ce/issues/66922
// eslint-disable-next-line jasmine/no-disabled-tests // eslint-disable-next-line jasmine/no-disabled-tests
xdescribe('link to chart', () => { xdescribe('link to chart', () => {
let wrapper; let wrapper;
......
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