diff --git a/__tests__/src/components/CollectionDialog.test.js b/__tests__/src/components/CollectionDialog.test.js index 64928539f0aa225405a7bae2e02aadba52dbc90d..d821f7dfc65cba4fd849e28de2f932c49995dff8 100644 --- a/__tests__/src/components/CollectionDialog.test.js +++ b/__tests__/src/components/CollectionDialog.test.js @@ -41,4 +41,10 @@ describe('CollectionDialog', () => { expect(wrapper.find(DialogActions).find(Button).first().simulate('click')); expect(hideCollectionDialog).toHaveBeenCalled(); }); + it('clicking the hide button fires hideWindowCollectionDialog in window variant', () => { + const hideWindowCollectionDialog = jest.fn(); + const wrapper = createWrapper({ hideWindowCollectionDialog, variant: 'window' }); + expect(wrapper.find(DialogActions).find(Button).first().simulate('click')); + expect(hideWindowCollectionDialog).toHaveBeenCalled(); + }); }); diff --git a/__tests__/src/reducers/windows.test.js b/__tests__/src/reducers/windows.test.js index cf59fe0d948f110b4affadbf3939350784169463..467243be3ec2e9aae260ffbecd66fd3fe7998875 100644 --- a/__tests__/src/reducers/windows.test.js +++ b/__tests__/src/reducers/windows.test.js @@ -381,4 +381,38 @@ describe('windows reducer', () => { type: ActionTypes.IMPORT_MIRADOR_STATE, })).toEqual({ new: 'stuff' }); }); + + describe('SHOW_WINDOW_COLLECTION_DIALOG', () => { + it('handles SHOW_WINDOW_COLLECTION_DIALOG by toggling the given window\'s collection dialog', () => { + const beforeState = { abc123: { collectionDialogOn: false } }; + const action = { + collectionPath: [], manifestId: 'def456', type: ActionTypes.SHOW_WINDOW_COLLECTION_DIALOG, windowId: 'abc123', + }; + const expectedState = { + abc123: { collectionDialogOn: true, collectionManifestId: 'def456', collectionPath: [] }, + }; + + expect(windowsReducer(beforeState, action)).toEqual(expectedState); + }); + }); + + describe('HIDE_WINDOW_COLLECTION_DIALOG', () => { + it('handles HIDE_WINDOW_COLLECTION_DIALOG by toggling the given window\'s collection dialog', () => { + const beforeState = { + abc123: { + collectionDialogOn: true, collectionManifestId: 'def456', collectionPath: [], + }, + }; + const action = { + type: ActionTypes.HIDE_WINDOW_COLLECTION_DIALOG, + windowId: 'abc123', + }; + + const expectedState = { + abc123: { collectionDialogOn: false, collectionManifestId: 'def456', collectionPath: [] }, + }; + + expect(windowsReducer(beforeState, action)).toEqual(expectedState); + }); + }); }); diff --git a/src/components/CollectionDialog.js b/src/components/CollectionDialog.js index 9930997f3c7daa509f9d0093f5df9ac18abc9c61..a1412e9bb9dfb76b4945ff56a2d34c061d609657 100644 --- a/src/components/CollectionDialog.js +++ b/src/components/CollectionDialog.js @@ -29,7 +29,7 @@ function asArray(value) { } /** - * a simple dialog providing the possibility to switch the theme + * a dialog providing the possibility to select the collection */ export class CollectionDialog extends Component { /** */ @@ -46,6 +46,7 @@ export class CollectionDialog extends Component { super(props); this.state = { filter: null }; + this.hideDialog = this.hideDialog.bind(this); } /** */ @@ -53,23 +54,40 @@ export class CollectionDialog extends Component { this.setState({ filter }); } + /** */ + hideDialog() { + const { + hideCollectionDialog, hideWindowCollectionDialog, variant, windowId, + } = this.props; + if (variant === 'window') { + hideWindowCollectionDialog(windowId); + } else { + hideCollectionDialog(); + } + } + + /** */ + showCollectionDialog(...args) { + const { showCollectionDialog, showWindowCollectionDialog, variant } = this.props; + return variant === 'window' ? showWindowCollectionDialog(...args) : showCollectionDialog(...args); + } + /** */ selectCollection(c) { const { collectionPath, manifestId, - showCollectionDialog, windowId, } = this.props; - showCollectionDialog(c.id, [...collectionPath, manifestId], windowId); + this.showCollectionDialog(c.id, [...collectionPath, manifestId], windowId); } /** */ goToPreviousCollection() { - const { collectionPath, showCollectionDialog, windowId } = this.props; + const { collectionPath, windowId } = this.props; - showCollectionDialog( + this.showCollectionDialog( collectionPath[collectionPath.length - 1], collectionPath.slice(0, -1), windowId, @@ -81,7 +99,6 @@ export class CollectionDialog extends Component { const { addWindow, collectionPath, - hideCollectionDialog, manifestId, setWorkspaceAddVisibility, updateWindow, @@ -96,21 +113,21 @@ export class CollectionDialog extends Component { addWindow({ collectionPath: [...collectionPath, manifestId], manifestId: m.id }); } - hideCollectionDialog(); + this.hideDialog(); setWorkspaceAddVisibility(false); } /** */ placeholder() { - const { classes, containerId, hideCollectionDialog, windowId } = this.props; + const { classes, containerId, windowId } = this.props; return ( <Dialog className={classes.dialog} - onClose={hideCollectionDialog} + onClose={this.hideDialog} open container={document.querySelector(`#${containerId} #${windowId}`)} - BackdropProps={{ classes: classes.dialog }} + BackdropProps={this.backdropProps()} > <DialogTitle id="select-collection" disableTypography> <Skeleton className={classes.placeholder} variant="text" /> @@ -123,6 +140,12 @@ export class CollectionDialog extends Component { ); } + /** */ + backdropProps() { + const { classes } = this.props; + return { classes: { root: classes.dialog } }; + } + /** */ render() { const { @@ -130,7 +153,6 @@ export class CollectionDialog extends Component { collection, containerId, error, - hideCollectionDialog, isMultipart, manifest, ready, @@ -158,9 +180,9 @@ export class CollectionDialog extends Component { return ( <Dialog className={classes.dialog} - onClose={hideCollectionDialog} + onClose={this.hideDialog} container={document.querySelector(`#${containerId} #${windowId}`)} - BackdropProps={{ classes: { root: classes.dialog }}} + BackdropProps={this.backdropProps()} open > <DialogTitle id="select-collection" disableTypography> @@ -195,7 +217,7 @@ export class CollectionDialog extends Component { <> <Typography variant="subtitle2" component="dt">{t('rights')}</Typography> { rights.map(v => ( - <Typography variant="body1" component="dd"> + <Typography variant="body1" component="dd" key={v}> <Link target="_blank" rel="noopener noreferrer" href={v}> {v} </Link> @@ -238,7 +260,7 @@ export class CollectionDialog extends Component { )} </ScrollIndicatedDialogContent> <DialogActions> - <Button onClick={hideCollectionDialog}> + <Button onClick={this.hideDialog}> {t('close')} </Button> </DialogActions> @@ -252,16 +274,20 @@ CollectionDialog.propTypes = { classes: PropTypes.objectOf(PropTypes.string).isRequired, collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types collectionPath: PropTypes.arrayOf(PropTypes.string), + containerId: PropTypes.string, error: PropTypes.string, hideCollectionDialog: PropTypes.func.isRequired, + hideWindowCollectionDialog: PropTypes.func.isRequired, isMultipart: PropTypes.bool, manifest: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types manifestId: PropTypes.string.isRequired, ready: PropTypes.bool, setWorkspaceAddVisibility: PropTypes.func.isRequired, showCollectionDialog: PropTypes.func.isRequired, + showWindowCollectionDialog: PropTypes.func.isRequired, t: PropTypes.func.isRequired, updateWindow: PropTypes.func.isRequired, + variant: PropTypes.oneOf(['window', 'workspace']), windowId: PropTypes.string, }; @@ -272,5 +298,6 @@ CollectionDialog.defaultProps = { error: null, isMultipart: false, ready: false, + variant: 'workspace', windowId: null, }; diff --git a/src/components/PrimaryWindow.js b/src/components/PrimaryWindow.js index d440b9759d93acdc7b921c4393b866f4209469b5..aeda8cfe493552e2fb261bf6384d8213e7cba11a 100644 --- a/src/components/PrimaryWindow.js +++ b/src/components/PrimaryWindow.js @@ -5,6 +5,7 @@ import WindowSideBar from '../containers/WindowSideBar'; import WindowViewer from '../containers/WindowViewer'; import GalleryView from '../containers/GalleryView'; import CompanionArea from '../containers/CompanionArea'; +import CollectionDialog from '../containers/CollectionDialog'; import ns from '../config/css-ns'; const SelectCollection = lazy(() => import('../containers/SelectCollection')); @@ -22,13 +23,16 @@ export class PrimaryWindow extends Component { */ renderViewer() { const { - isCollection, isFetching, view, windowId, + isCollection, isCollectionDialogVisible, isFetching, view, windowId, } = this.props; if (isCollection) { return ( - <SelectCollection - windowId={windowId} - /> + <> + { isCollectionDialogVisible && <CollectionDialog variant="window" windowId={windowId} /> } + <SelectCollection + windowId={windowId} + /> + </> ); } if (isFetching === false) { @@ -66,6 +70,7 @@ export class PrimaryWindow extends Component { PrimaryWindow.propTypes = { classes: PropTypes.objectOf(PropTypes.string).isRequired, isCollection: PropTypes.bool, + isCollectionDialogVisible: PropTypes.bool, isFetching: PropTypes.bool, view: PropTypes.string, windowId: PropTypes.string.isRequired, @@ -73,6 +78,7 @@ PrimaryWindow.propTypes = { PrimaryWindow.defaultProps = { isCollection: false, + isCollectionDialogVisible: false, isFetching: false, view: undefined, }; diff --git a/src/components/SelectCollection.js b/src/components/SelectCollection.js index decfdf6deabd97cf10e3238e22d0e31294434458..c4c8aebdef91b14ed536227c41d5afa17b6e98bc 100644 --- a/src/components/SelectCollection.js +++ b/src/components/SelectCollection.js @@ -19,9 +19,9 @@ export class SelectCollection extends Component { /** */ openCollectionDialog() { const { - collectionPath, manifestId, showCollectionDialog, windowId, + collectionPath, manifestId, showWindowCollectionDialog, windowId, } = this.props; - showCollectionDialog(manifestId, collectionPath.slice(0, -1), windowId); + showWindowCollectionDialog(manifestId, collectionPath.slice(0, -1), windowId); } /** */ @@ -54,7 +54,7 @@ export class SelectCollection extends Component { SelectCollection.propTypes = { collectionPath: PropTypes.arrayOf(PropTypes.string), manifestId: PropTypes.string, - showCollectionDialog: PropTypes.func.isRequired, + showWindowCollectionDialog: PropTypes.func.isRequired, t: PropTypes.func, windowId: PropTypes.string, }; diff --git a/src/containers/CollectionDialog.js b/src/containers/CollectionDialog.js index 325640145683690aee9042aa2fbcf5d84ed56eeb..21fe169f7fb10924d8c8377769be651f62aba184 100644 --- a/src/containers/CollectionDialog.js +++ b/src/containers/CollectionDialog.js @@ -5,7 +5,7 @@ import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { - getContainerId, getManifest, getManifestoInstance, getSequenceBehaviors, + getContainerId, getManifest, getManifestoInstance, getSequenceBehaviors, getWindow, } from '../state/selectors'; import { CollectionDialog } from '../components/CollectionDialog'; @@ -17,8 +17,10 @@ import { CollectionDialog } from '../components/CollectionDialog'; const mapDispatchToProps = { addWindow: actions.addWindow, hideCollectionDialog: actions.hideCollectionDialog, + hideWindowCollectionDialog: actions.hideWindowCollectionDialog, setWorkspaceAddVisibility: actions.setWorkspaceAddVisibility, showCollectionDialog: actions.showCollectionDialog, + showWindowCollectionDialog: actions.showWindowCollectionDialog, updateWindow: actions.updateWindow, }; @@ -27,8 +29,10 @@ const mapDispatchToProps = { * @memberof CollectionDialog * @private */ -const mapStateToProps = (state) => { - const { collectionPath, collectionManifestId: manifestId } = state.workspace; +const mapStateToProps = (state, { variant, windowId }) => { + const { collectionPath, collectionManifestId: manifestId } = variant === 'window' + ? getWindow(state, { windowId }) + : state.workspace; const manifest = getManifest(state, { manifestId }); const collectionId = collectionPath && collectionPath[collectionPath.length - 1]; @@ -44,7 +48,7 @@ const mapStateToProps = (state) => { manifestId, open: state.workspace.collectionDialogOn, ready: manifest && !!manifest.json, - windowId: state.workspace.collectionUpdateWindowId, + windowId: state.workspace.collectionUpdateWindowId || windowId, }; }; diff --git a/src/containers/PrimaryWindow.js b/src/containers/PrimaryWindow.js index 50332d7c899f5dd8c5e877d2c3142806e5c203c5..35a86dfa27dd2de03e166223ef6de34a649091e4 100644 --- a/src/containers/PrimaryWindow.js +++ b/src/containers/PrimaryWindow.js @@ -2,7 +2,7 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; -import { getManifestoInstance } from '../state/selectors'; +import { getManifestoInstance, getWindow } from '../state/selectors'; import { PrimaryWindow } from '../components/PrimaryWindow'; /** */ @@ -10,6 +10,7 @@ const mapStateToProps = (state, { windowId }) => { const manifestoInstance = getManifestoInstance(state, { windowId }); return { isCollection: manifestoInstance && manifestoInstance.isCollection(), + isCollectionDialogVisible: getWindow(state, { windowId }).collectionDialogOn, }; }; diff --git a/src/containers/SelectCollection.js b/src/containers/SelectCollection.js index 446c32711f233e68c9ca4fe904e643c4e71d0ab2..a620126daef65a56608c71a96306e18be598ff3c 100644 --- a/src/containers/SelectCollection.js +++ b/src/containers/SelectCollection.js @@ -20,7 +20,7 @@ const mapStateToProps = (state, { windowId }) => { }; const mapDispatchToProps = { - showCollectionDialog: actions.showCollectionDialog, + showWindowCollectionDialog: actions.showWindowCollectionDialog, }; /** */ const styles = (theme) => ({ diff --git a/src/state/actions/action-types.js b/src/state/actions/action-types.js index c1f124472712257053c1c755913de1a2e0d09099..69a4ea2a79da71408ccc007d6e0978069a14cc6d 100644 --- a/src/state/actions/action-types.js +++ b/src/state/actions/action-types.js @@ -72,6 +72,8 @@ const ActionTypes = { REMOVE_RESOURCE: 'mirador/REMOVE_RESOURCE', SHOW_COLLECTION_DIALOG: 'mirador/SHOW_COLLECTION_DIALOG', HIDE_COLLECTION_DIALOG: 'mirador/HIDE_COLLECTION_DIALOG', + SHOW_WINDOW_COLLECTION_DIALOG: 'mirador/SHOW_WINDOW_COLLECTION_DIALOG', + HIDE_WINDOW_COLLECTION_DIALOG: 'mirador/HIDE_WINDOW_COLLECTION_DIALOG', }; export default ActionTypes; diff --git a/src/state/actions/window.js b/src/state/actions/window.js index 6ba1a236967c8d934b0af8718e0497cbca89e281..e9f5da2002f16a253cb6c595d7f81c649d7d3b9e 100644 --- a/src/state/actions/window.js +++ b/src/state/actions/window.js @@ -186,3 +186,21 @@ export function setWindowViewType(windowId, viewType) { windowId, }; } + +/** */ +export function showWindowCollectionDialog(manifestId, collectionPath = [], windowId) { + return { + collectionPath, + manifestId, + type: ActionTypes.SHOW_WINDOW_COLLECTION_DIALOG, + windowId, + }; +} + +/** */ +export function hideWindowCollectionDialog(windowId) { + return { + type: ActionTypes.HIDE_WINDOW_COLLECTION_DIALOG, + windowId, + }; +} diff --git a/src/state/reducers/windows.js b/src/state/reducers/windows.js index 37ef215d0f236598413ea7d9baa5455840371c3c..847adbe505ce3cc4051798e01146be4797f7aa90 100644 --- a/src/state/reducers/windows.js +++ b/src/state/reducers/windows.js @@ -150,6 +150,24 @@ export const windowsReducer = (state = {}, action) => { suggestedSearches: undefined, }, }; + case ActionTypes.SHOW_WINDOW_COLLECTION_DIALOG: + return { + ...state, + [action.windowId]: { + ...state[action.windowId], + collectionDialogOn: true, + collectionManifestId: action.manifestId, + collectionPath: action.collectionPath, + }, + }; + case ActionTypes.HIDE_WINDOW_COLLECTION_DIALOG: + return { + ...state, + [action.windowId]: { + ...state[action.windowId], + collectionDialogOn: false, + }, + }; default: return state; } diff --git a/src/state/sagas/app.js b/src/state/sagas/app.js index 83a2c407696335ceb736469b5f1eb6a0fe8af72b..e915f68cf70fb5e22d538ed652e2b72c73d6344e 100644 --- a/src/state/sagas/app.js +++ b/src/state/sagas/app.js @@ -53,5 +53,6 @@ export default function* appSaga() { takeEvery(ActionTypes.IMPORT_MIRADOR_STATE, importState), takeEvery(ActionTypes.IMPORT_CONFIG, importConfig), takeEvery(ActionTypes.SHOW_COLLECTION_DIALOG, fetchCollectionManifests), + takeEvery(ActionTypes.SHOW_WINDOW_COLLECTION_DIALOG, fetchCollectionManifests), ]); } diff --git a/src/state/sagas/windows.js b/src/state/sagas/windows.js index 8d2449a07fb600447f17331dc67b4aaaef2a2c3f..20a76ed73277e73db5ba037ec0706c2e7386d3ed 100644 --- a/src/state/sagas/windows.js +++ b/src/state/sagas/windows.js @@ -13,7 +13,7 @@ import { fetchSearch, receiveManifest, fetchInfoResponse, - showCollectionDialog, + showWindowCollectionDialog, } from '../actions'; import { getSearchForWindow, getSearchAnnotationsForCompanionWindow, @@ -237,7 +237,7 @@ export function* determineAndShowCollectionDialog(manifestId, windowId) { const manifestoInstance = yield select(getManifestoInstance, { manifestId }); const isCollection = manifestoInstance.isCollection(); if (isCollection) { - yield put(showCollectionDialog(manifestId, [], windowId)); + yield put(showWindowCollectionDialog(manifestId, [], windowId)); } }