Skip to content
Snippets Groups Projects
Commit 52e7aba1 authored by Jack Reed's avatar Jack Reed
Browse files

Enables nice removal of windows fixes #2366

parent 22f1f7f0
Branches
Tags
No related merge requests found
......@@ -39,6 +39,34 @@ describe('WorkspaceMosaic', () => {
expect(updateWorkspaceMosaicLayout).toHaveBeenCalled();
});
it('updates the workspace layout when windows are removed', () => {
const updateWorkspaceMosaicLayout = jest.fn();
wrapper = createWrapper({
updateWorkspaceMosaicLayout,
windows,
workspace: {
layout: { first: 1, second: 2 },
},
});
wrapper.instance().windowPaths = { 2: ['second'] };
wrapper.setProps({ windows: { 1: { id: 1 } } });
expect(updateWorkspaceMosaicLayout).toHaveBeenLastCalledWith(1);
});
it('when no windows remain', () => {
const updateWorkspaceMosaicLayout = jest.fn();
wrapper = createWrapper({
updateWorkspaceMosaicLayout,
windows,
});
wrapper.setProps({ windows: {} });
expect(updateWorkspaceMosaicLayout).toHaveBeenLastCalledWith({});
});
});
describe('bookkeepPath', () => {
it('as windows are rendered keeps a reference to their path in binary tree', () => {
wrapper.instance().tileRenderer('1', 'foo');
expect(wrapper.instance().windowPaths).toEqual({ 1: 'foo' });
});
});
describe('determineWorkspaceLayout', () => {
it('when window ids do not match workspace layout', () => {
......@@ -47,9 +75,9 @@ describe('WorkspaceMosaic', () => {
direction: 'row', first: '1', second: '2',
});
});
it('when there are no windows', () => {
it('by default use workspace.layout', () => {
wrapper = createWrapper({ windows: {}, workspace: { layout: 'foo' } });
expect(wrapper.instance().determineWorkspaceLayout()).toBeNull();
expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo');
});
it('when window ids match workspace layout', () => {
wrapper = createWrapper({ windows: { foo: { id: 'foo' } }, workspace: { layout: 'foo' } });
......
......@@ -3,7 +3,9 @@ import PropTypes from 'prop-types';
import {
Mosaic, MosaicWindow, getLeaves, createBalancedTreeFromLeaves,
} from 'react-mosaic-component';
import { createRemoveUpdate, updateTree } from 'react-mosaic-component/lib/util/mosaicUpdates';
import 'react-mosaic-component/react-mosaic-component.css';
import difference from 'lodash/difference';
import MosaicRenderPreview from '../containers/MosaicRenderPreview';
import Window from '../containers/Window';
......@@ -22,6 +24,7 @@ export class WorkspaceMosaic extends React.Component {
this.mosaicChange = this.mosaicChange.bind(this);
this.determineWorkspaceLayout = this.determineWorkspaceLayout.bind(this);
this.zeroStateView = <div />;
this.windowPaths = {};
}
/** */
......@@ -35,28 +38,58 @@ export class WorkspaceMosaic extends React.Component {
/** */
componentDidUpdate(prevProps) {
const { windows, workspace, updateWorkspaceMosaicLayout } = this.props;
if (prevProps.windows !== windows || prevProps.workspace !== workspace) {
const prevWindows = Object.keys(prevProps.windows);
const currentWindows = Object.keys(windows);
// Handles when Windows are removed from the state
if (!prevWindows.every(e => currentWindows.includes(e))) {
// There are no more remaining Windows, just return an empty layout
if (currentWindows.length === 0) {
updateWorkspaceMosaicLayout({});
return;
}
// Generate a set of "removeUpdates" to update layout binary tree
const removedWindows = difference(prevWindows, currentWindows);
const removeUpdates = removedWindows
.map(windowId => (
createRemoveUpdate(workspace.layout, this.windowPaths[windowId])
));
const newTree = updateTree(workspace.layout, removeUpdates);
updateWorkspaceMosaicLayout(newTree);
}
// Handles when Windows are added (not via Add Resource UI)
// TODO: If a window is added, add it in a better way #2380
if (!currentWindows.every(e => prevWindows.includes(e))) {
const newLayout = this.determineWorkspaceLayout();
if (newLayout !== workspace.layout) updateWorkspaceMosaicLayout(newLayout);
}
}
/**
* bookkeepPath - used to book keep Window's path's
* @param {String} windowId [description]
* @param {Array} path [description]
*/
bookkeepPath(windowId, path) {
this.windowPaths[windowId] = path;
}
/**
* Used to determine whether or not a "new" layout should be autogenerated.
* If a Window is added or removed, generate that new layout and use that for
* this render. When the Mosaic changes, that will trigger a new store update.
* TODO: If a window is added, add it in a better way #2380
*/
determineWorkspaceLayout() {
const { windows, workspace } = this.props;
const windowKeys = Object.keys(windows).sort();
const leaveKeys = getLeaves(workspace.layout);
// Check every window is in the layout, and all layout windows are present
// in store
if (!windowKeys.every(e => leaveKeys.includes(e))
|| !leaveKeys.every(e => windowKeys.includes(e))) {
const newLayout = createBalancedTreeFromLeaves(windowKeys);
return newLayout;
// Windows were added
if (!windowKeys.every(e => leaveKeys.includes(e))) {
// No current layout, so just generate a new one
if (leaveKeys.length === 0) {
return createBalancedTreeFromLeaves(windowKeys);
}
// TODO: Here is where we will determine where to add a new Window #2380
return createBalancedTreeFromLeaves(windowKeys);
}
return workspace.layout;
......@@ -69,7 +102,7 @@ export class WorkspaceMosaic extends React.Component {
const { windows } = this.props;
const window = windows[id];
if (!window) return null;
this.bookkeepPath(window.id, path);
return (
<MosaicWindow
toolbarControls={[]}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment