Commit a40f2549 authored by Paul Slaughter's avatar Paul Slaughter Committed by Enrique Alcántara

Decouple resizable_panel and collapsible_sidebar

- Adds resizable flag to resizable_panel to control the
  ability to resize or not. This is helpful when resizable is
  wrapping both the nav and the viewport. When the
  viewport is closed, we don't want to resize anything
- Removes unnecessary div in collapsible_sidebar
- Removes some unnecessary CSS setting width
  since this is managed by resizable_panel
parent f6dc8369
......@@ -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