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

Merge pull request #1939 from ProjectMirador/alternativ-normalize-companion-window-state

Alternative normalize companion window state
parents 92c6cb66 d317ac6b
Branches
Tags
No related merge requests found
Showing
with 281 additions and 205 deletions
...@@ -3,45 +3,51 @@ import ActionTypes from '../../../src/state/actions/action-types'; ...@@ -3,45 +3,51 @@ import ActionTypes from '../../../src/state/actions/action-types';
describe('companionWindow actions', () => { describe('companionWindow actions', () => {
describe('addCompanionWindow', () => { describe('addCompanionWindow', () => {
it('should create a new companion window with the given options', () => { it('should return correct action object', () => {
const options = { const payload = {
id: 'abc123',
windowId: 'x',
content: 'info', content: 'info',
position: 'right', position: 'right',
foo: 'bar',
}; };
const action = actions.addCompanionWindow(payload);
expect(action.type).toBe(ActionTypes.ADD_COMPANION_WINDOW);
expect(action.payload).toEqual(payload);
});
const expectedAction = { it('should set the correct default values', () => {
type: ActionTypes.SET_COMPANION_WINDOW, const payload = {};
id: 'abc123', const defaults = { foo: 'bar' };
windowId: 'x', const action = actions.addCompanionWindow(payload, defaults);
content: 'info', expect(action.payload.foo).toBe('bar');
position: 'right',
};
expect(actions.addCompanionWindow(options)).toEqual(expectedAction);
}); });
it('should generate a new companionWindow ID if one is not provided', () => { it('should generate a new companionWindow ID', () => {
const options = { const payload = {};
windowId: 'x',
content: 'info',
position: 'right',
};
expect(actions.addCompanionWindow(options).id).toEqual( expect(actions.addCompanionWindow(payload).id).toEqual(
expect.stringMatching(/^cw-\w+-\w+/), expect.stringMatching(/^cw-\w+-\w+/),
); );
}); });
}); });
describe('removeCompanionWindow', () => { describe('updateCompanionWindow', () => {
it('should send the REMOVE_COMPANION_WINDOW action with the given ID', () => { it('should return correct action object', () => {
const expectedAction = { const payload = {
type: ActionTypes.REMOVE_COMPANION_WINDOW, content: 'info',
id: 'abc123', position: 'right',
}; };
const action = actions.updateCompanionWindow('cw-123', payload);
expect(action.type).toBe(ActionTypes.UPDATE_COMPANION_WINDOW);
expect(action.id).toBe('cw-123');
expect(action.payload).toEqual(payload);
});
});
expect(actions.removeCompanionWindow('abc123')).toEqual(expectedAction); describe('removeCompanionWindow', () => {
it('should return correct action object', () => {
const action = actions.removeCompanionWindow('cw-123');
expect(action.type).toBe(ActionTypes.REMOVE_COMPANION_WINDOW);
expect(action.id).toBe('cw-123');
}); });
}); });
}); });
...@@ -27,6 +27,20 @@ describe('window actions', () => { ...@@ -27,6 +27,20 @@ describe('window actions', () => {
expect(actions.addWindow(options)).toEqual(expectedAction); expect(actions.addWindow(options)).toEqual(expectedAction);
}); });
}); });
describe('updateWindow', () => {
it('should return correct action object', () => {
const payload = {
foo: 1,
bar: 2,
};
const action = actions.updateWindow('window-123', payload);
expect(action.type).toBe(ActionTypes.UPDATE_WINDOW);
expect(action.id).toBe('window-123');
expect(action.payload).toEqual(payload);
});
});
describe('removeWindow', () => { describe('removeWindow', () => {
it('removes the window and returns windowId', () => { it('removes the window and returns windowId', () => {
const id = 'abc123'; const id = 'abc123';
...@@ -88,20 +102,41 @@ describe('window actions', () => { ...@@ -88,20 +102,41 @@ describe('window actions', () => {
describe('popOutCompanionWindow', () => { describe('popOutCompanionWindow', () => {
it('returns a thunk which dispatches the appropriate actions', () => { it('returns a thunk which dispatches the appropriate actions', () => {
const mockDispatch = jest.fn(); const mockState = {
windows: {
abc123: {
companionWindowIds: ['cw-1'],
},
},
};
const mockDispatch = jest.fn(() => ({ id: 'cw-1' }));
const mockGetState = jest.fn(() => mockState);
const windowId = 'abc123'; const windowId = 'abc123';
const panelType = 'info'; const panelType = 'info';
const position = 'right'; const position = 'right';
const thunk = actions.popOutCompanionWindow(windowId, panelType, position); const thunk = actions.popOutCompanionWindow(windowId, panelType, position);
expect(typeof thunk).toEqual('function'); expect(typeof thunk).toEqual('function');
thunk(mockDispatch); thunk(mockDispatch, mockGetState);
expect(mockDispatch).toHaveBeenCalledTimes(2); expect(mockDispatch).toHaveBeenCalledTimes(4);
const cwId = mockDispatch.mock.calls[0][0].id;
expect(mockDispatch).toHaveBeenCalledWith({ expect(mockDispatch).toHaveBeenNthCalledWith(1, {
type: ActionTypes.SET_COMPANION_WINDOW, id: cwId, windowId, content: panelType, position, type: ActionTypes.REMOVE_COMPANION_WINDOW,
id: 'cw-1',
});
const addCompanionWindowAction = mockDispatch.mock.calls[1][0];
expect(addCompanionWindowAction.type).toBe(ActionTypes.ADD_COMPANION_WINDOW);
expect(addCompanionWindowAction.payload).toEqual({ content: 'info', position: 'right' });
expect(addCompanionWindowAction.id.startsWith('cw-')).toBe(true);
expect(mockDispatch).toHaveBeenNthCalledWith(3, {
type: ActionTypes.UPDATE_WINDOW,
id: 'abc123',
payload: { companionWindowIds: ['cw-1'] },
}); });
expect(mockDispatch).toHaveBeenCalledWith({
expect(mockDispatch).toHaveBeenNthCalledWith(4, {
type: ActionTypes.TOGGLE_WINDOW_SIDE_BAR_PANEL, windowId, panelType: 'closed', type: ActionTypes.TOGGLE_WINDOW_SIDE_BAR_PANEL, windowId, panelType: 'closed',
}); });
}); });
......
...@@ -35,7 +35,7 @@ describe('CompanionWindow', () => { ...@@ -35,7 +35,7 @@ describe('CompanionWindow', () => {
}); });
describe('when the close companion window button is clicked', () => { describe('when the close companion window button is clicked', () => {
it('triggers the removeCompanionWindow prop with the appropriate args', () => { it('triggers the onCloseClick prop with the appropriate args', () => {
const removeCompanionWindowEvent = jest.fn(); const removeCompanionWindowEvent = jest.fn();
companionWindow = createWrapper({ companionWindow = createWrapper({
onCloseClick: removeCompanionWindowEvent, onCloseClick: removeCompanionWindowEvent,
...@@ -44,7 +44,7 @@ describe('CompanionWindow', () => { ...@@ -44,7 +44,7 @@ describe('CompanionWindow', () => {
const closeButton = companionWindow.find('WithStyles(IconButton)[aria-label="closeCompanionWindow"]'); const closeButton = companionWindow.find('WithStyles(IconButton)[aria-label="closeCompanionWindow"]');
closeButton.simulate('click'); closeButton.simulate('click');
expect(removeCompanionWindowEvent).toHaveBeenCalledTimes(1); expect(removeCompanionWindowEvent).toHaveBeenCalledTimes(1);
expect(removeCompanionWindowEvent).toHaveBeenCalledWith('abc123', 'x'); expect(removeCompanionWindowEvent).toHaveBeenCalledWith('x', 'abc123');
}); });
}); });
}); });
...@@ -5,24 +5,34 @@ import CompanionWindow from '../../../src/containers/CompanionWindow'; ...@@ -5,24 +5,34 @@ import CompanionWindow from '../../../src/containers/CompanionWindow';
import WindowSideBar from '../../../src/containers/WindowSideBar'; import WindowSideBar from '../../../src/containers/WindowSideBar';
import WindowViewer from '../../../src/containers/WindowViewer'; import WindowViewer from '../../../src/containers/WindowViewer';
/** create wrapper */
function createWrapper(props) {
return shallow(
<WindowMiddleContent
companionWindowIds={['cw1', 'cw-2']}
window={{ id: 'window-1' }}
manifest={{}}
{...props}
/>,
);
}
describe('WindowMiddleContent', () => { describe('WindowMiddleContent', () => {
let wrapper;
let manifest;
it('should render outer element', () => { it('should render outer element', () => {
wrapper = shallow(<WindowMiddleContent window={window} />); const wrapper = createWrapper();
expect(wrapper.find('.mirador-window-middle-content')).toHaveLength(1); expect(wrapper.find('.mirador-window-middle-content')).toHaveLength(1);
}); });
it('should render <CompanionWindow>', () => { it('should render all <CompanionWindow> components', () => {
wrapper = shallow(<WindowMiddleContent window={window} rightCompanionWindowId="x" />); const wrapper = createWrapper();
expect(wrapper.find(CompanionWindow)).toHaveLength(1); expect(wrapper.find(CompanionWindow)).toHaveLength(2);
}); });
it('should render <WindowSideBar>', () => { it('should render <WindowSideBar>', () => {
wrapper = shallow(<WindowMiddleContent window={window} />); const wrapper = createWrapper();
expect(wrapper.find(WindowSideBar)).toHaveLength(1); expect(wrapper.find(WindowSideBar)).toHaveLength(1);
}); });
it('should render <WindowViewer> if manifest is present', () => { it('should render <WindowViewer> if manifest is present', () => {
manifest = { id: 456, isFetching: false }; const manifest = { id: 456, isFetching: false };
wrapper = shallow(<WindowMiddleContent window={window} manifest={manifest} />); const wrapper = createWrapper({ manifest });
expect(wrapper.find(WindowViewer)).toHaveLength(1); expect(wrapper.find(WindowViewer)).toHaveLength(1);
}); });
}); });
...@@ -20,7 +20,7 @@ function createWrapper(props) { ...@@ -20,7 +20,7 @@ function createWrapper(props) {
windowId="xyz" windowId="xyz"
classes={{}} classes={{}}
t={str => str} t={str => str}
removeWindow={() => {}} closeWindow={() => {}}
toggleWindowSideBar={() => {}} toggleWindowSideBar={() => {}}
{...props} {...props}
/>, />,
...@@ -67,8 +67,8 @@ describe('WindowTopBar', () => { ...@@ -67,8 +67,8 @@ describe('WindowTopBar', () => {
}); });
it('passes correct props to <Button/>', () => { it('passes correct props to <Button/>', () => {
const removeWindow = jest.fn(); const closeWindow = jest.fn();
const wrapper = createWrapper({ removeWindow }); const wrapper = createWrapper({ closeWindow });
expect(wrapper.find(IconButton).last().props().onClick).toBe(removeWindow); expect(wrapper.find(IconButton).last().props().onClick).toBe(closeWindow);
}); });
}); });
import { companionWindowsReducer } from '../../../src/state/reducers/companion_windows'; import { companionWindowsReducer } from '../../../src/state/reducers/companionWindows';
import ActionTypes from '../../../src/state/actions/action-types'; import ActionTypes from '../../../src/state/actions/action-types';
describe('companionWindowsReducer', () => { describe('companionWindowsReducer', () => {
describe('SET_COMPANION_WINDOW', () => { describe('ADD_COMPANION_WINDOW', () => {
it('adds a new companion window if a companion window for the given position does not exist', () => { it('adds a new companion window', () => {
const action = { const action = {
type: ActionTypes.SET_COMPANION_WINDOW, type: ActionTypes.ADD_COMPANION_WINDOW,
id: 'abc123', id: 'abc123',
windowId: 'x', payload: { content: 'info', position: 'right' },
position: 'right',
content: 'info',
}; };
const beforeState = {}; const beforeState = {};
const expectedState = { const expectedState = {
abc123: { abc123: {
id: 'abc123', windowId: 'x', position: 'right', content: 'info', position: 'right',
content: 'info',
}, },
}; };
expect(companionWindowsReducer(beforeState, action)).toEqual(expectedState); expect(companionWindowsReducer(beforeState, action)).toEqual(expectedState);
}); });
});
it('updates an existing companion window based on windowId and position (regardless of companionWindowId)', () => { describe('UPDATE_COMPANION_WINDOW', () => {
it('updates an existing companion window', () => {
const action = { const action = {
type: ActionTypes.SET_COMPANION_WINDOW, type: ActionTypes.UPDATE_COMPANION_WINDOW,
id: 'xyz321', id: 'abc123',
windowId: 'x', payload: { content: 'canvases', foo: 'bar' },
position: 'right',
content: 'info',
}; };
const beforeState = { const beforeState = {
abc123: { abc123: {
id: 'abc123', windowId: 'x', position: 'right', content: 'canvas_navigation', position: 'right',
content: 'info',
}, },
}; };
const expectedState = { const expectedState = {
abc123: { abc123: {
id: 'abc123', windowId: 'x', position: 'right', content: 'info', position: 'right',
content: 'canvases',
foo: 'bar',
}, },
}; };
expect(companionWindowsReducer(beforeState, action)).toEqual(expectedState); expect(companionWindowsReducer(beforeState, action)).toEqual(expectedState);
}); });
}); });
describe('REMOVE_COMPANION_WINDOW', () => { describe('REMOVE_COMPANION_WINDOW', () => {
it('removes the companion window w/ the given ID', () => { it('should remove a companion window', () => {
const action = { type: ActionTypes.REMOVE_COMPANION_WINDOW, id: 'abc123' };
const beforeState = { abc123: { id: 'abc123' } };
const expectedState = {};
expect(companionWindowsReducer(beforeState, action)).toEqual(expectedState);
});
});
describe('REMOVE_WINDOW', () => {
it('removes any companion window that has the given windowId', () => {
const action = { const action = {
type: ActionTypes.REMOVE_WINDOW, type: ActionTypes.REMOVE_COMPANION_WINDOW,
windowId: 'x', id: 'abc123',
}; };
const beforeState = { const beforeState = {
abc123: { windowId: 'x' }, abc123: {
abc456: { windowId: 'y' }, position: 'right',
abc789: { windowId: 'x' }, content: 'info',
}; },
const expectedState = {
abc456: { windowId: 'y' },
}; };
const expectedState = {};
expect(companionWindowsReducer(beforeState, action)).toEqual(expectedState); expect(companionWindowsReducer(beforeState, action)).toEqual(expectedState);
}); });
}); });
......
...@@ -121,61 +121,6 @@ describe('windows reducer', () => { ...@@ -121,61 +121,6 @@ describe('windows reducer', () => {
}); });
}); });
describe('SET_COMPANION_WINDOW', () => {
it('adds the id to the companin array', () => {
const action = {
type: ActionTypes.SET_COMPANION_WINDOW,
id: 'x',
windowId: 'abc123',
};
const before = {
abc123: { companionWindowIds: [] },
};
const after = {
abc123: { companionWindowIds: ['x'] },
};
expect(windowsReducer(before, action)).toEqual(after);
});
it('does not add id key that already exists', () => {
const action = {
type: ActionTypes.SET_COMPANION_WINDOW,
id: 'x',
windowId: 'abc123',
position: 'right',
panelType: 'info',
};
const before = {
abc123: { companionWindowIds: ['x'] },
};
const after = {
abc123: { companionWindowIds: ['x'] },
};
expect(windowsReducer(before, action)).toEqual(after);
});
});
describe('REMOVE_COMPANION_WINDOW', () => {
it('removes the id of the companionWindow from the ids array', () => {
const action = {
type: ActionTypes.REMOVE_COMPANION_WINDOW,
id: 'x',
windowId: 'abc123',
};
const before = {
abc123: { companionWindowIds: ['x'] },
};
const after = {
abc123: { companionWindowIds: [] },
};
expect(windowsReducer(before, action)).toEqual(after);
});
});
it('should handle NEXT_CANVAS', () => { it('should handle NEXT_CANVAS', () => {
expect(windowsReducer({ expect(windowsReducer({
abc123: { abc123: {
...@@ -249,4 +194,28 @@ describe('windows reducer', () => { ...@@ -249,4 +194,28 @@ describe('windows reducer', () => {
}, },
}); });
}); });
describe('UPDATE_WINDOW', () => {
it('updates an existing window', () => {
const action = {
type: ActionTypes.UPDATE_WINDOW,
id: 'abc123',
payload: { foo: 11, baz: 33 },
};
const beforeState = {
abc123: {
foo: 1,
bar: 2,
},
};
const expectedState = {
abc123: {
foo: 11,
bar: 2,
baz: 33,
},
};
expect(windowsReducer(beforeState, action)).toEqual(expectedState);
});
});
}); });
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
"deepmerge": "^3.1.0", "deepmerge": "^3.1.0",
"dompurify": "^1.0.9", "dompurify": "^1.0.9",
"i18next": "^14.0.1", "i18next": "^14.0.1",
"immutable": "^4.0.0-rc.12",
"intersection-observer": "^0.5.1", "intersection-observer": "^0.5.1",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"manifesto.js": "^3.0.9", "manifesto.js": "^3.0.9",
......
...@@ -48,7 +48,7 @@ class CompanionWindow extends Component { ...@@ -48,7 +48,7 @@ class CompanionWindow extends Component {
<IconButton <IconButton
aria-label={t('closeCompanionWindow')} aria-label={t('closeCompanionWindow')}
className={classes.closeButton} className={classes.closeButton}
onClick={() => { onCloseClick(id, windowId); }} onClick={() => { onCloseClick(windowId, id); }}
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
......
...@@ -32,29 +32,24 @@ class WindowMiddleContent extends Component { ...@@ -32,29 +32,24 @@ class WindowMiddleContent extends Component {
* Render the component * Render the component
*/ */
render() { render() {
const { rightCompanionWindowId, window } = this.props; const { companionWindowIds, window } = this.props;
return ( return (
<div className={ns('window-middle-content')}> <div className={ns('window-middle-content')}>
<WindowSideBar windowId={window.id} /> <WindowSideBar windowId={window.id} />
{this.renderViewer()} {this.renderViewer()}
{ // We can pass an array of ids here when we want to support { companionWindowIds.map(id => <CompanionWindow key={id} id={id} windowId={window.id} />) }
// multiple companion windows in a particular position
rightCompanionWindowId
&& <CompanionWindow id={rightCompanionWindowId} windowId={window.id} />
}
</div> </div>
); );
} }
} }
WindowMiddleContent.propTypes = { WindowMiddleContent.propTypes = {
rightCompanionWindowId: PropTypes.string, companionWindowIds: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
window: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types window: PropTypes.object.isRequired, // 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
}; };
WindowMiddleContent.defaultProps = { WindowMiddleContent.defaultProps = {
rightCompanionWindowId: null,
manifest: null, manifest: null,
}; };
......
...@@ -24,7 +24,7 @@ class WindowTopBar extends Component { ...@@ -24,7 +24,7 @@ class WindowTopBar extends Component {
*/ */
render() { render() {
const { const {
removeWindow, windowId, classes, toggleWindowSideBar, t, manifestTitle, closeWindow, windowId, classes, toggleWindowSideBar, t, manifestTitle,
} = this.props; } = this.props;
return ( return (
<AppBar position="relative"> <AppBar position="relative">
...@@ -46,7 +46,7 @@ class WindowTopBar extends Component { ...@@ -46,7 +46,7 @@ class WindowTopBar extends Component {
color="inherit" color="inherit"
className={ns('window-close')} className={ns('window-close')}
aria-label={t('closeWindow')} aria-label={t('closeWindow')}
onClick={removeWindow} onClick={closeWindow}
> >
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
...@@ -58,7 +58,7 @@ class WindowTopBar extends Component { ...@@ -58,7 +58,7 @@ class WindowTopBar extends Component {
WindowTopBar.propTypes = { WindowTopBar.propTypes = {
manifestTitle: PropTypes.string, manifestTitle: PropTypes.string,
removeWindow: PropTypes.func.isRequired, closeWindow: PropTypes.func.isRequired,
windowId: PropTypes.string.isRequired, windowId: PropTypes.string.isRequired,
classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
toggleWindowSideBar: PropTypes.func.isRequired, toggleWindowSideBar: PropTypes.func.isRequired,
......
...@@ -27,7 +27,7 @@ const mapStateToProps = (state, { id }) => { ...@@ -27,7 +27,7 @@ const mapStateToProps = (state, { id }) => {
* @private * @private
*/ */
const mapDispatchToProps = { const mapDispatchToProps = {
onCloseClick: actions.removeCompanionWindow, onCloseClick: actions.closeCompanionWindow,
}; };
const enhance = compose( const enhance = compose(
......
import { compose } from 'redux'; import { compose } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { getCompanionWindowForPosition } from '../state/selectors'; import { getCompantionWindowIds } from '../state/selectors';
import miradorWithPlugins from '../lib/miradorWithPlugins'; import miradorWithPlugins from '../lib/miradorWithPlugins';
import WindowMiddleContent from '../components/WindowMiddleContent'; import WindowMiddleContent from '../components/WindowMiddleContent';
/** */ /** */
const mapStateToProps = (state, { window }) => { const mapStateToProps = (state, { window }) => ({
const rightCompanionWindow = getCompanionWindowForPosition(state, window.id, 'right'); companionWindowIds: getCompantionWindowIds(state, window.id),
});
return {
rightCompanionWindowId: rightCompanionWindow && rightCompanionWindow.id,
};
};
const enhance = compose( const enhance = compose(
connect(mapStateToProps, null), connect(mapStateToProps, null),
......
...@@ -17,7 +17,7 @@ const mapStateToProps = (state, { windowId }) => ({ ...@@ -17,7 +17,7 @@ const mapStateToProps = (state, { windowId }) => ({
* @private * @private
*/ */
const mapDispatchToProps = (dispatch, { windowId }) => ({ const mapDispatchToProps = (dispatch, { windowId }) => ({
removeWindow: () => dispatch(actions.removeWindow(windowId)), closeWindow: () => dispatch(actions.closeWindow(windowId)),
toggleWindowSideBar: () => dispatch(actions.toggleWindowSideBar(windowId)), toggleWindowSideBar: () => dispatch(actions.toggleWindowSideBar(windowId)),
}); });
......
const ActionTypes = { const ActionTypes = {
SET_COMPANION_WINDOW: 'SET_COMPANION_WINDOW', ADD_COMPANION_WINDOW: 'ADD_COMPANION_WINDOW',
UPDATE_COMPANION_WINDOW: 'UPDATE_COMPANION_WINDOW',
REMOVE_COMPANION_WINDOW: 'REMOVE_COMPANION_WINDOW', REMOVE_COMPANION_WINDOW: 'REMOVE_COMPANION_WINDOW',
UPDATE_WINDOW: 'UPDATE_WINDOW',
FOCUS_WINDOW: 'FOCUS_WINDOW', FOCUS_WINDOW: 'FOCUS_WINDOW',
SET_WORKSPACE_FULLSCREEN: 'SET_WORKSPACE_FULLSCREEN', SET_WORKSPACE_FULLSCREEN: 'SET_WORKSPACE_FULLSCREEN',
ADD_MANIFEST: 'ADD_MANIFEST', ADD_MANIFEST: 'ADD_MANIFEST',
......
import uuid from 'uuid/v4'; import uuid from 'uuid/v4';
import ActionTypes from './action-types'; import ActionTypes from './action-types';
/** const defaultProps = {
* addCompanionWindow - action creator content: null,
* position: null,
* @param {Object} options };
* @memberof ActionCreators
*/ /** */
export function addCompanionWindow(companionWindow) { export function addCompanionWindow(payload, defaults = defaultProps) {
return { return {
type: ActionTypes.SET_COMPANION_WINDOW, type: ActionTypes.ADD_COMPANION_WINDOW,
id: `cw-${uuid()}`, id: `cw-${uuid()}`,
...companionWindow, payload: { ...defaults, ...payload },
}; };
} }
/** /** */
* removeCompanionWindow - action creator export function updateCompanionWindow(id, payload) {
* return { type: ActionTypes.UPDATE_COMPANION_WINDOW, id, payload };
* @param {Object} options }
* @memberof ActionCreators
*/ /** */
export function removeCompanionWindow(id, windowId) { export function removeCompanionWindow(id) {
return { return { type: ActionTypes.REMOVE_COMPANION_WINDOW, id };
type: ActionTypes.REMOVE_COMPANION_WINDOW,
id,
windowId,
};
} }
import uuid from 'uuid/v4'; import uuid from 'uuid/v4';
import ActionTypes from './action-types'; import ActionTypes from './action-types';
import { addCompanionWindow } from './companionWindow'; import { addCompanionWindow, removeCompanionWindow } from './companionWindow';
/** /**
* focusWindow - action creator * focusWindow - action creator
...@@ -34,6 +34,11 @@ export function addWindow(options) { ...@@ -34,6 +34,11 @@ export function addWindow(options) {
return { type: ActionTypes.ADD_WINDOW, window: { ...defaultOptions, ...options } }; return { type: ActionTypes.ADD_WINDOW, window: { ...defaultOptions, ...options } };
} }
/** */
export function updateWindow(id, payload) {
return { type: ActionTypes.UPDATE_WINDOW, id, payload };
}
/** /**
* removeWindow - action creator * removeWindow - action creator
* *
...@@ -76,10 +81,40 @@ export function toggleWindowSideBarPanel(windowId, panelType) { ...@@ -76,10 +81,40 @@ export function toggleWindowSideBarPanel(windowId, panelType) {
* @memberof ActionCreators * @memberof ActionCreators
*/ */
export function popOutCompanionWindow(windowId, panelType, position) { export function popOutCompanionWindow(windowId, panelType, position) {
return ((dispatch) => { return (dispatch, getState) => {
dispatch(addCompanionWindow({ windowId, content: panelType, position })); const { companionWindowIds } = getState().windows[windowId];
companionWindowIds.map(id => dispatch(removeCompanionWindow(id)));
const action = dispatch(addCompanionWindow({ content: panelType, position }));
const companionWindowId = action.id;
dispatch(updateWindow(windowId, { companionWindowIds: [companionWindowId] }));
dispatch(toggleWindowSideBarPanel(windowId, 'closed')); dispatch(toggleWindowSideBarPanel(windowId, 'closed'));
}); };
}
/**
* Clean up state and remove window
*/
export function closeWindow(windowId) {
return (dispatch, getState) => {
const { companionWindowIds } = getState().windows[windowId];
companionWindowIds.map(id => dispatch(removeCompanionWindow(id)));
dispatch(removeWindow(windowId));
};
}
/**
* Close companion window and remove reference from window
*/
export function closeCompanionWindow(windowId, companionWindowId) {
return (dispatch, getState) => {
dispatch(removeCompanionWindow(companionWindowId));
const companionWindowIds = getState().windows[windowId].companionWindowIds
.filter(id => id !== companionWindowId);
dispatch(updateWindow(windowId, { companionWindowIds }));
};
} }
/** /**
......
import {
removeIn, setIn, updateIn, merge,
} from 'immutable';
import ActionTypes from '../actions/action-types';
/** */
export function companionWindowsReducer(state = {}, action) {
switch (action.type) {
case ActionTypes.ADD_COMPANION_WINDOW:
return setIn(state, [action.id], action.payload);
case ActionTypes.UPDATE_COMPANION_WINDOW:
return updateIn(state, [action.id], orig => merge(orig, action.payload));
case ActionTypes.REMOVE_COMPANION_WINDOW:
return removeIn(state, [action.id]);
default:
return state;
}
}
import ActionTypes from '../actions/action-types';
import { getCompanionWindowForPosition } from '../selectors';
/**
* companionWindowsReducer
*/
export const companionWindowsReducer = (state = {}, action) => {
switch (action.type) {
// action params: id
case ActionTypes.REMOVE_COMPANION_WINDOW:
return Object.keys(state).reduce((object, key) => {
if (state[key].id !== action.id) {
object[key] = state[key]; // eslint-disable-line no-param-reassign
}
return object;
}, {});
// action params: id, windowId, position, content
case ActionTypes.SET_COMPANION_WINDOW: {
const companionWindowForPosition = getCompanionWindowForPosition(
{ companionWindows: state }, action.windowId, action.position,
);
let cwId;
let cwObject;
if (companionWindowForPosition) {
cwId = companionWindowForPosition.id;
cwObject = {
...companionWindowForPosition,
position: action.position,
content: action.content,
};
} else {
cwId = action.id;
cwObject = {
id: cwId, windowId: action.windowId, position: action.position, content: action.content,
};
}
return { ...state, [cwId]: cwObject };
}
// action params: windowId
case ActionTypes.REMOVE_WINDOW:
return Object.keys(state).reduce((object, key) => {
if (state[key].windowId !== action.windowId) {
object[key] = state[key]; // eslint-disable-line no-param-reassign
}
return object;
}, {});
default:
return state;
}
};
export * from './companion_windows'; export * from './companionWindows';
export * from './workspace'; export * from './workspace';
export * from './windows'; export * from './windows';
export * from './manifests'; export * from './manifests';
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment