Skip to content

Commit

Permalink
Merge pull request #43 from ProjectMirador/enforce-single-canvas-view…
Browse files Browse the repository at this point in the history
…-for-annotation-editor

Enforce single canvas view for annotation editor
  • Loading branch information
mejackreed authored Apr 7, 2021
2 parents 4f576d5 + 073bb17 commit ec86f5d
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 6 deletions.
1 change: 1 addition & 0 deletions __tests__/CanvasListItem.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function createWrapper(props, context = {}) {
canvases: [],
receiveAnnotation,
storageAdapter,
switchToSingleCanvasView: () => undefined,
...context,
}}
>
Expand Down
10 changes: 10 additions & 0 deletions __tests__/miradorAnnotationPlugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ function createWrapper(props) {
targetProps={{}}
addCompanionWindow={jest.fn()}
receiveAnnotation={jest.fn()}
switchToSingleCanvasView={jest.fn()}
windowViewType="single"
{...props}
/>,
);
Expand All @@ -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', () => {
wrapper = createWrapper();
expect(wrapper.find(MiradorMenuButton).some({ 'aria-label': 'Export local annotations for visible items' })).toBe(false);
Expand Down
7 changes: 6 additions & 1 deletion src/CanvasListItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class CanvasListItem extends Component {
render() {
const { children } = this.props;
const { isHovering } = this.state;
const { windowViewType, toggleSingleCanvasDialogOpen } = this.context;
return (
<div
onMouseEnter={this.handleMouseHover}
Expand All @@ -107,7 +108,11 @@ class CanvasListItem extends Component {
right: 0,
}}
>
<ToggleButton aria-label="Edit" onClick={this.handleEdit} value="edit">
<ToggleButton
aria-label="Edit"
onClick={windowViewType === 'single' ? this.handleEdit : toggleSingleCanvasDialogOpen}
value="edit"
>
<EditIcon />
</ToggleButton>
<ToggleButton aria-label="Delete" onClick={this.handleDelete} value="delete">
Expand Down
80 changes: 80 additions & 0 deletions src/SingleCanvasDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
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;
40 changes: 38 additions & 2 deletions src/plugins/canvasAnnotationsPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,37 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases';
import * as actions from 'mirador/dist/es/src/state/actions';
import { getWindowViewType } from 'mirador/dist/es/src/state/selectors';
import CanvasListItem from '../CanvasListItem';
import AnnotationActionsContext from '../AnnotationActionsContext';
import SingleCanvasDialog from '../SingleCanvasDialog';

/** */
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() {
const {
addCompanionWindow, canvases, config, receiveAnnotation, TargetComponent,
targetProps, annotationsOnCanvases,
addCompanionWindow, annotationsOnCanvases, canvases, config, receiveAnnotation,
switchToSingleCanvasView, TargetComponent, targetProps, windowViewType,
} = this.props;
const { singleCanvasDialogOpen } = this.state;
const props = {
...targetProps,
listContainerComponent: CanvasListItem,
Expand All @@ -26,12 +46,21 @@ class CanvasAnnotationsWrapper extends Component {
config,
receiveAnnotation,
storageAdapter: config.annotation.adapter,
toggleSingleCanvasDialogOpen: this.toggleSingleCanvasDialogOpen,
windowId: targetProps.windowId,
windowViewType,
}}
>
<TargetComponent
{...props} // eslint-disable-line react/jsx-props-no-spreading
/>
{windowViewType !== 'single' && (
<SingleCanvasDialog
handleClose={this.toggleSingleCanvasDialogOpen}
open={singleCanvasDialogOpen}
switchToSingleCanvasView={switchToSingleCanvasView}
/>
)}
</AnnotationActionsContext.Provider>
);
}
Expand All @@ -49,11 +78,13 @@ CanvasAnnotationsWrapper.propTypes = {
}),
}).isRequired,
receiveAnnotation: PropTypes.func.isRequired,
switchToSingleCanvasView: PropTypes.func.isRequired,
TargetComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.node,
]).isRequired,
targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
windowViewType: PropTypes.string.isRequired,
};

