Skip to content
Snippets Groups Projects
Verified Commit a4357b8f authored by Adriel Santiago's avatar Adriel Santiago
Browse files

Handle window and container resize events

Resizes metrics graph on window and sidebard width changes
parent 84ac9253
No related branches found
No related tags found
1 merge request!9399EE Compatibility Port: CE Handle window and container resize events
Pipeline #46076534 passed
...@@ -220,6 +220,22 @@ export const scrollToElement = element => { ...@@ -220,6 +220,22 @@ export const scrollToElement = element => {
); );
}; };
/**
* Returns a function that can only be invoked once between
* each browser screen repaint.
* @param {Function} fn
*/
export const debounceByAnimationFrame = fn => {
let requestId;
return function debounced(...args) {
if (requestId) {
window.cancelAnimationFrame(requestId);
}
requestId = window.requestAnimationFrame(() => fn.apply(this, args));
};
};
/** /**
this will take in the `name` of the param you want to parse in the url this will take in the `name` of the param you want to parse in the url
if the name does not exist this function will return `null` if the name does not exist this function will return `null`
......
<script> <script>
import { GlAreaChart } from '@gitlab/ui'; import { GlAreaChart } from '@gitlab/ui';
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
let debouncedResize;
export default { export default {
components: { components: {
...@@ -26,12 +29,22 @@ export default { ...@@ -26,12 +29,22 @@ export default {
); );
}, },
}, },
containerWidth: {
type: Number,
required: true,
},
alertData: { alertData: {
type: Object, type: Object,
required: false, required: false,
default: () => ({}), default: () => ({}),
}, },
}, },
data() {
return {
width: 0,
height: 0,
};
},
computed: { computed: {
chartData() { chartData() {
return this.graphData.queries.reduce((accumulator, query) => { return this.graphData.queries.reduce((accumulator, query) => {
...@@ -76,11 +89,26 @@ export default { ...@@ -76,11 +89,26 @@ export default {
return `${this.graphData.y_label} (${query.unit})`; return `${this.graphData.y_label} (${query.unit})`;
}, },
}, },
watch: {
containerWidth: 'onResize',
},
beforeDestroy() {
window.removeEventListener('resize', debouncedResize);
},
created() {
debouncedResize = debounceByAnimationFrame(this.onResize);
window.addEventListener('resize', debouncedResize);
},
methods: { methods: {
formatTooltipText(params) { formatTooltipText(params) {
const [date, value] = params; const [date, value] = params;
return [dateFormat(date, 'dd mmm yyyy, h:MMtt'), value.toFixed(3)]; return [dateFormat(date, 'dd mmm yyyy, h:MMtt'), value.toFixed(3)];
}, },
onResize() {
const { width, height } = this.$refs.areaChart.$el.getBoundingClientRect();
this.width = width;
this.height = height;
},
}, },
}; };
</script> </script>
...@@ -92,11 +120,14 @@ export default { ...@@ -92,11 +120,14 @@ export default {
<div class="prometheus-graph-widgets"><slot></slot></div> <div class="prometheus-graph-widgets"><slot></slot></div>
</div> </div>
<gl-area-chart <gl-area-chart
ref="areaChart"
v-bind="$attrs" v-bind="$attrs"
:data="chartData" :data="chartData"
:option="chartOptions" :option="chartOptions"
:format-tooltip-text="formatTooltipText" :format-tooltip-text="formatTooltipText"
:thresholds="alertData" :thresholds="alertData"
:width="width"
:height="height"
/> />
</div> </div>
</template> </template>
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
// ee-only // ee-only
import DashboardMixin from 'ee/monitoring/components/dashboard_mixin'; import DashboardMixin from 'ee/monitoring/components/dashboard_mixin';
import _ from 'underscore';
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 Flash from '../../flash'; import Flash from '../../flash';
...@@ -12,6 +11,9 @@ import GraphGroup from './graph_group.vue'; ...@@ -12,6 +11,9 @@ import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store'; import MonitoringStore from '../stores/monitoring_store';
const sidebarAnimationDuration = 150;
let sidebarMutationObserver;
export default { export default {
components: { components: {
MonitorAreaChart, MonitorAreaChart,
...@@ -101,29 +103,19 @@ export default { ...@@ -101,29 +103,19 @@ export default {
elWidth: 0, elWidth: 0,
}; };
}, },
computed: {
forceRedraw() {
return this.elWidth;
},
},
created() { created() {
this.service = new MonitoringService({ this.service = new MonitoringService({
metricsEndpoint: this.metricsEndpoint, metricsEndpoint: this.metricsEndpoint,
deploymentEndpoint: this.deploymentEndpoint, deploymentEndpoint: this.deploymentEndpoint,
environmentsEndpoint: this.environmentsEndpoint, environmentsEndpoint: this.environmentsEndpoint,
}); });
this.mutationObserverConfig = {
attributes: true,
childList: false,
subtree: false,
};
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('resize', this.resizeThrottled, false); if (sidebarMutationObserver) {
this.sidebarMutationObserver.disconnect(); sidebarMutationObserver.disconnect();
}
}, },
mounted() { mounted() {
this.resizeThrottled = _.debounce(this.resize, 100);
this.servicePromises = [ this.servicePromises = [
this.service this.service
.getGraphsData() .getGraphsData()
...@@ -148,12 +140,12 @@ export default { ...@@ -148,12 +140,12 @@ export default {
); );
} }
this.getGraphsData(); this.getGraphsData();
window.addEventListener('resize', this.resizeThrottled, false); sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
const sidebarEl = document.querySelector('.nav-sidebar'); attributes: true,
// The sidebar listener childList: false,
this.sidebarMutationObserver = new MutationObserver(this.resizeThrottled); subtree: false,
this.sidebarMutationObserver.observe(sidebarEl, this.mutationObserverConfig); });
} }
}, },
methods: { methods: {
...@@ -171,20 +163,21 @@ export default { ...@@ -171,20 +163,21 @@ export default {
this.showEmptyState = false; this.showEmptyState = false;
}) })
.then(this.resize)
.catch(() => { .catch(() => {
this.state = 'unableToConnect'; this.state = 'unableToConnect';
}); });
}, },
resize() { onSidebarMutation() {
this.elWidth = this.$el.clientWidth; setTimeout(() => {
this.elWidth = this.$el.clientWidth;
}, sidebarAnimationDuration);
}, },
}, },
}; };
</script> </script>
<template> <template>
<div v-if="!showEmptyState" :key="forceRedraw" class="prometheus-graphs prepend-top-default"> <div v-if="!showEmptyState" class="prometheus-graphs prepend-top-default">
<div v-if="showEnvironmentDropdown" class="environments d-flex align-items-center"> <div v-if="showEnvironmentDropdown" class="environments d-flex align-items-center">
{{ s__('Metrics|Environment') }} {{ s__('Metrics|Environment') }}
<div class="dropdown prepend-left-10"> <div class="dropdown prepend-left-10">
...@@ -220,6 +213,7 @@ export default { ...@@ -220,6 +213,7 @@ export default {
:key="graphIndex" :key="graphIndex"
:graph-data="graphData" :graph-data="graphData"
:alert-data="getGraphAlerts(graphData.id)" :alert-data="getGraphAlerts(graphData.id)"
:container-width="elWidth"
group-id="monitor-area-chart" group-id="monitor-area-chart"
> >
<!-- EE content --> <!-- EE content -->
......
...@@ -232,6 +232,21 @@ describe('common_utils', () => { ...@@ -232,6 +232,21 @@ describe('common_utils', () => {
}); });
}); });
describe('debounceByAnimationFrame', () => {
it('debounces a function to allow a maximum of one call per animation frame', done => {
const spy = jasmine.createSpy('spy');
const debouncedSpy = commonUtils.debounceByAnimationFrame(spy);
window.requestAnimationFrame(() => {
debouncedSpy();
debouncedSpy();
window.requestAnimationFrame(() => {
expect(spy).toHaveBeenCalledTimes(1);
done();
});
});
});
});
describe('getParameterByName', () => { describe('getParameterByName', () => {
beforeEach(() => { beforeEach(() => {
window.history.pushState({}, null, '?scope=all&p=2'); window.history.pushState({}, null, '?scope=all&p=2');
......
...@@ -29,7 +29,7 @@ describe('Dashboard', () => { ...@@ -29,7 +29,7 @@ describe('Dashboard', () => {
beforeEach(() => { beforeEach(() => {
setFixtures(` setFixtures(`
<div class="prometheus-graphs"></div> <div class="prometheus-graphs"></div>
<div class="nav-sidebar"></div> <div class="layout-page"></div>
`); `);
DashboardComponent = Vue.extend(Dashboard); DashboardComponent = Vue.extend(Dashboard);
}); });
...@@ -183,16 +183,16 @@ describe('Dashboard', () => { ...@@ -183,16 +183,16 @@ describe('Dashboard', () => {
jasmine.clock().uninstall(); jasmine.clock().uninstall();
}); });
it('rerenders the dashboard when the sidebar is resized', done => { it('sets elWidth to page width when the sidebar is resized', done => {
const component = new DashboardComponent({ const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'), el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showPanels: false }, propsData: { ...propsData, hasMetrics: true, showPanels: false },
}); });
expect(component.forceRedraw).toEqual(0); expect(component.elWidth).toEqual(0);
const navSidebarEl = document.querySelector('.nav-sidebar'); const pageLayoutEl = document.querySelector('.layout-page');
navSidebarEl.classList.add('nav-sidebar-collapsed'); pageLayoutEl.classList.add('page-with-icon-sidebar');
Vue.nextTick() Vue.nextTick()
.then(() => { .then(() => {
...@@ -200,7 +200,7 @@ describe('Dashboard', () => { ...@@ -200,7 +200,7 @@ describe('Dashboard', () => {
return Vue.nextTick(); return Vue.nextTick();
}) })
.then(() => { .then(() => {
expect(component.forceRedraw).toEqual(component.elWidth); expect(component.elWidth).toEqual(pageLayoutEl.clientWidth);
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment