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

Merge pull request #2501 from ProjectMirador/window-prop

Address additional components that received full objects from state
parents 696de482 80543530
Branches
Tags
No related merge requests found
Showing
with 166 additions and 127 deletions
...@@ -12,16 +12,7 @@ function createWrapper(props) { ...@@ -12,16 +12,7 @@ function createWrapper(props) {
windowId="abc123" windowId="abc123"
position="right" position="right"
companionAreaOpen companionAreaOpen
companionWindows={[ companionWindowIds={['foo', 'baz']}
{
id: 'foo',
position: 'right',
},
{
id: 'baz',
position: 'right',
},
]}
t={key => key} t={key => key}
{...props} {...props}
/>, />,
......
...@@ -18,25 +18,25 @@ describe('ErrorDialog', () => { ...@@ -18,25 +18,25 @@ describe('ErrorDialog', () => {
let wrapper; let wrapper;
it('renders properly', () => { it('renders properly', () => {
const errors = { testid123: { id: 'testid123', message: '' } }; const error = { id: 'testid123', message: '' };
wrapper = createWrapper({ errors }); wrapper = createWrapper({ error });
expect(wrapper.find('WithStyles(Dialog)').length).toBe(1); expect(wrapper.find('WithStyles(Dialog)').length).toBe(1);
}); });
it('shows up error message correctly', () => { it('shows up error message correctly', () => {
const errorMessage = 'error testMessage 123'; const errorMessage = 'error testMessage 123';
const errors = { testid123: { id: 'testid123', message: errorMessage } }; const error = { id: 'testid123', message: errorMessage };
wrapper = createWrapper({ errors }); wrapper = createWrapper({ error });
expect(wrapper.find('WithStyles(DialogContentText)[variant="body2"]').render().text()).toBe(errorMessage); expect(wrapper.find('WithStyles(DialogContentText)[variant="body2"]').render().text()).toBe(errorMessage);
}); });
it('triggers the handleClick prop when clicking the ok button', () => { it('triggers the handleClick prop when clicking the ok button', () => {
const errors = { testid123: { id: 'testid123', message: '' } }; const error = { id: 'testid123', message: '' };
const mockHandleClick = jest.fn(); const mockHandleClick = jest.fn();
wrapper = createWrapper({ errors, removeError: mockHandleClick }); wrapper = createWrapper({ error, removeError: mockHandleClick });
wrapper.find('WithStyles(Button)').simulate('click'); wrapper.find('WithStyles(Button)').simulate('click');
expect(mockHandleClick).toHaveBeenCalledTimes(1); expect(mockHandleClick).toHaveBeenCalledTimes(1);
}); });
......
...@@ -9,19 +9,17 @@ describe('WindowList', () => { ...@@ -9,19 +9,17 @@ describe('WindowList', () => {
let handleClose; let handleClose;
let focusWindow; let focusWindow;
let titles; let titles;
let windows;
beforeEach(() => { beforeEach(() => {
handleClose = jest.fn(); handleClose = jest.fn();
focusWindow = jest.fn(); focusWindow = jest.fn();
titles = {}; titles = {};
windows = {};
wrapper = shallow( wrapper = shallow(
<WindowList <WindowList
containerId="mirador" containerId="mirador"
anchorEl={{}} anchorEl={{}}
titles={titles} titles={titles}
windows={windows} windowIds={[]}
handleClose={handleClose} handleClose={handleClose}
focusWindow={focusWindow} focusWindow={focusWindow}
/>, />,
...@@ -34,14 +32,12 @@ describe('WindowList', () => { ...@@ -34,14 +32,12 @@ describe('WindowList', () => {
describe('with a window without a matching manifest', () => { describe('with a window without a matching manifest', () => {
beforeEach(() => { beforeEach(() => {
windows = { xyz: { id: 'xyz', manifestId: 'abc' } };
wrapper = shallow( wrapper = shallow(
<WindowList <WindowList
containerId="mirador" containerId="mirador"
anchorEl={{}} anchorEl={{}}
titles={titles} titles={titles}
windows={windows} windowIds={['xyz']}
handleClose={handleClose} handleClose={handleClose}
focusWindow={focusWindow} focusWindow={focusWindow}
/>, />,
...@@ -62,7 +58,6 @@ describe('WindowList', () => { ...@@ -62,7 +58,6 @@ describe('WindowList', () => {
describe('with a window with a matching manifest', () => { describe('with a window with a matching manifest', () => {
beforeEach(() => { beforeEach(() => {
windows = { xyz: { id: 'xyz', manifestId: 'abc' } };
titles = { xyz: 'Some title' }; titles = { xyz: 'Some title' };
wrapper = shallow( wrapper = shallow(
...@@ -70,7 +65,7 @@ describe('WindowList', () => { ...@@ -70,7 +65,7 @@ describe('WindowList', () => {
containerId="mirador" containerId="mirador"
anchorEl={{}} anchorEl={{}}
titles={titles} titles={titles}
windows={windows} windowIds={['xyz']}
handleClose={handleClose} handleClose={handleClose}
focusWindow={focusWindow} focusWindow={focusWindow}
/>, />,
...@@ -88,10 +83,6 @@ describe('WindowList', () => { ...@@ -88,10 +83,6 @@ describe('WindowList', () => {
describe('with multiple windows', () => { describe('with multiple windows', () => {
beforeEach(() => { beforeEach(() => {
windows = {
xyz: { id: 'xyz', manifestId: 'abc' },
zyx: { id: 'zyx', manifestId: '123' },
};
titles = { xyz: 'Some title' }; titles = { xyz: 'Some title' };
wrapper = shallow( wrapper = shallow(
...@@ -99,7 +90,7 @@ describe('WindowList', () => { ...@@ -99,7 +90,7 @@ describe('WindowList', () => {
containerId="mirador" containerId="mirador"
anchorEl={{}} anchorEl={{}}
titles={titles} titles={titles}
windows={windows} windowIds={['xyz', 'zyx']}
handleClose={handleClose} handleClose={handleClose}
focusWindow={focusWindow} focusWindow={focusWindow}
/>, />,
......
...@@ -6,9 +6,6 @@ import WorkspaceElastic from '../../../src/containers/WorkspaceElastic'; ...@@ -6,9 +6,6 @@ import WorkspaceElastic from '../../../src/containers/WorkspaceElastic';
import Window from '../../../src/containers/Window'; import Window from '../../../src/containers/Window';
import { Workspace } from '../../../src/components/Workspace'; import { Workspace } from '../../../src/components/Workspace';
const windows = { 1: { id: 1 }, 2: { id: 2 } };
const maximizedWindows = { 1: { id: 1, maximized: true }, 2: { id: 2, maximized: false } };
/** /**
* Utility function to create a Worksapce * Utility function to create a Worksapce
* component with all required props set * component with all required props set
...@@ -17,7 +14,7 @@ function createWrapper(props) { ...@@ -17,7 +14,7 @@ function createWrapper(props) {
return shallow( return shallow(
<Workspace <Workspace
isWorkspaceControlPanelVisible isWorkspaceControlPanelVisible
windows={windows} windowIds={['1', '2']}
workspaceId="foo" workspaceId="foo"
workspaceType="mosaic" workspaceType="mosaic"
t={k => k} t={k => k}
...@@ -46,7 +43,7 @@ describe('Workspace', () => { ...@@ -46,7 +43,7 @@ describe('Workspace', () => {
expect(wrapper.matchesElement( expect(wrapper.matchesElement(
<div className="mirador-workspace-viewport mirador-workspace-with-control-panel"> <div className="mirador-workspace-viewport mirador-workspace-with-control-panel">
<Typography>miradorViewer</Typography> <Typography>miradorViewer</Typography>
<WorkspaceMosaic windows={windows} /> <WorkspaceMosaic />
</div>, </div>,
)).toBe(true); )).toBe(true);
}); });
...@@ -65,7 +62,7 @@ describe('Workspace', () => { ...@@ -65,7 +62,7 @@ describe('Workspace', () => {
}); });
describe('if any windows are maximized', () => { describe('if any windows are maximized', () => {
it('should render only maximized <Window/> components', () => { it('should render only maximized <Window/> components', () => {
const wrapper = createWrapper({ windows: maximizedWindows }); const wrapper = createWrapper({ maximizedWindowIds: ['1'] });
expect(wrapper.matchesElement( expect(wrapper.matchesElement(
<div className="mirador-workspace-viewport mirador-workspace-with-control-panel"> <div className="mirador-workspace-viewport mirador-workspace-with-control-panel">
<Typography>miradorViewer</Typography> <Typography>miradorViewer</Typography>
...@@ -77,7 +74,7 @@ describe('Workspace', () => { ...@@ -77,7 +74,7 @@ describe('Workspace', () => {
describe('if there are no windows', () => { describe('if there are no windows', () => {
it('should render placeholder content', () => { it('should render placeholder content', () => {
const wrapper = createWrapper({ windows: {} }); const wrapper = createWrapper({ windowIds: [] });
expect(wrapper.find(Typography).at(1).matchesElement( expect(wrapper.find(Typography).at(1).matchesElement(
<Typography>welcome</Typography>, <Typography>welcome</Typography>,
......
...@@ -9,7 +9,7 @@ function createWrapper(props) { ...@@ -9,7 +9,7 @@ function createWrapper(props) {
return shallow( return shallow(
<WorkspaceMosaic <WorkspaceMosaic
windows={{}} windows={{}}
workspace={{}} workspaceId="foo"
updateWorkspaceMosaicLayout={() => {}} updateWorkspaceMosaicLayout={() => {}}
{...props} {...props}
/>, />,
...@@ -42,11 +42,9 @@ describe('WorkspaceMosaic', () => { ...@@ -42,11 +42,9 @@ describe('WorkspaceMosaic', () => {
it('updates the workspace layout when windows are removed', () => { it('updates the workspace layout when windows are removed', () => {
const updateWorkspaceMosaicLayout = jest.fn(); const updateWorkspaceMosaicLayout = jest.fn();
wrapper = createWrapper({ wrapper = createWrapper({
layout: { first: 1, second: 2 },
updateWorkspaceMosaicLayout, updateWorkspaceMosaicLayout,
windows, windows,
workspace: {
layout: { first: 1, second: 2 },
},
}); });
wrapper.instance().windowPaths = { 2: ['second'] }; wrapper.instance().windowPaths = { 2: ['second'] };
wrapper.setProps({ windows: { 1: { id: 1 } } }); wrapper.setProps({ windows: { 1: { id: 1 } } });
...@@ -70,21 +68,21 @@ describe('WorkspaceMosaic', () => { ...@@ -70,21 +68,21 @@ describe('WorkspaceMosaic', () => {
}); });
describe('determineWorkspaceLayout', () => { describe('determineWorkspaceLayout', () => {
it('when window ids do not match workspace layout', () => { it('when window ids do not match workspace layout', () => {
wrapper = createWrapper({ windows, workspace: { layout: 'foo' } }); wrapper = createWrapper({ layout: {}, windows });
expect(wrapper.instance().determineWorkspaceLayout()).toMatchObject({ expect(wrapper.instance().determineWorkspaceLayout()).toMatchObject({
direction: 'row', first: '1', second: '2', direction: 'row', first: '1', second: '2',
}); });
}); });
it('by default use workspace.layout', () => { it('by default use workspace.layout', () => {
wrapper = createWrapper({ windows: { foo: 'bar' }, workspace: { layout: 'foo' } }); wrapper = createWrapper({ layout: {}, windows: { foo: 'bar' } });
expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo'); expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo');
}); });
it('generates a new layout if windows do not match current layout', () => { it('generates a new layout if windows do not match current layout', () => {
wrapper = createWrapper({ windows: { foo: 'bar' }, workspace: { layout: { first: 'foo', second: 'bark' } } }); wrapper = createWrapper({ layout: { first: 'foo', second: 'bark' }, windows: { foo: 'bar' } });
expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo'); expect(wrapper.instance().determineWorkspaceLayout()).toEqual('foo');
}); });
it('when window ids match workspace layout', () => { it('when window ids match workspace layout', () => {
wrapper = createWrapper({ windows: { foo: { id: 'foo' } }, workspace: { layout: 'foo' } }); wrapper = createWrapper({ layout: {}, windows: { foo: { id: 'foo' } } });
expect(wrapper.instance().determineWorkspaceLayout()).toBe('foo'); expect(wrapper.instance().determineWorkspaceLayout()).toBe('foo');
}); });
}); });
...@@ -99,7 +97,7 @@ describe('WorkspaceMosaic', () => { ...@@ -99,7 +97,7 @@ describe('WorkspaceMosaic', () => {
toolbarControls: [], toolbarControls: [],
})); }));
expect(shallow(shallow(renderedTile).props().renderPreview()).matchesElement( expect(shallow(shallow(renderedTile).props().renderPreview({ windowId: 1 })).matchesElement(
<div className="mosaic-preview"> <div className="mosaic-preview">
<MosaicRenderPreview windowId={1} /> <MosaicRenderPreview windowId={1} />
</div>, </div>,
......
...@@ -7,15 +7,47 @@ import { ...@@ -7,15 +7,47 @@ import {
getThumbnailNavigationPosition, getThumbnailNavigationPosition,
getWindowViewType, getWindowViewType,
getCompanionWindow, getCompanionWindow,
getCompanionWindowForPosition, getCompanionWindowsForPosition,
getCompanionWindowsOfWindow, getCompanionWindowsOfWindow,
getViewer, getViewer,
getWindowDraggability, getWindowDraggability,
selectCompanionWindowDimensions, selectCompanionWindowDimensions,
getCanvasIndex, getCanvasIndex,
getWindowManifests, getWindowManifests,
getWindows,
getMaximizedWindows,
} from '../../../src/state/selectors/windows'; } from '../../../src/state/selectors/windows';
describe('getWindows', () => {
it('should return windows from state', () => {
const state = {
windows: {
a: { manifestId: 'amanifest' },
b: { manifestId: 'bmanifest' },
},
};
const received = getWindows(state);
expect(received).toEqual(state.windows);
});
});
describe('getMaximizedWindows', () => {
it('filters windows to only those maximized', () => {
const state = {
windows: {
a: { manifestId: 'amanifest', maximized: true },
b: { manifestId: 'bmanifest' },
},
};
const received = getMaximizedWindows(state);
expect(received.length).toEqual(1);
expect(received[0].manifestId).toEqual('amanifest');
});
});
describe('getWindowTitles', () => { describe('getWindowTitles', () => {
it('should return manifest titles for the open windows', () => { it('should return manifest titles for the open windows', () => {
...@@ -136,7 +168,7 @@ describe('getWindowViewType', () => { ...@@ -136,7 +168,7 @@ describe('getWindowViewType', () => {
}); });
}); });
describe('getCompanionWindowForPosition', () => { describe('getCompanionWindowsForPosition', () => {
const state = { const state = {
companionWindows: { companionWindows: {
abc: { id: 'abc', position: 'right' }, abc: { id: 'abc', position: 'right' },
...@@ -146,30 +178,30 @@ describe('getCompanionWindowForPosition', () => { ...@@ -146,30 +178,30 @@ describe('getCompanionWindowForPosition', () => {
}; };
it('the companion window type based on the given position', () => { it('the companion window type based on the given position', () => {
const received = getCompanionWindowForPosition(state, { const received = getCompanionWindowsForPosition(state, {
position: 'right', position: 'right',
windowId: 'a', windowId: 'a',
}); });
expect(received.length).toBe(1);
expect(received.id).toEqual('abc'); expect(received[0].id).toEqual('abc');
}); });
it('returns undefined if the given window does not exist', () => { it('returns undefined if the given window does not exist', () => {
const received = getCompanionWindowForPosition(state, { const received = getCompanionWindowsForPosition(state, {
position: 'right', position: 'right',
windowId: 'c', windowId: 'c',
}); });
expect(received).toBeUndefined(); expect(received).toEqual([]);
}); });
it('returns undefined if a companion window at the given position does not exist', () => { it('returns undefined if a companion window at the given position does not exist', () => {
const received = getCompanionWindowForPosition(state, { const received = getCompanionWindowsForPosition(state, {
position: 'bottom', position: 'bottom',
windowId: 'a', windowId: 'a',
}); });
expect(received).toBeUndefined(); expect(received).toEqual([]);
}); });
}); });
......
...@@ -21,14 +21,14 @@ export class CompanionArea extends Component { ...@@ -21,14 +21,14 @@ export class CompanionArea extends Component {
/** */ /** */
render() { render() {
const { const {
classes, companionWindows, companionAreaOpen, setCompanionAreaOpen, classes, companionWindowIds, companionAreaOpen, setCompanionAreaOpen,
position, sideBarOpen, t, windowId, position, sideBarOpen, t, windowId,
} = this.props; } = this.props;
return ( return (
<div className={[classes.root, this.areaLayoutClass(), ns(`companion-area-${position}`)].join(' ')}> <div className={[classes.root, this.areaLayoutClass(), ns(`companion-area-${position}`)].join(' ')}>
{ {
setCompanionAreaOpen && position === 'left' && sideBarOpen && companionWindows.length > 0 setCompanionAreaOpen && position === 'left' && sideBarOpen && companionWindowIds.length > 0
&& ( && (
<MiradorMenuButton <MiradorMenuButton
aria-label={companionAreaOpen ? t('collapseSidePanel') : t('expandSidePanel')} aria-label={companionAreaOpen ? t('collapseSidePanel') : t('expandSidePanel')}
...@@ -49,8 +49,8 @@ export class CompanionArea extends Component { ...@@ -49,8 +49,8 @@ export class CompanionArea extends Component {
<Slide in={companionAreaOpen} direction="right"> <Slide in={companionAreaOpen} direction="right">
<div className={[ns('companion-windows'), this.areaLayoutClass()].join(' ')} style={{ display: companionAreaOpen ? 'flex' : 'none' }}> <div className={[ns('companion-windows'), this.areaLayoutClass()].join(' ')} style={{ display: companionAreaOpen ? 'flex' : 'none' }}>
{ {
companionWindows.map(cw => ( companionWindowIds.map(id => (
<CompanionWindowFactory id={cw.id} key={cw.id} windowId={windowId} /> <CompanionWindowFactory id={id} key={id} windowId={windowId} />
)) ))
} }
</div> </div>
...@@ -63,7 +63,7 @@ export class CompanionArea extends Component { ...@@ -63,7 +63,7 @@ export class CompanionArea extends Component {
CompanionArea.propTypes = { CompanionArea.propTypes = {
classes: PropTypes.objectOf(PropTypes.string), classes: PropTypes.objectOf(PropTypes.string),
companionAreaOpen: PropTypes.bool.isRequired, companionAreaOpen: PropTypes.bool.isRequired,
companionWindows: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types companionWindowIds: PropTypes.arrayOf(PropTypes.string).isRequired,
position: PropTypes.string.isRequired, position: PropTypes.string.isRequired,
setCompanionAreaOpen: PropTypes.func, setCompanionAreaOpen: PropTypes.func,
sideBarOpen: PropTypes.bool, sideBarOpen: PropTypes.bool,
......
...@@ -5,12 +5,7 @@ import DialogTitle from '@material-ui/core/DialogTitle'; ...@@ -5,12 +5,7 @@ import DialogTitle from '@material-ui/core/DialogTitle';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { DialogActions, DialogContentText, Typography } from '@material-ui/core'; import { DialogActions, DialogContentText, Typography } from '@material-ui/core';
import Button from '@material-ui/core/Button'; import Button from '@material-ui/core/Button';
import { import { isUndefined } from 'lodash';
first,
isUndefined,
omit,
values,
} from 'lodash';
/** /**
*/ */
...@@ -21,11 +16,9 @@ export class ErrorDialog extends Component { ...@@ -21,11 +16,9 @@ export class ErrorDialog extends Component {
*/ */
render() { render() {
const { const {
errors, removeError, t, error, removeError, t,
} = this.props; } = this.props;
/* extract 'items' value and get first key-value-pair (an error) */
const error = first(values(omit(errors, 'items')));
const hasError = !isUndefined(error); const hasError = !isUndefined(error);
return error ? ( return error ? (
...@@ -54,13 +47,16 @@ export class ErrorDialog extends Component { ...@@ -54,13 +47,16 @@ export class ErrorDialog extends Component {
} }
ErrorDialog.propTypes = { ErrorDialog.propTypes = {
errors: PropTypes.object, // eslint-disable-line react/forbid-prop-types error: PropTypes.shape({
id: PropTypes.string,
message: PropTypes.string,
}),
removeError: PropTypes.func, removeError: PropTypes.func,
t: PropTypes.func, t: PropTypes.func,
}; };
ErrorDialog.defaultProps = { ErrorDialog.defaultProps = {
errors: null, error: null,
removeError: () => {}, removeError: () => {},
t: key => key, t: key => key,
}; };
...@@ -13,10 +13,10 @@ export class WindowList extends Component { ...@@ -13,10 +13,10 @@ export class WindowList extends Component {
* Get the title for a window from its manifest title * Get the title for a window from its manifest title
* @private * @private
*/ */
titleContent(window) { titleContent(windowId) {
const { titles, t } = this.props; const { titles, t } = this.props;
return titles[window.id] || t('untitled'); return titles[windowId] || t('untitled');
} }
/** /**
...@@ -25,7 +25,7 @@ export class WindowList extends Component { ...@@ -25,7 +25,7 @@ export class WindowList extends Component {
*/ */
render() { render() {
const { const {
containerId, handleClose, anchorEl, windows, focusWindow, t, containerId, handleClose, anchorEl, windowIds, focusWindow, t,
} = this.props; } = this.props;
return ( return (
...@@ -48,15 +48,15 @@ export class WindowList extends Component { ...@@ -48,15 +48,15 @@ export class WindowList extends Component {
{t('openWindows')} {t('openWindows')}
</ListSubheader> </ListSubheader>
{ {
Object.values(windows).map((window, i) => ( windowIds.map((windowId, i) => (
<MenuItem <MenuItem
key={window.id} key={windowId}
selected={i === 0} selected={i === 0}
onClick={(e) => { focusWindow(window.id, true); handleClose(e); }} onClick={(e) => { focusWindow(windowId, true); handleClose(e); }}
> >
<ListItemText primaryTypographyProps={{ variant: 'body1' }}> <ListItemText primaryTypographyProps={{ variant: 'body1' }}>
{ {
this.titleContent(window) this.titleContent(windowId)
} }
</ListItemText> </ListItemText>
</MenuItem> </MenuItem>
...@@ -73,8 +73,8 @@ WindowList.propTypes = { ...@@ -73,8 +73,8 @@ WindowList.propTypes = {
focusWindow: PropTypes.func.isRequired, focusWindow: PropTypes.func.isRequired,
handleClose: PropTypes.func.isRequired, handleClose: PropTypes.func.isRequired,
t: PropTypes.func, t: PropTypes.func,
titles: PropTypes.object, // eslint-disable-line react/forbid-prop-types titles: PropTypes.objectOf(PropTypes.string),
windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types windowIds: PropTypes.arrayOf(PropTypes.string).isRequired,
}; };
WindowList.defaultProps = { WindowList.defaultProps = {
......
...@@ -18,20 +18,20 @@ export class Workspace extends React.Component { ...@@ -18,20 +18,20 @@ export class Workspace extends React.Component {
* Determine which workspace to render by configured type * Determine which workspace to render by configured type
*/ */
workspaceByType() { workspaceByType() {
const { workspaceId, workspaceType, windows } = this.props; const { workspaceId, workspaceType, windowIds } = this.props;
if (this.maximizedWindows()) { if (this.maximizedWindows()) {
return this.maximizedWindows(); return this.maximizedWindows();
} }
if (Object.keys(windows).length === 0) return this.zeroWindows(); if (windowIds.length === 0) return this.zeroWindows();
switch (workspaceType) { switch (workspaceType) {
case 'elastic': case 'elastic':
return <WorkspaceElastic />; return <WorkspaceElastic />;
case 'mosaic': case 'mosaic':
return <WorkspaceMosaic windows={windows} />; return <WorkspaceMosaic />;
default: default:
return Object.keys(windows).map(windowId => ( return windowIds.map(windowId => (
<Window <Window
key={`${windowId}-${workspaceId}`} key={`${windowId}-${workspaceId}`}
windowId={windowId} windowId={windowId}
...@@ -72,12 +72,10 @@ export class Workspace extends React.Component { ...@@ -72,12 +72,10 @@ export class Workspace extends React.Component {
* Determine whether or not there are maximized windows * Determine whether or not there are maximized windows
*/ */
maximizedWindows() { maximizedWindows() {
const { windows, workspaceId } = this.props; const { maximizedWindowIds, workspaceId } = this.props;
const windowKeys = Object.keys(windows).sort();
const maximizedWindows = windowKeys if (maximizedWindowIds.length > 0) {
.filter(windowId => windows[windowId].maximized === true); return maximizedWindowIds.map(windowId => (
if (maximizedWindows.length) {
return maximizedWindows.map(windowId => (
<Window <Window
key={`${windowId}-${workspaceId}`} key={`${windowId}-${workspaceId}`}
windowId={windowId} windowId={windowId}
...@@ -112,8 +110,14 @@ export class Workspace extends React.Component { ...@@ -112,8 +110,14 @@ export class Workspace extends React.Component {
Workspace.propTypes = { Workspace.propTypes = {
isWorkspaceControlPanelVisible: PropTypes.bool.isRequired, isWorkspaceControlPanelVisible: PropTypes.bool.isRequired,
maximizedWindowIds: PropTypes.arrayOf(PropTypes.string),
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types windowIds: PropTypes.arrayOf(PropTypes.string),
workspaceId: PropTypes.string.isRequired, workspaceId: PropTypes.string.isRequired,
workspaceType: PropTypes.string.isRequired, workspaceType: PropTypes.string.isRequired,
}; };
Workspace.defaultProps = {
maximizedWindowIds: [],
windowIds: [],
};
...@@ -26,6 +26,8 @@ export class WorkspaceMosaic extends React.Component { ...@@ -26,6 +26,8 @@ export class WorkspaceMosaic extends React.Component {
this.determineWorkspaceLayout = this.determineWorkspaceLayout.bind(this); this.determineWorkspaceLayout = this.determineWorkspaceLayout.bind(this);
this.zeroStateView = <div />; this.zeroStateView = <div />;
this.windowPaths = {}; this.windowPaths = {};
this.toolbarControls = [];
this.additionalControls = [];
} }
/** */ /** */
...@@ -38,7 +40,7 @@ export class WorkspaceMosaic extends React.Component { ...@@ -38,7 +40,7 @@ export class WorkspaceMosaic extends React.Component {
/** */ /** */
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { windows, workspace, updateWorkspaceMosaicLayout } = this.props; const { windows, layout, updateWorkspaceMosaicLayout } = this.props;
const prevWindows = Object.keys(prevProps.windows); const prevWindows = Object.keys(prevProps.windows);
const currentWindows = Object.keys(windows); const currentWindows = Object.keys(windows);
// Handles when Windows are removed from the state // Handles when Windows are removed from the state
...@@ -50,15 +52,15 @@ export class WorkspaceMosaic extends React.Component { ...@@ -50,15 +52,15 @@ export class WorkspaceMosaic extends React.Component {
} }
const removedWindows = difference(prevWindows, currentWindows); const removedWindows = difference(prevWindows, currentWindows);
const layout = new MosaicLayout(workspace.layout); const newLayout = new MosaicLayout(layout);
layout.removeWindows(removedWindows, this.windowPaths); newLayout.removeWindows(removedWindows, this.windowPaths);
updateWorkspaceMosaicLayout(layout.layout); updateWorkspaceMosaicLayout(newLayout.layout);
} }
// Handles when Windows are added (not via Add Resource UI) // Handles when Windows are added (not via Add Resource UI)
// TODO: If a window is added, add it in a better way #2380 // TODO: If a window is added, add it in a better way #2380
if (!currentWindows.every(e => prevWindows.includes(e))) { if (!currentWindows.every(e => prevWindows.includes(e))) {
const newLayout = this.determineWorkspaceLayout(); const newLayout = this.determineWorkspaceLayout();
if (newLayout !== workspace.layout) updateWorkspaceMosaicLayout(newLayout); if (newLayout !== layout) updateWorkspaceMosaicLayout(newLayout);
} }
} }
...@@ -75,10 +77,10 @@ export class WorkspaceMosaic extends React.Component { ...@@ -75,10 +77,10 @@ export class WorkspaceMosaic extends React.Component {
* Used to determine whether or not a "new" layout should be autogenerated. * Used to determine whether or not a "new" layout should be autogenerated.
*/ */
determineWorkspaceLayout() { determineWorkspaceLayout() {
const { windows, workspace } = this.props; const { windows, layout } = this.props;
const sortedWindows = toPairs(windows) const sortedWindows = toPairs(windows)
.sort((a, b) => a.layoutOrder - b.layoutOrder).map(val => val[0]); .sort((a, b) => a.layoutOrder - b.layoutOrder).map(val => val[0]);
const leaveKeys = getLeaves(workspace.layout); const leaveKeys = getLeaves(layout);
// Windows were added // Windows were added
if (!sortedWindows.every(e => leaveKeys.includes(e))) { if (!sortedWindows.every(e => leaveKeys.includes(e))) {
// No current layout, so just generate a new one // No current layout, so just generate a new one
...@@ -87,40 +89,45 @@ export class WorkspaceMosaic extends React.Component { ...@@ -87,40 +89,45 @@ export class WorkspaceMosaic extends React.Component {
} }
// Add new windows to layout // Add new windows to layout
const addedWindows = difference(sortedWindows, leaveKeys); const addedWindows = difference(sortedWindows, leaveKeys);
const layout = new MosaicLayout(workspace.layout); const newLayout = new MosaicLayout(layout);
layout.addWindows(addedWindows); newLayout.addWindows(addedWindows);
return layout.layout; return newLayout.layout;
} }
// Windows were removed (perhaps in a different Workspace). We don't have a // Windows were removed (perhaps in a different Workspace). We don't have a
// way to reconfigure.. so we have to random generate // way to reconfigure.. so we have to random generate
if (!leaveKeys.every(e => sortedWindows.includes(e))) { if (!leaveKeys.every(e => sortedWindows.includes(e))) {
return createBalancedTreeFromLeaves(sortedWindows); return createBalancedTreeFromLeaves(sortedWindows);
} }
return workspace.layout; return layout;
}
/** */
static renderPreview(mosaicProps) {
return (
<div className="mosaic-preview">
<MosaicRenderPreview windowId={mosaicProps.windowId} />
</div>
);
} }
/** /**
* Render a tile (Window) in the Mosaic. * Render a tile (Window) in the Mosaic.
*/ */
tileRenderer(id, path) { tileRenderer(id, path) {
const { windows, workspace } = this.props; const { windows, workspaceId } = this.props;
const window = windows[id]; const window = windows[id];
if (!window) return null; if (!window) return null;
this.bookkeepPath(window.id, path); this.bookkeepPath(window.id, path);
return ( return (
<MosaicWindow <MosaicWindow
toolbarControls={[]} toolbarControls={this.toolbarControls}
additionalControls={[]} additionalControls={this.additionalControls}
path={path} path={path}
windowId={window.id} windowId={window.id}
renderPreview={() => ( renderPreview={WorkspaceMosaic.renderPreview}
<div className="mosaic-preview">
<MosaicRenderPreview windowId={window.id} />
</div>
)}
> >
<Window <Window
key={`${window.id}-${workspace.id}`} key={`${window.id}-${workspaceId}`}
windowId={window.id} windowId={window.id}
/> />
</MosaicWindow> </MosaicWindow>
...@@ -138,11 +145,11 @@ export class WorkspaceMosaic extends React.Component { ...@@ -138,11 +145,11 @@ export class WorkspaceMosaic extends React.Component {
/** /**
*/ */
render() { render() {
const { workspace } = this.props; const { layout } = this.props;
return ( return (
<Mosaic <Mosaic
renderTile={this.tileRenderer} renderTile={this.tileRenderer}
initialValue={workspace.layout || this.determineWorkspaceLayout()} initialValue={layout || this.determineWorkspaceLayout()}
onChange={this.mosaicChange} onChange={this.mosaicChange}
className="mirador-mosaic" className="mirador-mosaic"
zeroStateView={this.zeroStateView} zeroStateView={this.zeroStateView}
...@@ -152,7 +159,12 @@ export class WorkspaceMosaic extends React.Component { ...@@ -152,7 +159,12 @@ export class WorkspaceMosaic extends React.Component {
} }
WorkspaceMosaic.propTypes = { WorkspaceMosaic.propTypes = {
layout: PropTypes.object, // eslint-disable-line react/forbid-prop-types
updateWorkspaceMosaicLayout: PropTypes.func.isRequired, updateWorkspaceMosaicLayout: PropTypes.func.isRequired,
windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types windows: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
workspace: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types workspaceId: PropTypes.string.isRequired,
};
WorkspaceMosaic.defaultProps = {
layout: undefined,
}; };
...@@ -3,15 +3,14 @@ import { connect } from 'react-redux'; ...@@ -3,15 +3,14 @@ import { connect } from 'react-redux';
import { withStyles } from '@material-ui/core'; import { withStyles } from '@material-ui/core';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import { withPlugins } from '../extend'; import { withPlugins } from '../extend';
import { getCompanionWindowsOfWindow, getCompanionAreaVisibility, getWindow } from '../state/selectors'; import { getCompanionWindowsForPosition, getCompanionAreaVisibility, getWindow } from '../state/selectors';
import * as actions from '../state/actions'; import * as actions from '../state/actions';
import { CompanionArea } from '../components/CompanionArea'; import { CompanionArea } from '../components/CompanionArea';
/** */ /** */
const mapStateToProps = (state, { windowId, position }) => ({ const mapStateToProps = (state, { windowId, position }) => ({
companionAreaOpen: getCompanionAreaVisibility(state, { position, windowId }), companionAreaOpen: getCompanionAreaVisibility(state, { position, windowId }),
companionWindows: getCompanionWindowsOfWindow(state, { windowId }) companionWindowIds: getCompanionWindowsForPosition(state, { position, windowId }).map(w => w.id),
.filter(cw => cw && cw.position === position),
sideBarOpen: (getWindow(state, { windowId }) || {}).sideBarOpen, sideBarOpen: (getWindow(state, { windowId }) || {}).sideBarOpen,
}); });
......
import { compose } from 'redux'; import { compose } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import {
first,
omit,
values,
} from 'lodash';
import { withPlugins } from '../extend'; import { withPlugins } from '../extend';
import { ErrorDialog } from '../components/ErrorDialog'; import { ErrorDialog } from '../components/ErrorDialog';
import * as actions from '../state/actions'; import * as actions from '../state/actions';
...@@ -11,7 +16,8 @@ import * as actions from '../state/actions'; ...@@ -11,7 +16,8 @@ import * as actions from '../state/actions';
* @private * @private
*/ */
const mapStateToProps = state => ({ const mapStateToProps = state => ({
errors: state.errors, /* extract 'items' value and get first key-value-pair (an error) */
errors: first(values(omit(state.errors, 'items'))),
}); });
/** /**
......
...@@ -24,7 +24,7 @@ const mapStateToProps = state => ( ...@@ -24,7 +24,7 @@ const mapStateToProps = state => (
{ {
containerId: getContainerId(state), containerId: getContainerId(state),
titles: getWindowTitles(state), titles: getWindowTitles(state),
windows: state.windows, windowIds: Object.keys(state.windows),
} }
); );
......
...@@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next'; ...@@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next';
import { withPlugins } from '../extend'; import { withPlugins } from '../extend';
import * as actions from '../state/actions'; import * as actions from '../state/actions';
import { import {
getCompanionWindowForPosition, getCompanionWindowsForPosition,
getAnnotationResourcesByMotivation, getAnnotationResourcesByMotivation,
} from '../state/selectors'; } from '../state/selectors';
import { WindowSideBarButtons } from '../components/WindowSideBarButtons'; import { WindowSideBarButtons } from '../components/WindowSideBarButtons';
...@@ -31,7 +31,7 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({ ...@@ -31,7 +31,7 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({
const mapStateToProps = (state, { windowId }) => ({ const mapStateToProps = (state, { windowId }) => ({
hasAnnotations: getAnnotationResourcesByMotivation(state, { motivations: ['oa:commenting', 'sc:painting'], windowId }).length > 0, hasAnnotations: getAnnotationResourcesByMotivation(state, { motivations: ['oa:commenting', 'sc:painting'], windowId }).length > 0,
hideAnnotationsPanel: state.config.window.hideAnnotationsPanel, hideAnnotationsPanel: state.config.window.hideAnnotationsPanel,
sideBarPanel: (getCompanionWindowForPosition(state, { position: 'left', windowId }) || {}).content, sideBarPanel: ((getCompanionWindowsForPosition(state, { position: 'left', windowId }))[0] || {}).content,
}); });
/** */ /** */
......
...@@ -3,7 +3,7 @@ import { connect } from 'react-redux'; ...@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import { withPlugins } from '../extend'; import { withPlugins } from '../extend';
import { Workspace } from '../components/Workspace'; import { Workspace } from '../components/Workspace';
import { getWorkspaceType } from '../state/selectors'; import { getMaximizedWindows, getWorkspaceType } from '../state/selectors';
/** /**
* mapStateToProps - to hook up connect * mapStateToProps - to hook up connect
...@@ -13,7 +13,8 @@ import { getWorkspaceType } from '../state/selectors'; ...@@ -13,7 +13,8 @@ import { getWorkspaceType } from '../state/selectors';
const mapStateToProps = state => ( const mapStateToProps = state => (
{ {
isWorkspaceControlPanelVisible: state.config.workspaceControlPanel.enabled, isWorkspaceControlPanelVisible: state.config.workspaceControlPanel.enabled,
windows: state.windows, maximizedWindowIds: Object.keys(getMaximizedWindows(state)),
windowIds: Object.keys(state.windows),
workspaceId: state.workspace.id, workspaceId: state.workspace.id,
workspaceType: getWorkspaceType(state), workspaceType: getWorkspaceType(state),
} }
......
...@@ -11,7 +11,9 @@ import { WorkspaceMosaic } from '../components/WorkspaceMosaic'; ...@@ -11,7 +11,9 @@ import { WorkspaceMosaic } from '../components/WorkspaceMosaic';
*/ */
const mapStateToProps = state => ( const mapStateToProps = state => (
{ {
workspace: state.workspace, layout: state.workspace.layout,
windows: state.windows,
workspaceId: state.workspace.id,
} }
); );
......
...@@ -10,7 +10,7 @@ import { getWorkspaceType } from './config'; ...@@ -10,7 +10,7 @@ import { getWorkspaceType } from './config';
export function getWindowTitles(state) { export function getWindowTitles(state) {
const result = {}; const result = {};
Object.keys(state.windows).forEach((windowId) => { Object.keys(getWindows(state)).forEach((windowId) => {
result[windowId] = getManifestTitle(state, { windowId }); result[windowId] = getManifestTitle(state, { windowId });
}); });
...@@ -26,9 +26,19 @@ export function getWindowManifests(state) { ...@@ -26,9 +26,19 @@ export function getWindowManifests(state) {
return Object.values(state.windows).map(window => window.manifestId); return Object.values(state.windows).map(window => window.manifestId);
} }
/** */
export function getWindows(state) {
return state.windows || {};
}
/** */
export function getMaximizedWindows(state) {
return Object.values(getWindows(state)).filter(window => window.maximized === true);
}
/** */ /** */
export function getWindow(state, { windowId }) { export function getWindow(state, { windowId }) {
return state.windows && state.windows[windowId]; return getWindows(state)[windowId];
} }
/** Return the canvas index for a certain window. /** Return the canvas index for a certain window.
...@@ -109,9 +119,9 @@ export const getCompanionWindowsOfWindow = createSelector( ...@@ -109,9 +119,9 @@ export const getCompanionWindowsOfWindow = createSelector(
* @param {String} position * @param {String} position
* @return {String} * @return {String}
*/ */
export const getCompanionWindowForPosition = createSelector( export const getCompanionWindowsForPosition = createSelector(
[getCompanionWindowsOfWindow, (state, { position }) => position], [getCompanionWindowsOfWindow, (state, { position }) => position],
(companionWindows, position) => companionWindows.find(cw => cw.position === position), (companionWindows, position) => companionWindows.filter(cw => cw.position === position),
); );
export const getViewer = createSelector( export const getViewer = createSelector(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment