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

Create a seam used to render other types of workspaces

parent a5ef9fc5
No related branches found
No related tags found
No related merge requests found
import React from 'react';
import { shallow } from 'enzyme';
import { Mosaic } from 'react-mosaic-component';
import Workspace from '../../../src/components/Workspace';
import Window from '../../../src/containers/Window';
describe('Workspace', () => {
const windows = { 1: { id: 1 }, 2: { id: 2 } };
......@@ -10,62 +10,31 @@ describe('Workspace', () => {
wrapper = shallow(
<Workspace
windows={windows}
workspace={{}}
updateWorkspaceMosaicLayout={() => {}}
config={{ workspace: { type: 'mosaic' } }}
/>,
);
});
it('should render properly with an initialValue', () => {
expect(wrapper.matchesElement(
<div className="mirador-workspace">
<Mosaic initialValue={{ direction: 'row', first: '1', second: '2' }} />
</div>,
)).toBe(true);
});
describe('determineWorkspaceLayout', () => {
it('when window ids do not match workspace layout', () => {
wrapper = shallow(
<Workspace
windows={windows}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={() => {}}
/>,
);
expect(wrapper.instance().determineWorkspaceLayout()).toMatchObject({
direction: 'row', first: '1', second: '2',
});
});
it('when window ids match workspace layout', () => {
wrapper = shallow(
<Workspace
windows={{ foo: { id: 'foo' } }}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={() => {}}
/>,
);
expect(wrapper.instance().determineWorkspaceLayout()).toBeNull();
});
});
describe('tileRenderer', () => {
it('when window is available', () => {
expect(wrapper.instance().tileRenderer('1')).not.toBeNull();
it('should render properly', () => {
expect(wrapper.find('.mirador-workspace').length).toBe(1);
expect(wrapper.find('Connect(WorkspaceMosaic)').length).toBe(1);
});
it('when window is not available', () => {
expect(wrapper.instance().tileRenderer('bar')).toBeNull();
describe('workspaceByType', () => {
it('when mosaic', () => {
expect(wrapper.find('Connect(WorkspaceMosaic)').length).toBe(1);
});
});
describe('mosaicChange', () => {
it('calls the provided prop to update layout', () => {
const mock = jest.fn();
it('anything else', () => {
wrapper = shallow(
<Workspace
windows={{ foo: { id: 'foo' } }}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={mock}
windows={windows}
config={{ workspace: { type: 'foo' } }}
/>,
);
wrapper.instance().mosaicChange();
expect(mock).toBeCalled();
expect(wrapper.matchesElement(
<div className="mirador-workspace">
<Window window={{ id: 1 }} />
<Window window={{ id: 2 }} />
</div>,
)).toBe(true);
});
});
});
import React from 'react';
import { shallow } from 'enzyme';
import { Mosaic } from 'react-mosaic-component';
import WorkspaceMosaic from '../../../src/components/WorkspaceMosaic';
describe('WorkspaceMosaic', () => {
const windows = { 1: { id: 1 }, 2: { id: 2 } };
let wrapper;
beforeEach(() => {
wrapper = shallow(
<WorkspaceMosaic
windows={windows}
workspace={{}}
updateWorkspaceMosaicLayout={() => {}}
/>,
);
});
it('should render properly with an initialValue', () => {
expect(wrapper.matchesElement(
<Mosaic initialValue={{ direction: 'row', first: '1', second: '2' }} />,
)).toBe(true);
});
describe('determineWorkspaceLayout', () => {
it('when window ids do not match workspace layout', () => {
wrapper = shallow(
<WorkspaceMosaic
windows={windows}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={() => {}}
/>,
);
expect(wrapper.instance().determineWorkspaceLayout()).toMatchObject({
direction: 'row', first: '1', second: '2',
});
});
it('when window ids match workspace layout', () => {
wrapper = shallow(
<WorkspaceMosaic
windows={{ foo: { id: 'foo' } }}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={() => {}}
/>,
);
expect(wrapper.instance().determineWorkspaceLayout()).toBeNull();
});
});
describe('tileRenderer', () => {
it('when window is available', () => {
expect(wrapper.instance().tileRenderer('1')).not.toBeNull();
});
it('when window is not available', () => {
expect(wrapper.instance().tileRenderer('bar')).toBeNull();
});
});
describe('mosaicChange', () => {
it('calls the provided prop to update layout', () => {
const mock = jest.fn();
wrapper = shallow(
<WorkspaceMosaic
windows={{ foo: { id: 'foo' } }}
workspace={{ layout: 'foo' }}
updateWorkspaceMosaicLayout={mock}
/>,
);
wrapper.instance().mosaicChange();
expect(mock).toBeCalled();
});
});
});
import React from 'react';
import PropTypes from 'prop-types';
import {
Mosaic, getLeaves, createBalancedTreeFromLeaves,
} from 'react-mosaic-component';
import 'react-mosaic-component/react-mosaic-component.css';
import Window from '../containers/Window';
import WorkspaceMosaic from '../containers/WorkspaceMosaic';
import ns from '../config/css-ns';
/**
......@@ -18,77 +15,42 @@ class Workspace extends React.Component {
constructor(props) {
super(props);
this.tileRenderer = this.tileRenderer.bind(this);
this.mosaicChange = this.mosaicChange.bind(this);
this.determineWorkspaceLayout = this.determineWorkspaceLayout.bind(this);
this.workspaceByType = this.workspaceByType.bind(this);
}
/**
* Render a tile (Window) in the Mosaic.
* Determine which workspace to render by configured type
*/
tileRenderer(id, path) {
const { windows } = this.props;
const window = windows[id];
if (!window) return null;
return (
workspaceByType() {
const { config, windows } = this.props;
switch (config.workspace.type) {
case 'mosaic':
return <WorkspaceMosaic windows={windows} />;
default:
return Object.values(windows).map(window => (
<Window
key={window.id}
window={window}
/>
);
));
}
/**
* Update the redux store when the Mosaic is changed.
*/
mosaicChange(newLayout) {
const { updateWorkspaceMosaicLayout } = this.props;
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);
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
*/
render() {
const { workspace } = this.props;
const newLayout = this.determineWorkspaceLayout();
return (
<div className={ns('workspace')}>
<Mosaic
renderTile={this.tileRenderer}
initialValue={newLayout || workspace.layout}
onChange={this.mosaicChange}
className="mirador-mosaic"
zeroStateView={<div />}
/>
{this.workspaceByType()}
</div>
);
}
}
Workspace.propTypes = {
updateWorkspaceMosaicLayout: PropTypes.func.isRequired,
windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
workspace: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
config: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};
export default Workspace;
import React from 'react';
import PropTypes from 'prop-types';
import {
Mosaic, getLeaves, createBalancedTreeFromLeaves,
} from 'react-mosaic-component';
import 'react-mosaic-component/react-mosaic-component.css';
import Window from '../containers/Window';
/**
* Represents a work area that contains any number of windows
* @memberof Workspace
* @private
*/
class WorkspaceMosaic extends React.Component {
/**
*/
constructor(props) {
super(props);
this.tileRenderer = this.tileRenderer.bind(this);
this.mosaicChange = this.mosaicChange.bind(this);
this.determineWorkspaceLayout = this.determineWorkspaceLayout.bind(this);
}
/**
* Render a tile (Window) in the Mosaic.
*/
tileRenderer(id, path) {
const { windows } = this.props;
const window = windows[id];
if (!window) return null;
return (
<Window
key={window.id}
window={window}
/>
);
}
/**
* Update the redux store when the Mosaic is changed.
*/
mosaicChange(newLayout) {
const { updateWorkspaceMosaicLayout } = this.props;
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);
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() {
const { workspace } = this.props;
const newLayout = this.determineWorkspaceLayout();
return (
<Mosaic
renderTile={this.tileRenderer}
initialValue={newLayout || workspace.layout}
onChange={this.mosaicChange}
className="mirador-mosaic"
zeroStateView={<div />}
/>
);
}
}
WorkspaceMosaic.propTypes = {
updateWorkspaceMosaicLayout: PropTypes.func.isRequired,
windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
workspace: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
};
export default WorkspaceMosaic;
......@@ -4,4 +4,7 @@ export default {
defaultPosition: 'bottom',
height: 150,
},
workspace: {
type: 'mosaic',
}
};
......@@ -10,21 +10,13 @@ import Workspace from '../components/Workspace';
*/
const mapStateToProps = state => (
{
config: state.config,
windows: state.windows,
workspace: state.workspace,
}
);
/**
* mapDispatchToProps - used to hook up connect to action creators
* @memberof Workspace
* @private
*/
const mapDispatchToProps = { updateWorkspaceMosaicLayout: actions.updateWorkspaceMosaicLayout };
const enhance = compose(
connect(mapStateToProps, mapDispatchToProps),
connect(mapStateToProps),
// further HOC go here
);
......
import { compose } from 'redux';
import { connect } from 'react-redux';
import * as actions from '../state/actions';
import WorkspaceMosaic from '../components/WorkspaceMosaic';
/**
* mapStateToProps - to hook up connect
* @memberof Workspace
* @private
*/
const mapStateToProps = state => (
{
workspace: state.workspace,
}
);
/**
* mapDispatchToProps - used to hook up connect to action creators
* @memberof Workspace
* @private
*/
const mapDispatchToProps = { updateWorkspaceMosaicLayout: actions.updateWorkspaceMosaicLayout };
const enhance = compose(
connect(mapStateToProps, mapDispatchToProps),
// further HOC go here
);
export default enhance(WorkspaceMosaic);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment