summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriel Santiago <adriel@gitlab.com>2019-01-23 18:06:40 -0500
committerAdriel Santiago <adriel@gitlab.com>2019-02-04 08:04:45 -0500
commitc974f4a82ee3cd4b5b73b95d66d99854a0e5d3bc (patch)
tree183122acce41af316c31515c5c7431e886db282b
parent2b0f4df0217b4a4aee53f964610d66ceedb68dca (diff)
downloadgitlab-ce-c974f4a82ee3cd4b5b73b95d66d99854a0e5d3bc.tar.gz
Handle window and container resize events
Resizes metrics graph on window and sidebard width changes
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js16
-rw-r--r--app/assets/javascripts/monitoring/components/charts/area.vue31
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue42
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js15
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js12
5 files changed, 86 insertions, 30 deletions
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index ae8b4b4d635..0ceff10a02a 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -221,6 +221,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
if the name does not exist this function will return `null`
otherwise it will return the value of the param key provided
diff --git a/app/assets/javascripts/monitoring/components/charts/area.vue b/app/assets/javascripts/monitoring/components/charts/area.vue
index 5ca561259b6..91004ca54a9 100644
--- a/app/assets/javascripts/monitoring/components/charts/area.vue
+++ b/app/assets/javascripts/monitoring/components/charts/area.vue
@@ -1,6 +1,9 @@
<script>
import { GlAreaChart } from '@gitlab/ui';
import dateFormat from 'dateformat';
+import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
+
+let debouncedResize;
export default {
components: {
@@ -26,12 +29,22 @@ export default {
);
},
},
+ containerWidth: {
+ type: Number,
+ required: true,
+ },
alertData: {
type: Object,
required: false,
default: () => ({}),
},
},
+ data() {
+ return {
+ width: 0,
+ height: 0,
+ };
+ },
computed: {
chartData() {
return this.graphData.queries.reduce((accumulator, query) => {
@@ -76,11 +89,26 @@ export default {
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: {
formatTooltipText(params) {
const [date, value] = params;
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>
@@ -92,11 +120,14 @@ export default {
<div class="prometheus-graph-widgets"><slot></slot></div>
</div>
<gl-area-chart
+ ref="areaChart"
v-bind="$attrs"
:data="chartData"
:option="chartOptions"
:format-tooltip-text="formatTooltipText"
:thresholds="alertData"
+ :width="width"
+ :height="height"
/>
</div>
</template>
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 0b4bb9cc686..76059cb602d 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -1,5 +1,4 @@
<script>
-import _ from 'underscore';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import Flash from '../../flash';
@@ -9,6 +8,9 @@ import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store';
+const sidebarAnimationDuration = 150;
+let sidebarMutationObserver;
+
export default {
components: {
MonitorAreaChart,
@@ -89,39 +91,29 @@ export default {
elWidth: 0,
};
},
- computed: {
- forceRedraw() {
- return this.elWidth;
- },
- },
created() {
this.service = new MonitoringService({
metricsEndpoint: this.metricsEndpoint,
deploymentEndpoint: this.deploymentEndpoint,
environmentsEndpoint: this.environmentsEndpoint,
});
- this.mutationObserverConfig = {
- attributes: true,
- childList: false,
- subtree: false,
- };
},
beforeDestroy() {
- window.removeEventListener('resize', this.resizeThrottled, false);
- this.sidebarMutationObserver.disconnect();
+ if (sidebarMutationObserver) {
+ sidebarMutationObserver.disconnect();
+ }
},
mounted() {
- this.resizeThrottled = _.debounce(this.resize, 100);
if (!this.hasMetrics) {
this.state = 'gettingStarted';
} else {
this.getGraphsData();
- window.addEventListener('resize', this.resizeThrottled, false);
-
- const sidebarEl = document.querySelector('.nav-sidebar');
- // The sidebar listener
- this.sidebarMutationObserver = new MutationObserver(this.resizeThrottled);
- this.sidebarMutationObserver.observe(sidebarEl, this.mutationObserverConfig);
+ sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
+ sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
+ attributes: true,
+ childList: false,
+ subtree: false,
+ });
}
},
methods: {
@@ -149,20 +141,21 @@ export default {
this.showEmptyState = false;
})
- .then(this.resize)
.catch(() => {
this.state = 'unableToConnect';
});
},
- resize() {
- this.elWidth = this.$el.clientWidth;
+ onSidebarMutation() {
+ setTimeout(() => {
+ this.elWidth = this.$el.clientWidth;
+ }, sidebarAnimationDuration);
},
},
};
</script>
<template>
- <div v-if="!showEmptyState" :key="forceRedraw" class="prometheus-graphs prepend-top-default">
+ <div v-if="!showEmptyState" class="prometheus-graphs prepend-top-default">
<div class="environments d-flex align-items-center">
{{ s__('Metrics|Environment') }}
<div class="dropdown prepend-left-10">
@@ -198,6 +191,7 @@ export default {
:key="graphIndex"
:graph-data="graphData"
:alert-data="getGraphAlerts(graphData.id)"
+ :container-width="elWidth"
group-id="monitor-area-chart"
/>
</graph-group>
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index e3fd9604474..3eff3f655ee 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -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', () => {
beforeEach(() => {
window.history.pushState({}, null, '?scope=all&p=2');
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index 565b87de248..97b9671c809 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -29,7 +29,7 @@ describe('Dashboard', () => {
beforeEach(() => {
setFixtures(`
<div class="prometheus-graphs"></div>
- <div class="nav-sidebar"></div>
+ <div class="layout-page"></div>
`);
DashboardComponent = Vue.extend(Dashboard);
});
@@ -164,16 +164,16 @@ describe('Dashboard', () => {
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({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showPanels: false },
});
- expect(component.forceRedraw).toEqual(0);
+ expect(component.elWidth).toEqual(0);
- const navSidebarEl = document.querySelector('.nav-sidebar');
- navSidebarEl.classList.add('nav-sidebar-collapsed');
+ const pageLayoutEl = document.querySelector('.layout-page');
+ pageLayoutEl.classList.add('page-with-icon-sidebar');
Vue.nextTick()
.then(() => {
@@ -181,7 +181,7 @@ describe('Dashboard', () => {
return Vue.nextTick();
})
.then(() => {
- expect(component.forceRedraw).toEqual(component.elWidth);
+ expect(component.elWidth).toEqual(pageLayoutEl.clientWidth);
done();
})
.catch(done.fail);