Commit d816cb6c authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '204821-decouple-resizable-panel-and-collapsible-panel' into 'master'

Step 3 - Decouple resizeable_panel from collapsible_sidebar

See merge request gitlab-org/gitlab!32462
parents e6e22c22 a40f2549
......@@ -9,7 +9,7 @@ import CommitForm from './commit_sidebar/form.vue';
import IdeReview from './ide_review.vue';
import SuccessMessage from './commit_sidebar/success_message.vue';
import IdeProjectHeader from './ide_project_header.vue';
import { leftSidebarViews, LEFT_SIDEBAR_INIT_WIDTH } from '../constants';
import { leftSidebarViews, SIDEBAR_INIT_WIDTH } from '../constants';
export default {
components: {
......@@ -33,13 +33,13 @@ export default {
);
},
},
LEFT_SIDEBAR_INIT_WIDTH,
SIDEBAR_INIT_WIDTH,
};
</script>
<template>
<resizable-panel
:initial-width="$options.LEFT_SIDEBAR_INIT_WIDTH"
:initial-width="$options.SIDEBAR_INIT_WIDTH"
side="left"
class="multi-file-commit-panel flex-column"
>
......
......@@ -2,7 +2,6 @@
import { mapActions, mapState } from 'vuex';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import ResizablePanel from '../resizable_panel.vue';
import IdeSidebarNav from '../ide_sidebar_nav.vue';
export default {
......@@ -12,7 +11,6 @@ export default {
},
components: {
Icon,
ResizablePanel,
IdeSidebarNav,
},
props: {
......@@ -25,10 +23,6 @@ export default {
type: String,
required: true,
},
width: {
type: Number,
required: true,
},
},
computed: {
...mapState({
......@@ -75,25 +69,20 @@ export default {
:data-qa-selector="`ide_${side}_sidebar`"
class="multi-file-commit-panel ide-sidebar"
>
<resizable-panel
<div
v-show="isOpen"
:initial-width="width"
:min-size="width"
:class="`ide-${side}-sidebar-${currentView}`"
:side="side"
class="multi-file-commit-panel-inner"
>
<div class="h-100 d-flex flex-column align-items-stretch">
<div
v-for="tabView in aliveTabViews"
v-show="tabView.name === currentView"
:key="tabView.name"
class="flex-fill gl-overflow-hidden js-tab-view"
>
<component :is="tabView.component" />
</div>
<div
v-for="tabView in aliveTabViews"
v-show="tabView.name === currentView"
:key="tabView.name"
class="flex-fill gl-overflow-hidden js-tab-view gl-h-full"
>
<component :is="tabView.component" />
</div>
</resizable-panel>
</div>
<ide-sidebar-nav
:tabs="tabs"
:side="side"
......
......@@ -2,15 +2,20 @@
import { mapGetters, mapState } from 'vuex';
import { __ } from '~/locale';
import CollapsibleSidebar from './collapsible_sidebar.vue';
import { rightSidebarViews } from '../../constants';
import ResizablePanel from '../resizable_panel.vue';
import { rightSidebarViews, SIDEBAR_INIT_WIDTH, SIDEBAR_NAV_WIDTH } from '../../constants';
import PipelinesList from '../pipelines/list.vue';
import JobsDetail from '../jobs/detail.vue';
import Clientside from '../preview/clientside.vue';
// Need to add the width of the nav buttons since the resizable container contains those as well
const WIDTH = SIDEBAR_INIT_WIDTH + SIDEBAR_NAV_WIDTH;
export default {
name: 'RightPane',
components: {
CollapsibleSidebar,
ResizablePanel,
},
props: {
extensionTabs: {
......@@ -22,6 +27,7 @@ export default {
computed: {
...mapState(['currentMergeRequestId', 'clientsidePreviewEnabled']),
...mapGetters(['packageJson']),
...mapState('rightPane', ['isOpen']),
showLivePreview() {
return this.packageJson && this.clientsidePreviewEnabled;
},
......@@ -46,9 +52,18 @@ export default {
];
},
},
WIDTH,
};
</script>
<template>
<collapsible-sidebar :extension-tabs="rightExtensionTabs" side="right" :width="350" />
<resizable-panel
class="gl-display-flex gl-overflow-hidden"
side="right"
:initial-width="$options.WIDTH"
:min-size="$options.WIDTH"
:resizable="isOpen"
>
<collapsible-sidebar class="gl-w-full" :extension-tabs="rightExtensionTabs" side="right" />
</resizable-panel>
</template>
<script>
import { mapActions } from 'vuex';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import { DEFAULT_SIDEBAR_MIN_WIDTH } from '../constants';
import { SIDEBAR_MIN_WIDTH } from '../constants';
export default {
components: {
......@@ -15,12 +15,17 @@ export default {
minSize: {
type: Number,
required: false,
default: DEFAULT_SIDEBAR_MIN_WIDTH,
default: SIDEBAR_MIN_WIDTH,
},
side: {
type: String,
required: true,
},
resizable: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
......@@ -29,7 +34,7 @@ export default {
},
computed: {
panelStyle() {
if (!this.collapsed) {
if (this.resizable) {
return {
width: `${this.width}px`,
};
......@@ -46,9 +51,10 @@ export default {
</script>
<template>
<div :style="panelStyle">
<div class="gl-relative" :style="panelStyle">
<slot></slot>
<panel-resizer
v-show="resizable"
:size.sync="width"
:start-size="initialWidth"
:min-size="minSize"
......
......@@ -4,8 +4,9 @@ export const MAX_WINDOW_HEIGHT_COMPACT = 750;
export const MAX_TITLE_LENGTH = 50;
export const MAX_BODY_LENGTH = 72;
export const LEFT_SIDEBAR_INIT_WIDTH = 340;
export const DEFAULT_SIDEBAR_MIN_WIDTH = 340;
export const SIDEBAR_INIT_WIDTH = 340;
export const SIDEBAR_MIN_WIDTH = 340;
export const SIDEBAR_NAV_WIDTH = 60;
// File view modes
export const FILE_VIEW_MODE_EDITOR = 'editor';
......
......@@ -282,7 +282,6 @@ $ide-commit-header-height: 48px;
.multi-file-commit-panel {
display: flex;
position: relative;
width: 340px;
padding: 0;
background-color: var(--ide-background, $gray-light);
......@@ -874,13 +873,11 @@ $ide-commit-header-height: 48px;
}
.ide-sidebar {
width: auto;
min-width: 60px;
}
.ide-right-sidebar {
.multi-file-commit-panel-inner {
width: 350px;
padding: $grid-size 0;
background-color: var(--ide-highlight-background, $white);
border-right: 1px solid var(--ide-border-color, $white-dark);
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import ResizablePanel from '~/ide/components/resizable_panel.vue';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import { SIDE_LEFT, SIDE_RIGHT } from '~/ide/constants';
const TEST_WIDTH = 500;
const TEST_MIN_WIDTH = 400;
describe('~/ide/components/resizable_panel', () => {
const localVue = createLocalVue();
localVue.use(Vuex);
let wrapper;
let store;
beforeEach(() => {
store = new Vuex.Store({});
jest.spyOn(store, 'dispatch').mockImplementation();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const createComponent = (props = {}) => {
wrapper = shallowMount(ResizablePanel, {
propsData: {
initialWidth: TEST_WIDTH,
minSize: TEST_MIN_WIDTH,
side: SIDE_LEFT,
...props,
},
store,
localVue,
});
};
const findResizer = () => wrapper.find(PanelResizer);
const findInlineStyle = () => wrapper.element.style.cssText;
const createInlineStyle = width => `width: ${width}px;`;
describe.each`
props | showResizer | resizerSide | expectedStyle
${{ resizable: true, side: SIDE_LEFT }} | ${true} | ${SIDE_RIGHT} | ${createInlineStyle(TEST_WIDTH)}
${{ resizable: true, side: SIDE_RIGHT }} | ${true} | ${SIDE_LEFT} | ${createInlineStyle(TEST_WIDTH)}
${{ resizable: false, side: SIDE_LEFT }} | ${false} | ${SIDE_RIGHT} | ${''}
`('with props $props', ({ props, showResizer, resizerSide, expectedStyle }) => {
beforeEach(() => {
createComponent(props);
});
it(`show resizer is ${showResizer}`, () => {
const expectedDisplay = showResizer ? '' : 'none';
const resizer = findResizer();
expect(resizer.exists()).toBe(true);
expect(resizer.element.style.display).toBe(expectedDisplay);
});
it(`resizer side is '${resizerSide}'`, () => {
const resizer = findResizer();
expect(resizer.props('side')).toBe(resizerSide);
});
it(`has style '${expectedStyle}'`, () => {
expect(findInlineStyle()).toBe(expectedStyle);
});
});
describe('default', () => {
beforeEach(() => {
createComponent();
});
it('does not dispatch anything', () => {
expect(store.dispatch).not.toHaveBeenCalled();
});
it.each`
event | dispatchArgs
${'resize-start'} | ${['setResizingStatus', true]}
${'resize-end'} | ${['setResizingStatus', false]}
`('when resizer emits $event, dispatch $dispatchArgs', ({ event, dispatchArgs }) => {
const resizer = findResizer();
resizer.vm.$emit(event);
expect(store.dispatch).toHaveBeenCalledWith(...dispatchArgs);
});
it('renders resizer', () => {
const resizer = findResizer();
expect(resizer.props()).toMatchObject({
maxSize: window.innerWidth / 2,
minSize: TEST_MIN_WIDTH,
startSize: TEST_WIDTH,
});
});
it('when resizer emits update:size, changes inline width', () => {
const newSize = TEST_WIDTH - 100;
const resizer = findResizer();
resizer.vm.$emit('update:size', newSize);
return wrapper.vm.$nextTick().then(() => {
expect(findInlineStyle()).toBe(createInlineStyle(newSize));
});
});
});
});
......@@ -15,7 +15,7 @@ exports[`WebIDE runs 1`] = `
(jest: contents hidden)
</div>
<div
class="multi-file-commit-panel flex-column"
class="gl-relative multi-file-commit-panel flex-column"
style="width: 340px;"
>
<div
......
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