Commit 2327dd84 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'resize-vuln-graph' into 'master'

Resize vuln graph

See merge request gitlab-org/gitlab-ee!10028
parents bdc3e121 6867dc78
...@@ -41,6 +41,9 @@ export default class ContextualSidebar { ...@@ -41,6 +41,9 @@ export default class ContextualSidebar {
this.toggleCollapsedSidebar(value, true); this.toggleCollapsedSidebar(value, true);
} }
}); });
this.$page.on('transitionstart transitionend', () => {
$(document).trigger('content.resize');
});
$(window).on('resize', () => _.debounce(this.render(), 100)); $(window).on('resize', () => _.debounce(this.render(), 100));
} }
......
<script>
import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
import $ from 'jquery';
export default {
data() {
return {
width: 0,
height: 0,
};
},
beforeDestroy() {
this.contentResizeHandler.off('content.resize', this.debouncedResize);
window.removeEventListener('resize', this.debouncedResize);
},
created() {
this.debouncedResize = debounceByAnimationFrame(this.onResize);
// Handle when we explicictly trigger a custom resize event
this.contentResizeHandler = $(document).on('content.resize', this.debouncedResize);
// Handle window resize
window.addEventListener('resize', this.debouncedResize);
},
methods: {
onResize() {
// Slot dimensions
const { clientWidth, clientHeight } = this.$refs.chartWrapper;
this.width = clientWidth;
this.height = clientHeight;
},
},
};
</script>
<template>
<div ref="chartWrapper">
<slot :width="width" :height="height"> </slot>
</div>
</template>
...@@ -7,6 +7,7 @@ import ChartTooltip from './vulnerability_chart_tooltip.vue'; ...@@ -7,6 +7,7 @@ import ChartTooltip from './vulnerability_chart_tooltip.vue';
import ChartButtons from './vulnerability_chart_buttons.vue'; import ChartButtons from './vulnerability_chart_buttons.vue';
import { DAY_IN_MS, DAYS } from '../store/modules/vulnerabilities/constants'; import { DAY_IN_MS, DAYS } from '../store/modules/vulnerabilities/constants';
import { SEVERITY_LEVELS } from '../store/constants'; import { SEVERITY_LEVELS } from '../store/constants';
import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue';
export default { export default {
name: 'VulnerabilityChart', name: 'VulnerabilityChart',
...@@ -14,6 +15,7 @@ export default { ...@@ -14,6 +15,7 @@ export default {
GlChart, GlChart,
ChartTooltip, ChartTooltip,
ChartButtons, ChartButtons,
ResizableChartContainer,
}, },
DAYS, DAYS,
data: () => ({ data: () => ({
...@@ -189,7 +191,15 @@ export default { ...@@ -189,7 +191,15 @@ export default {
<div class="vulnerabilities-chart"> <div class="vulnerabilities-chart">
<div class="vulnerabilities-chart-wrapper"> <div class="vulnerabilities-chart-wrapper">
<gl-chart :options="options" :disable-theme="true" /> <resizable-chart-container>
<gl-chart
slot-scope="{ width, height }"
:options="options"
:width="width"
:height="height"
:disable-theme="true"
/>
</resizable-chart-container>
<chart-tooltip <chart-tooltip
v-show="false" v-show="false"
ref="tooltip" ref="tooltip"
......
...@@ -2,22 +2,5 @@ $trans-white: rgba(255, 255, 255, 0); ...@@ -2,22 +2,5 @@ $trans-white: rgba(255, 255, 255, 0);
.vulnerabilities-chart-wrapper { .vulnerabilities-chart-wrapper {
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
overflow: auto;
} }
@media screen and (max-width: 1240px) {
.vulnerabilities-chart {
position: relative;
}
.vulnerabilities-chart::after {
background-image: linear-gradient(to right, $trans-white, $gl-gray-350);
bottom: 0;
content: '';
height: 305px;
position: absolute;
right: -1px;
top: 10px;
width: 32px;
}
}
---
title: Dynamically resize security group dashboard vuln graph
merge_request: 10028
author:
type: changed
import Vue from 'vue'; import Vue from 'vue';
import MockAdapater from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import component from 'ee/security_dashboard/components/vulnerability_chart.vue'; import component from 'ee/security_dashboard/components/vulnerability_chart.vue';
import createStore from 'ee/security_dashboard/store'; import createStore from 'ee/security_dashboard/store';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { mount } from '@vue/test-utils';
import waitForPromises from 'spec/helpers/wait_for_promises';
import { resetStore } from '../helpers'; import { resetStore } from '../helpers';
import mockDataVulnerabilitiesHistory from '../store/vulnerabilities/data/mock_data_vulnerabilities_history.json'; import mockDataVulnerabilitiesHistory from '../store/vulnerabilities/data/mock_data_vulnerabilities_history.json';
...@@ -15,26 +14,26 @@ describe('Vulnerabilities Chart', () => { ...@@ -15,26 +14,26 @@ describe('Vulnerabilities Chart', () => {
const vulnerabilitiesHistoryEndpoint = '/vulnerabilitiesEndpoint.json'; const vulnerabilitiesHistoryEndpoint = '/vulnerabilitiesEndpoint.json';
let store; let store;
let mock; let mock;
let vm; let wrapper;
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore();
store.state.vulnerabilities.vulnerabilitiesHistoryEndpoint = vulnerabilitiesHistoryEndpoint; store.state.vulnerabilities.vulnerabilitiesHistoryEndpoint = vulnerabilitiesHistoryEndpoint;
mock = new MockAdapater(axios); mock = new MockAdapter(axios);
mock.onGet(vulnerabilitiesHistoryEndpoint).replyOnce(200, mockDataVulnerabilitiesHistory); mock.onGet(vulnerabilitiesHistoryEndpoint).replyOnce(200, mockDataVulnerabilitiesHistory);
vm = mountComponentWithStore(Component, { store }); wrapper = mount(Component, { store, sync: false });
}); });
afterEach(() => { afterEach(() => {
resetStore(store); resetStore(store);
vm.$destroy(); wrapper.destroy();
mock.restore(); mock.restore();
}); });
it('should render the e-chart instance', done => { it('should render the e-chart instance', done => {
waitForPromises() Vue.nextTick()
.then(() => { .then(() => {
expect(vm.$el.querySelector('[_echarts_instance_]')).not.toBeNull(); expect(wrapper.find('[_echarts_instance_]')).not.toBeNull();
done(); done();
}) })
.catch(done.fail); .catch(done.fail);
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Resizable Chart Container renders the component 1`] = `
<div>
<div
class="slot"
>
<span
class="width"
>
0
</span>
<span
class="height"
>
0
</span>
</div>
</div>
`;
import Vue from 'vue';
import { mount } from '@vue/test-utils';
import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue';
import $ from 'jquery';
jest.mock('~/lib/utils/common_utils', () => ({
debounceByAnimationFrame(callback) {
return jest.spyOn({ callback }, 'callback');
},
}));
describe('Resizable Chart Container', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(ResizableChartContainer, {
attachToDocument: true,
scopedSlots: {
default: `
<div class="slot" slot-scope="{ width, height }">
<span class="width">{{width}}</span>
<span class="height">{{height}}</span>
</div>
`,
},
});
});
afterEach(() => {
wrapper.destroy();
});
it('renders the component', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('updates the slot width and height props', () => {
const width = 1920;
const height = 1080;
// JSDOM mocks and sets clientWidth/clientHeight to 0 so we set manually
wrapper.vm.$refs.chartWrapper = { clientWidth: width, clientHeight: height };
$(document).trigger('content.resize');
return Vue.nextTick().then(() => {
const widthNode = wrapper.find('.slot > .width');
const heightNode = wrapper.find('.slot > .height');
expect(parseInt(widthNode.text(), 10)).toEqual(width);
expect(parseInt(heightNode.text(), 10)).toEqual(height);
});
});
it('calls onResize on manual resize', () => {
$(document).trigger('content.resize');
expect(wrapper.vm.debouncedResize).toHaveBeenCalled();
});
it('calls onResize on page resize', () => {
window.dispatchEvent(new Event('resize'));
expect(wrapper.vm.debouncedResize).toHaveBeenCalled();
});
});
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