CanvasAnnotationsWrapper.defaultProps = {
Expand All @@ -65,6 +96,7 @@ CanvasAnnotationsWrapper.defaultProps = {
function mapStateToProps(state, { targetProps: { windowId } }) {
const canvases = getVisibleCanvases(state, { windowId });
const annotationsOnCanvases = {};

canvases.forEach((canvas) => {
const anno = state.annotations[canvas.id];
if (anno) {
Expand All @@ -75,6 +107,7 @@ function mapStateToProps(state, { targetProps: { windowId } }) {
annotationsOnCanvases,
canvases,
config: state.config,
windowViewType: getWindowViewType(state, { windowId }),
};
}

Expand All @@ -86,6 +119,9 @@ const mapDispatchToProps = (dispatch, props) => ({
receiveAnnotation: (targetId, id, annotation) => dispatch(
actions.receiveAnnotation(targetId, id, annotation),
),
switchToSingleCanvasView: () => dispatch(
actions.setWindowViewType(props.targetProps.windowId, 'single'),
),
});

export default {
Expand Down
36 changes: 33 additions & 3 deletions src/plugins/miradorAnnotationPlugin.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
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 GetAppIcon from '@material-ui/icons/GetApp';
import { MiradorMenuButton } from 'mirador/dist/es/src/components/MiradorMenuButton';
import { getVisibleCanvases } from 'mirador/dist/es/src/state/selectors/canvases';
import SingleCanvasDialog from '../SingleCanvasDialog';
import AnnotationExportDialog from '../AnnotationExportDialog';
import LocalStorageAdapter from '../LocalStorageAdapter';

Expand All @@ -15,9 +17,11 @@ class MiradorAnnotation extends Component {
super(props);
this.state = {
annotationExportDialogOpen: false,
singleCanvasDialogOpen: false,
};
this.openCreateAnnotationCompanionWindow = this.openCreateAnnotationCompanionWindow.bind(this);
this.toggleCanvasExportDialog = this.toggleCanvasExportDialog.bind(this);
this.toggleSingleCanvasDialogOpen = this.toggleSingleCanvasDialogOpen.bind(this);
}

/** */
Expand All @@ -31,6 +35,14 @@ class MiradorAnnotation extends Component {
});
}

/** */
toggleSingleCanvasDialogOpen() {
const { singleCanvasDialogOpen } = this.state;
this.setState({
singleCanvasDialogOpen: !singleCanvasDialogOpen,
});
}

/** */
toggleCanvasExportDialog(e) {
const { annotationExportDialogOpen } = this.state;
Expand All @@ -43,9 +55,14 @@ class MiradorAnnotation extends Component {
/** */
render() {
const {
canvases, config, TargetComponent, targetProps,
canvases,
config,
switchToSingleCanvasView,
TargetComponent,
targetProps,
windowViewType,
} = this.props;
const { annotationExportDialogOpen } = this.state;
const { annotationExportDialogOpen, singleCanvasDialogOpen } = this.state;
const storageAdapter = config.annotation && config.annotation.adapter('poke');
const offerExportDialog = config.annotation && storageAdapter instanceof LocalStorageAdapter
&& config.annotation.exportLocalStorageAnnotations;
Expand All @@ -56,11 +73,18 @@ class MiradorAnnotation extends Component {
/>
<MiradorMenuButton
aria-label="Create new annotation"
onClick={this.openCreateAnnotationCompanionWindow}
onClick={windowViewType === 'single' ? this.openCreateAnnotationCompanionWindow : this.toggleSingleCanvasDialogOpen}
size="small"
>
<AddBoxIcon />
</MiradorMenuButton>
{ singleCanvasDialogOpen && (
<SingleCanvasDialog
open={singleCanvasDialogOpen}
handleClose={this.toggleSingleCanvasDialogOpen}
switchToSingleCanvasView={switchToSingleCanvasView}
/>
)}
{ offerExportDialog && (
<MiradorMenuButton
aria-label="Export local annotations for visible items"
Expand Down Expand Up @@ -94,24 +118,30 @@ MiradorAnnotation.propTypes = {
exportLocalStorageAnnotations: PropTypes.bool,
}),
}).isRequired,
switchToSingleCanvasView: PropTypes.func.isRequired,
TargetComponent: PropTypes.oneOfType([
PropTypes.func,
PropTypes.node,
]).isRequired,
targetProps: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
windowViewType: PropTypes.string.isRequired,
};

/** */
const mapDispatchToProps = (dispatch, props) => ({
addCompanionWindow: (content, additionalProps) => dispatch(
actions.addCompanionWindow(props.targetProps.windowId, { content, ...additionalProps }),
),
switchToSingleCanvasView: () => dispatch(
actions.setWindowViewType(props.targetProps.windowId, 'single'),
),
});

/** */
const mapStateToProps = (state, { targetProps: { windowId } }) => ({
canvases: getVisibleCanvases(state, { windowId }),
config: state.config,
windowViewType: getWindowViewType(state, { windowId }),
});

export default {
Expand Down

0 comments on commit ec86f5d

Please sign in to comment.