Skip to content
Snippets Groups Projects
Unverified Commit d80a2cb6 authored by Jack Reed's avatar Jack Reed Committed by GitHub
Browse files

Merge pull request #1920 from ProjectMirador/1914-mosaic-lifecycle

Move determining the workspace layout to component lifecycle methods instead of render()
parents 5deee933 029ceffe
No related branches found
No related tags found
No related merge requests found
...@@ -7,6 +7,10 @@ import WindowMiddleContent from '../../../src/containers/WindowMiddleContent'; ...@@ -7,6 +7,10 @@ import WindowMiddleContent from '../../../src/containers/WindowMiddleContent';
describe('Window', () => { describe('Window', () => {
let wrapper; let wrapper;
const window = { id: 123, xywh: [0, 0, 400, 500] }; const window = { id: 123, xywh: [0, 0, 400, 500] };
it('should render nothing, if provided with no window data', () => {
wrapper = shallow(<Window />);
expect(wrapper.find('.mirador-window')).toHaveLength(0);
});
it('should render outer element', () => { it('should render outer element', () => {
wrapper = shallow(<Window window={window} />); wrapper = shallow(<Window window={window} />);
expect(wrapper.find('.mirador-window')).toHaveLength(1); expect(wrapper.find('.mirador-window')).toHaveLength(1);
......
...@@ -3,44 +3,48 @@ import { shallow } from 'enzyme'; ...@@ -3,44 +3,48 @@ import { shallow } from 'enzyme';
import { Mosaic } from 'react-mosaic-component'; import { Mosaic } from 'react-mosaic-component';
import WorkspaceMosaic from '../../../src/components/WorkspaceMosaic'; import WorkspaceMosaic from '../../../src/components/WorkspaceMosaic';
describe('WorkspaceMosaic', () => { /** create wrapper */
const windows = { 1: { id: 1 }, 2: { id: 2 } }; function createWrapper(props) {
let wrapper; return shallow(
beforeEach(() => {
wrapper = shallow(
<WorkspaceMosaic <WorkspaceMosaic
windows={windows} windows={{}}
workspace={{}} workspace={{}}
updateWorkspaceMosaicLayout={() => {}} updateWorkspaceMosaicLayout={() => {}}
{...props}
/>, />,
); );
}
describe('WorkspaceMosaic', () => {
const windows = { 1: { id: 1 }, 2: { id: 2 } };
let wrapper;
beforeEach(() => {
wrapper = createWrapper({ windows });
}); });
it('should render properly with an initialValue', () => { it('should render properly with an initialValue', () => {
expect(wrapper.matchesElement( expect(wrapper.matchesElement(
<Mosaic initialValue={{ direction: 'row', first: '1', second: '2' }} />, <Mosaic initialValue={{ direction: 'row', first: '1', second: '2' }} />,
)).toBe(true); )).toBe(true);
}); });
describe('componentDidUpdate', () => {
it('updates the workspace layout when windows change', () => {
const updateWorkspaceMosaicLayout = jest.fn();
wrapper = createWrapper({ windows, updateWorkspaceMosaicLayout });
wrapper.setProps({ windows: { ...windows, 3: { id: 3 } } });
expect(updateWorkspaceMosaicLayout).toHaveBeenCalled();
});
});
describe('determineWorkspaceLayout', () => { describe('determineWorkspaceLayout', () => {
it('when window ids do not match workspace layout', () => { it('when window ids do not match workspace layout', () => {
wrapper = shallow( wrapper = createWrapper({ windows, workspace: { layout: 'foo' } });
<WorkspaceMosaic
windows={windows}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={() => {}}
/>,
);
expect(wrapper.instance().determineWorkspaceLayout()).toMatchObject({ expect(wrapper.instance().determineWorkspaceLayout()).toMatchObject({
direction: 'row', first: '1', second: '2', direction: 'row', first: '1', second: '2',
}); });
}); });
it('when window ids match workspace layout', () => { it('when window ids match workspace layout', () => {
wrapper = shallow( wrapper = createWrapper({ windows: { foo: { id: 'foo' } }, workspace: { layout: 'foo' } });
<WorkspaceMosaic
windows={{ foo: { id: 'foo' } }}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={() => {}}
/>,
);
expect(wrapper.instance().determineWorkspaceLayout()).toBeNull(); expect(wrapper.instance().determineWorkspaceLayout()).toBeNull();
}); });
}); });
...@@ -54,16 +58,11 @@ describe('WorkspaceMosaic', () => { ...@@ -54,16 +58,11 @@ describe('WorkspaceMosaic', () => {
}); });
describe('mosaicChange', () => { describe('mosaicChange', () => {
it('calls the provided prop to update layout', () => { it('calls the provided prop to update layout', () => {
const mock = jest.fn(); const updateWorkspaceMosaicLayout = jest.fn();
wrapper = shallow( wrapper = createWrapper({ windows, updateWorkspaceMosaicLayout });
<WorkspaceMosaic
windows={{ foo: { id: 'foo' } }}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={mock}
/>,
);
wrapper.instance().mosaicChange(); wrapper.instance().mosaicChange();
expect(mock).toBeCalled(); expect(updateWorkspaceMosaicLayout).toBeCalled();
}); });
}); });
}); });
...@@ -15,6 +15,8 @@ class Window extends Component { ...@@ -15,6 +15,8 @@ class Window extends Component {
*/ */
render() { render() {
const { manifest, window } = this.props; const { manifest, window } = this.props;
if (!window) return <></>;
return ( return (
<div id={window.id} className={ns('window')}> <div id={window.id} className={ns('window')}>
<WindowTopBar <WindowTopBar
...@@ -38,11 +40,12 @@ class Window extends Component { ...@@ -38,11 +40,12 @@ class Window extends Component {
} }
Window.propTypes = { Window.propTypes = {
window: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types window: PropTypes.object, // eslint-disable-line react/forbid-prop-types
manifest: PropTypes.object, // eslint-disable-line react/forbid-prop-types manifest: PropTypes.object, // eslint-disable-line react/forbid-prop-types
}; };
Window.defaultProps = { Window.defaultProps = {
window: null,
manifest: null, manifest: null,
}; };
......
...@@ -23,6 +23,44 @@ class WorkspaceMosaic extends React.Component { ...@@ -23,6 +23,44 @@ class WorkspaceMosaic extends React.Component {
this.zeroStateView = <div />; this.zeroStateView = <div />;
} }
/** */
componentDidMount() {
const { updateWorkspaceMosaicLayout } = this.props;
const newLayout = this.determineWorkspaceLayout();
if (newLayout) updateWorkspaceMosaicLayout(newLayout);
}
/** */
componentDidUpdate(prevProps) {
const { windows, workspace, updateWorkspaceMosaicLayout } = this.props;
if (prevProps.windows !== windows || prevProps.workspace !== workspace) {
const newLayout = this.determineWorkspaceLayout();
if (newLayout) updateWorkspaceMosaicLayout(newLayout);
}
}
/**
* 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.
*/
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;
}
return null;
}
/** /**
* Render a tile (Window) in the Mosaic. * Render a tile (Window) in the Mosaic.
*/ */
...@@ -46,35 +84,14 @@ class WorkspaceMosaic extends React.Component { ...@@ -46,35 +84,14 @@ class WorkspaceMosaic extends React.Component {
updateWorkspaceMosaicLayout(newLayout); updateWorkspaceMosaicLayout(newLayout);
} }
/**
* 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.
*/
determineWorkspaceLayout() {
const { windows, workspace, updateWorkspaceMosaicLayout } = 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);
updateWorkspaceMosaicLayout(newLayout);
return newLayout;
}
return null;
}
/** /**
*/ */
render() { render() {
const { workspace } = this.props; const { workspace } = this.props;
const newLayout = this.determineWorkspaceLayout();
return ( return (
<Mosaic <Mosaic
renderTile={this.tileRenderer} renderTile={this.tileRenderer}
initialValue={newLayout || workspace.layout} initialValue={workspace.layout || this.determineWorkspaceLayout()}
onChange={this.mosaicChange} onChange={this.mosaicChange}
className="mirador-mosaic" className="mirador-mosaic"
zeroStateView={this.zeroStateView} zeroStateView={this.zeroStateView}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment