diff options
Diffstat (limited to 'app/assets/javascripts/monitoring/components/dashboard.vue')
-rw-r--r-- | app/assets/javascripts/monitoring/components/dashboard.vue | 121 |
1 files changed, 112 insertions, 9 deletions
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index f54319d283e..bde62275797 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -1,6 +1,7 @@ <script> import { mapActions, mapState, mapGetters } from 'vuex'; import VueDraggable from 'vuedraggable'; +import Mousetrap from 'mousetrap'; import { GlIcon, GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui'; import DashboardHeader from './dashboard_header.vue'; import DashboardPanel from './dashboard_panel.vue'; @@ -24,7 +25,7 @@ import { expandedPanelPayloadFromUrl, convertVariablesForURL, } from '../utils'; -import { metricStates } from '../constants'; +import { metricStates, keyboardShortcutKeys } from '../constants'; import { defaultTimeRange } from '~/vue_shared/constants'; export default { @@ -71,6 +72,10 @@ export default { type: String, required: true, }, + addDashboardDocumentationPath: { + type: String, + required: true, + }, settingsPath: { type: String, required: true, @@ -149,21 +154,25 @@ export default { selectedTimeRange: timeRangeFromUrl() || defaultTimeRange, isRearrangingPanels: false, originalDocumentTitle: document.title, + hoveredPanel: '', }; }, computed: { ...mapState('monitoringDashboard', [ 'dashboard', 'emptyState', - 'showEmptyState', 'expandedPanel', 'variables', 'links', 'currentDashboard', + 'hasDashboardValidationWarnings', ]), ...mapGetters('monitoringDashboard', ['selectedDashboard', 'getMetricStates']), + shouldShowEmptyState() { + return Boolean(this.emptyState); + }, shouldShowVariablesSection() { - return Object.keys(this.variables).length > 0; + return Boolean(this.variables.length); }, shouldShowLinksSection() { return Object.keys(this.links).length > 0; @@ -197,12 +206,29 @@ export default { selectedDashboard(dashboard) { this.prependToDocumentTitle(dashboard?.display_name); }, + hasDashboardValidationWarnings(hasWarnings) { + /** + * This watcher is set for future SPA behaviour of the dashboard + */ + if (hasWarnings) { + createFlash( + s__( + 'Metrics|Your dashboard schema is invalid. Edit the dashboard to correct the YAML schema.', + ), + 'warning', + ); + } + }, }, created() { window.addEventListener('keyup', this.onKeyup); + + Mousetrap.bind(Object.values(keyboardShortcutKeys), this.runShortcut); }, destroyed() { window.removeEventListener('keyup', this.onKeyup); + + Mousetrap.unbind(Object.values(keyboardShortcutKeys)); }, mounted() { if (!this.hasMetrics) { @@ -254,6 +280,14 @@ export default { return null; }, /** + * Return true if the entire group is loading. + * @param {String} groupKey - Identifier for group + * @returns {boolean} + */ + isGroupLoading(groupKey) { + return this.groupSingleEmptyState(groupKey) === metricStates.LOADING; + }, + /** * A group should be not collapsed if any metric is loaded (OK) * * @param {String} groupKey - Identifier for group @@ -302,6 +336,66 @@ export default { // As a fallback, switch to default time range instead this.selectedTimeRange = defaultTimeRange; }, + isPanelHalfWidth(panelIndex, totalPanels) { + /** + * A single panel on a row should take the full width of its parent. + * All others should have half the width their parent. + */ + const isNumberOfPanelsEven = totalPanels % 2 === 0; + const isLastPanel = panelIndex === totalPanels - 1; + + return isNumberOfPanelsEven || !isLastPanel; + }, + /** + * TODO: Investigate this to utilize the eventBus from Vue + * The intentation behind this cleanup is to allow for better tests + * as well as use the correct eventBus facilities that are compatible + * with Vue 3 + * https://gitlab.com/gitlab-org/gitlab/-/issues/225583 + */ + // + runShortcut(e) { + const panel = this.$refs[this.hoveredPanel]; + + if (!panel) return; + + const [panelInstance] = panel; + let actionToRun = ''; + + switch (e.key) { + case keyboardShortcutKeys.EXPAND: + actionToRun = 'onExpandFromKeyboardShortcut'; + break; + + case keyboardShortcutKeys.VISIT_LOGS: + actionToRun = 'visitLogsPageFromKeyboardShortcut'; + break; + + case keyboardShortcutKeys.SHOW_ALERT: + actionToRun = 'showAlertModalFromKeyboardShortcut'; + break; + + case keyboardShortcutKeys.DOWNLOAD_CSV: + actionToRun = 'downloadCsvFromKeyboardShortcut'; + break; + + case keyboardShortcutKeys.CHART_COPY: + actionToRun = 'copyChartLinkFromKeyboardShotcut'; + break; + + default: + actionToRun = 'onExpandFromKeyboardShortcut'; + break; + } + + panelInstance[actionToRun](); + }, + setHoveredPanel(groupKey, graphIndex) { + this.hoveredPanel = `dashboard-panel-${groupKey}-${graphIndex}`; + }, + clearHoveredPanel() { + this.hoveredPanel = ''; + }, }, i18n: { goBackLabel: s__('Metrics|Go back (Esc)'), @@ -315,6 +409,7 @@ export default { v-if="showHeader" ref="prometheusGraphsHeader" class="prometheus-graphs-header d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 border-bottom bg-gray-light" + :add-dashboard-documentation-path="addDashboardDocumentationPath" :default-branch="defaultBranch" :rearrange-panels-available="rearrangePanelsAvailable" :custom-metrics-available="customMetricsAvailable" @@ -327,9 +422,9 @@ export default { @dateTimePickerInvalid="onDateTimePickerInvalid" @setRearrangingPanels="onSetRearrangingPanels" /> - <variables-section v-if="shouldShowVariablesSection && !showEmptyState" /> - <links-section v-if="shouldShowLinksSection && !showEmptyState" /> - <div v-if="!showEmptyState"> + <template v-if="!shouldShowEmptyState"> + <variables-section v-if="shouldShowVariablesSection" /> + <links-section v-if="shouldShowLinksSection" /> <dashboard-panel v-show="expandedPanel.panel" ref="expandedPanel" @@ -364,6 +459,7 @@ export default { :key="`${groupData.group}.${groupData.priority}`" :name="groupData.group" :show-panels="showPanels" + :is-loading="isGroupLoading(groupData.key)" :collapse-group="collapseGroup(groupData.key)" > <vue-draggable @@ -377,8 +473,14 @@ export default { <div v-for="(graphData, graphIndex) in groupData.panels" :key="`dashboard-panel-${graphIndex}`" - class="col-12 col-lg-6 px-2 mb-2 draggable" - :class="{ 'draggable-enabled': isRearrangingPanels }" + data-testid="dashboard-panel-layout-wrapper" + class="col-12 px-2 mb-2 draggable" + :class="{ + 'draggable-enabled': isRearrangingPanels, + 'col-lg-6': isPanelHalfWidth(graphIndex, groupData.panels.length), + }" + @mouseover="setHoveredPanel(groupData.key, graphIndex)" + @mouseout="clearHoveredPanel" > <div class="position-relative draggable-panel js-draggable-panel"> <div @@ -392,6 +494,7 @@ export default { </div> <dashboard-panel + :ref="`dashboard-panel-${groupData.key}-${graphIndex}`" :settings-path="settingsPath" :clipboard-text="generatePanelUrl(groupData.group, graphData)" :graph-data="graphData" @@ -414,7 +517,7 @@ export default { </div> </graph-group> </div> - </div> + </template> <empty-state v-else :selected-state="emptyState" |