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

Merge pull request #43 from ProjectMirador/enforce-single-canvas-view-for-annotation-editor

Enforce single canvas view for annotation editor
parents 4f576d56 073bb17e
No related branches found
No related tags found
No related merge requests found
...@@ -26,6 +26,7 @@ function createWrapper(props, context = {}) { ...@@ -26,6 +26,7 @@ function createWrapper(props, context = {}) {
canvases: [], canvases: [],
receiveAnnotation, receiveAnnotation,
storageAdapter, storageAdapter,
switchToSingleCanvasView: () => undefined,
...context, ...context,
}} }}
> >
......
...@@ -14,6 +14,8 @@ function createWrapper(props) { ...@@ -14,6 +14,8 @@ function createWrapper(props) {
targetProps={{}} targetProps={{}}
addCompanionWindow={jest.fn()} addCompanionWindow={jest.fn()}
receiveAnnotation={jest.fn()} receiveAnnotation={jest.fn()}
switchToSingleCanvasView={jest.fn()}
windowViewType="single"
{...props} {...props}
/>, />,
); );
...@@ -40,6 +42,14 @@ describe('MiradorAnnotation', () => { ...@@ -40,6 +42,14 @@ describe('MiradorAnnotation', () => {
}, },
); );
}); });
it('opens single canvas view dialog if not in single view', () => {
wrapper = createWrapper({
windowViewType: 'book',
});
expect(wrapper.instance().state.singleCanvasDialogOpen).toBe(false);
wrapper.find(MiradorMenuButton).simulate('click');
expect(wrapper.instance().state.singleCanvasDialogOpen).toBe(true);
});
it('renders no export button if export or LocalStorageAdapter are not configured', () => { it('renders no export button if export or LocalStorageAdapter are not configured', () => {
wrapper = createWrapper(); wrapper = createWrapper();
expect(wrapper.find(MiradorMenuButton).some({ 'aria-label': 'Export local annotations for visible items' })).toBe(false); expect(wrapper.find(MiradorMenuButton).some({ 'aria-label': 'Export local annotations for visible items' })).toBe(false);
......
...@@ -86,6 +86,7 @@ class CanvasListItem extends Component { ...@@ -86,6 +86,7 @@ class CanvasListItem extends Component {
render() { render() {
const { children } = this.props; const { children } = this.props;
const { isHovering } = this.state; const { isHovering } = this.state;
const { windowViewType, toggleSingleCanvasDialogOpen } = this.context;
return ( return (
<div <div
onMouseEnter={this.handleMouseHover} onMouseEnter={this.handleMouseHover}
...@@ -107,7 +108,11 @@ class CanvasListItem extends Component { ...@@ -107,7 +108,11 @@ class CanvasListItem extends Component {
right: 0, right: 0,
}} }}
> >
<ToggleButton aria-label="Edit" onClick={this.handleEdit} value="edit"> <ToggleButton
aria-label="Edit"
onClick={windowViewType === 'single' ? this.handleEdit : toggleSingleCanvasDialogOpen}
value="edit"
>
<EditIcon /> <EditIcon />
</ToggleButton> </ToggleButton>
<ToggleButton aria-label="Delete" onClick={this.handleDelete} value="delete"> <ToggleButton aria-label="Delete" onClick={this.handleDelete} value="delete">
......
import React, { Component } from 'react';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Typography from '@material-ui/core/Typography';
import PropTypes from 'prop-types';
/**
* Dialog to enforce single view for annotation creation / editing
*/
class SingleCanvasDialog extends Component {
/** */
constructor(props) {
super(props);
this.confirm = this.confirm.bind(this);
}
/** */
confirm() {
const {
handleClose,
switchToSingleCanvasView,
} = this.props;
switchToSingleCanvasView();
handleClose();
}
/** */
render() {
const {
handleClose,
open,
} = this.props;
return (
<Dialog
aria-labelledby="single-canvas-dialog-title"
fullWidth
maxWidth="sm"
onClose={handleClose}
onEscapeKeyDown={handleClose}
open={open}
>
<DialogTitle id="single-canvas-dialog-title" disableTypography>
<Typography variant="h2">
Switch view type to single view?
</Typography>
</DialogTitle>
<DialogContent>
<DialogContentText variant="body1" color="inherit">
Annotations can only be edited in single canvas view type.
Switch view type to single view now?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>
Cancel
</Button>
<Button color="primary" onClick={this.confirm} variant="contained">
Switch to single view
</Button>
</DialogActions>
</Dialog>
);
}
}
SingleCanvasDialog.propTypes = {
handleClose: PropTypes.func.isRequired,
open: PropTypes.bool,
switchToSingleCanvasView: PropTypes.func.isRequired,
};
SingleCanvasDialog.defaultProps = {
open: false,
};
export default SingleCanvasDialog;
...@@ -2,17 +2,37 @@ import React, { Component } from 'react'; ...@@ -2,17 +2,37 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases'; import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases';
import * as actions from 'mirador/dist/es/src/state/actions'; import * as actions from 'mirador/dist/es/src/state/actions';
import { getWindowViewType } from 'mirador/dist/es/src/state/selectors';
import CanvasListItem from '../CanvasListItem'; import CanvasListItem from '../CanvasListItem';
import AnnotationActionsContext from '../AnnotationActionsContext'; import AnnotationActionsContext from '../AnnotationActionsContext';
import SingleCanvasDialog from '../SingleCanvasDialog';
/** */ /** */
class CanvasAnnotationsWrapper extends Component { class CanvasAnnotationsWrapper extends Component {
/** */
constructor(props) {
super(props);
this.state = {
singleCanvasDialogOpen: false,
};
this.toggleSingleCanvasDialogOpen = this.toggleSingleCanvasDialogOpen.bind(this);
}
/** */
toggleSingleCanvasDialogOpen() {
const { singleCanvasDialogOpen } = this.state;
this.setState({
singleCanvasDialogOpen: !singleCanvasDialogOpen,
});
}
/** */ /** */
render() { render() {
const { const {
addCompanionWindow, canvases, config, receiveAnnotation, TargetComponent, addCompanionWindow, annotationsOnCanvases, canvases, config, receiveAnnotation,
targetProps, annotationsOnCanvases, switchToSingleCanvasView, TargetComponent, targetProps, windowViewType,
} = this.props; } = this.props;
const { singleCanvasDialogOpen } = this.state;
const props = { const props = {
...targetProps, ...targetProps,
listContainerComponent: CanvasListItem, listContainerComponent: CanvasListItem,
...@@ -26,12 +46,21 @@ class CanvasAnnotationsWrapper extends Component { ...@@ -26,12 +46,21 @@ class CanvasAnnotationsWrapper extends Component {
config, config,
receiveAnnotation, receiveAnnotation,
storageAdapter: config.annotation.adapter, storageAdapter: config.annotation.adapter,
toggleSingleCanvasDialogOpen: this.toggleSingleCanvasDialogOpen,
windowId: targetProps.windowId, windowId: targetProps.windowId,
windowViewType,
}} }}
> >
<TargetComponent <TargetComponent
{...props} // eslint-disable-line react/jsx-props-no-spreading {...props} // eslint-disable-line react/jsx-props-no-spreading
/> />
{windowViewType !== 'single' && (
<SingleCanvasDialog
handleClose={this.toggleSingleCanvasDialogOpen}
open={singleCanvasDialogOpen}
switchToSingleCanvasView={switchToSingleCanvasView}
/>
)}
</AnnotationActionsContext.Provider> </AnnotationActionsContext.Provider>
); );
} }
...@@ -49,11 +78,13 @@ CanvasAnnotationsWrapper.propTypes = { ...@@ -49,11 +78,13 @@ CanvasAnnotationsWrapper.propTypes = {
}), }),
}).isRequired, }).isRequired,
receiveAnnotation: PropTypes.func.isRequired, receiveAnnotation: PropTypes.func.isRequired,
switchToSingleCanvasView: PropTypes.func.isRequired,
TargetComponent: PropTypes.oneOfType([ TargetComponent: PropTypes.oneOfType([
PropTypes.func, PropTypes.func,
PropTypes.node, PropTypes.node,
]).isRequired, ]).isRequired,
targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
windowViewType: PropTypes.string.isRequired,
}; };
CanvasAnnotationsWrapper.defaultProps = { CanvasAnnotationsWrapper.defaultProps = {
...@@ -65,6 +96,7 @@ CanvasAnnotationsWrapper.defaultProps = { ...@@ -65,6 +96,7 @@ CanvasAnnotationsWrapper.defaultProps = {
function mapStateToProps(state, { targetProps: { windowId } }) { function mapStateToProps(state, { targetProps: { windowId } }) {
const canvases = getVisibleCanvases(state, { windowId }); const canvases = getVisibleCanvases(state, { windowId });
const annotationsOnCanvases = {}; const annotationsOnCanvases = {};
canvases.forEach((canvas) => { canvases.forEach((canvas) => {
const anno = state.annotations[canvas.id]; const anno = state.annotations[canvas.id];
if (anno) { if (anno) {
...@@ -75,6 +107,7 @@ function mapStateToProps(state, { targetProps: { windowId } }) { ...@@ -75,6 +107,7 @@ function mapStateToProps(state, { targetProps: { windowId } }) {
annotationsOnCanvases, annotationsOnCanvases,
canvases, canvases,
config: state.config, config: state.config,
windowViewType: getWindowViewType(state, { windowId }),
}; };
} }
...@@ -86,6 +119,9 @@ const mapDispatchToProps = (dispatch, props) => ({ ...@@ -86,6 +119,9 @@ const mapDispatchToProps = (dispatch, props) => ({
receiveAnnotation: (targetId, id, annotation) => dispatch( receiveAnnotation: (targetId, id, annotation) => dispatch(
actions.receiveAnnotation(targetId, id, annotation), actions.receiveAnnotation(targetId, id, annotation),
), ),
switchToSingleCanvasView: () => dispatch(
actions.setWindowViewType(props.targetProps.windowId, 'single'),
),
}); });
export default { export default {
......
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as actions from 'mirador/dist/es/src/state/actions'; import * as actions from 'mirador/dist/es/src/state/actions';
import { getWindowViewType } from 'mirador/dist/es/src/state/selectors';
import AddBoxIcon from '@material-ui/icons/AddBox'; import AddBoxIcon from '@material-ui/icons/AddBox';
import GetAppIcon from '@material-ui/icons/GetApp'; import GetAppIcon from '@material-ui/icons/GetApp';
import { MiradorMenuButton } from 'mirador/dist/es/src/components/MiradorMenuButton'; import { MiradorMenuButton } from 'mirador/dist/es/src/components/MiradorMenuButton';
import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases'; import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases';
import SingleCanvasDialog from '../SingleCanvasDialog';
import AnnotationExportDialog from '../AnnotationExportDialog'; import AnnotationExportDialog from '../AnnotationExportDialog';
import LocalStorageAdapter from '../LocalStorageAdapter'; import LocalStorageAdapter from '../LocalStorageAdapter';
...@@ -15,9 +17,11 @@ class MiradorAnnotation extends Component { ...@@ -15,9 +17,11 @@ class MiradorAnnotation extends Component {
super(props); super(props);
this.state = { this.state = {
annotationExportDialogOpen: false, annotationExportDialogOpen: false,
singleCanvasDialogOpen: false,
}; };
this.openCreateAnnotationCompanionWindow = this.openCreateAnnotationCompanionWindow.bind(this); this.openCreateAnnotationCompanionWindow = this.openCreateAnnotationCompanionWindow.bind(this);
this.toggleCanvasExportDialog = this.toggleCanvasExportDialog.bind(this); this.toggleCanvasExportDialog = this.toggleCanvasExportDialog.bind(this);
this.toggleSingleCanvasDialogOpen = this.toggleSingleCanvasDialogOpen.bind(this);
} }
/** */ /** */
...@@ -31,6 +35,14 @@ class MiradorAnnotation extends Component { ...@@ -31,6 +35,14 @@ class MiradorAnnotation extends Component {
}); });
} }
/** */
toggleSingleCanvasDialogOpen() {
const { singleCanvasDialogOpen } = this.state;
this.setState({
singleCanvasDialogOpen: !singleCanvasDialogOpen,
});
}
/** */ /** */
toggleCanvasExportDialog(e) { toggleCanvasExportDialog(e) {
const { annotationExportDialogOpen } = this.state; const { annotationExportDialogOpen } = this.state;
...@@ -43,9 +55,14 @@ class MiradorAnnotation extends Component { ...@@ -43,9 +55,14 @@ class MiradorAnnotation extends Component {
/** */ /** */
render() { render() {
const { const {
canvases, config, TargetComponent, targetProps, canvases,
config,
switchToSingleCanvasView,
TargetComponent,
targetProps,
windowViewType,
} = this.props; } = this.props;
const { annotationExportDialogOpen } = this.state; const { annotationExportDialogOpen, singleCanvasDialogOpen } = this.state;
const storageAdapter = config.annotation && config.annotation.adapter('poke'); const storageAdapter = config.annotation && config.annotation.adapter('poke');
const offerExportDialog = config.annotation && storageAdapter instanceof LocalStorageAdapter const offerExportDialog = config.annotation && storageAdapter instanceof LocalStorageAdapter
&& config.annotation.exportLocalStorageAnnotations; && config.annotation.exportLocalStorageAnnotations;
...@@ -56,11 +73,18 @@ class MiradorAnnotation extends Component { ...@@ -56,11 +73,18 @@ class MiradorAnnotation extends Component {
/> />
<MiradorMenuButton <MiradorMenuButton
aria-label="Create new annotation" aria-label="Create new annotation"
onClick={this.openCreateAnnotationCompanionWindow} onClick={windowViewType === 'single' ? this.openCreateAnnotationCompanionWindow : this.toggleSingleCanvasDialogOpen}
size="small" size="small"
> >
<AddBoxIcon /> <AddBoxIcon />
</MiradorMenuButton> </MiradorMenuButton>
{ singleCanvasDialogOpen && (
<SingleCanvasDialog
open={singleCanvasDialogOpen}
handleClose={this.toggleSingleCanvasDialogOpen}
switchToSingleCanvasView={switchToSingleCanvasView}
/>
)}
{ offerExportDialog && ( { offerExportDialog && (
<MiradorMenuButton <MiradorMenuButton
aria-label="Export local annotations for visible items" aria-label="Export local annotations for visible items"
...@@ -94,11 +118,13 @@ MiradorAnnotation.propTypes = { ...@@ -94,11 +118,13 @@ MiradorAnnotation.propTypes = {
exportLocalStorageAnnotations: PropTypes.bool, exportLocalStorageAnnotations: PropTypes.bool,
}), }),
}).isRequired, }).isRequired,
switchToSingleCanvasView: PropTypes.func.isRequired,
TargetComponent: PropTypes.oneOfType([ TargetComponent: PropTypes.oneOfType([
PropTypes.func, PropTypes.func,
PropTypes.node, PropTypes.node,
]).isRequired, ]).isRequired,
targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
windowViewType: PropTypes.string.isRequired,
}; };
/** */ /** */
...@@ -106,12 +132,16 @@ const mapDispatchToProps = (dispatch, props) => ({ ...@@ -106,12 +132,16 @@ const mapDispatchToProps = (dispatch, props) => ({
addCompanionWindow: (content, additionalProps) => dispatch( addCompanionWindow: (content, additionalProps) => dispatch(
actions.addCompanionWindow(props.targetProps.windowId, { content, ...additionalProps }), actions.addCompanionWindow(props.targetProps.windowId, { content, ...additionalProps }),
), ),
switchToSingleCanvasView: () => dispatch(
actions.setWindowViewType(props.targetProps.windowId, 'single'),
),
}); });
/** */ /** */
const mapStateToProps = (state, { targetProps: { windowId } }) => ({ const mapStateToProps = (state, { targetProps: { windowId } }) => ({
canvases: getVisibleCanvases(state, { windowId }), canvases: getVisibleCanvases(state, { windowId }),
config: state.config, config: state.config,
windowViewType: getWindowViewType(state, { windowId }),
}); });
export default { export default {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment