Skip to content

Commit

Permalink
Merge pull request #49 from ProjectMirador/accessible-click-and-drag
Browse files Browse the repository at this point in the history
Accessibility updates to Alternate viewer section and copy buttons
  • Loading branch information
phil-plencner-hl authored Oct 11, 2023
2 parents e99511f + 33e2840 commit 449c29b
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 56 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Configurations for this plugin are injected when Mirador is initialized under th

| Config Key | Type | Description |
| --- | --- | --- |
| `dragAndDropInfoLink` | string/url | Provides a `What is IIIF` link under the Alternate Viewers / Drag & Drop that points to the configured URL. This will also be the base of the Drag & Drop link so that if a user clicks instead of drags, they will go the informational page (as opposed to the manifest which is the default behavior) |
| `iiifInfoLink` | string/url | Provides a `What is IIIF` link under the Add to another viewer section that points to the configured URL |
| `embedOption` | object | The configuration objects for the Embed section. You can use this if you're able to easily modify the manifest ID to a URL that can be used to embed the resource in an `<iframe>` (most likely in embedded mode where you're only displaying your own content) |
| `embedOption.enabled` | boolean | Configure whether to render the Embed section in the Share Dialog. |
| `embedOption.embedIframeAttributes` | string | A string that contains HTML attributes to be applied to the `<iframe>` embed code. (Default: `allowfullscreen frameborder="0"`) |
Expand Down
39 changes: 17 additions & 22 deletions __tests__/MiradorShareDialog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('Dialog', () => {
wrapper = createWrapper();
expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="h3"]').get(0).props.children).toEqual('Share link');
expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="h3"]').get(1).props.children).toEqual('Embed');
expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="h3"]').get(2).props.children).toEqual('Alternate viewer');
expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="h3"]').get(2).props.children).toEqual('Add to another viewer');
});

it('has a close button that calls closeShareDialog on click', () => {
Expand All @@ -49,15 +49,15 @@ describe('Dialog', () => {
wrapper = createWrapper();

expect(wrapper.find('WithStyles(ForwardRef(TextField))').length).toBe(1);
expect(wrapper.find('CopyToClipboard WithStyles(ForwardRef(Button))').props().children).toEqual('Copy');
expect(wrapper.find('CopyToClipboard WithStyles(ForwardRef(Button))').get(0).props.children).toEqual('Copy');
});

it('renders the TextField & CopyToClipboard components w/ the shareLinkText state value', () => {
wrapper = createWrapper();

wrapper.setState({ shareLinkText: 'http://example.com/iiif/manifest' });
expect(wrapper.find('WithStyles(ForwardRef(TextField))').props().defaultValue).toEqual('http://example.com/iiif/manifest');
expect(wrapper.find('CopyToClipboard').props().text).toEqual('http://example.com/iiif/manifest');
expect(wrapper.find('CopyToClipboard').get(0).props.text).toEqual('http://example.com/iiif/manifest');
});

it("sets the component's shareLinkText on TextField change", () => {
Expand All @@ -70,7 +70,7 @@ describe('Dialog', () => {
wrapper = createWrapper({ displayShareLink: false });

expect(wrapper.find('WithStyles(ForwardRef(TextField))').length).toBe(0);
expect(wrapper.find('CopyToClipboard').length).toBe(0);
expect(wrapper.find('CopyToClipboard').length).toBe(1);
});
});

Expand All @@ -84,34 +84,29 @@ describe('Dialog', () => {
});
});

describe('Alternate viewer section', () => {
it('renders a link, icon, and text', () => {
describe('Add to another viewer section', () => {
it('renders a link, icon, button, and text', () => {
wrapper = createWrapper();

expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="body1"] WithStyles(ForwardRef(Link)) IiifIcon').length).toBe(1);
expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="body1"]').props().children[1]).toEqual(
'Drag & drop this icon to any IIIF viewer.',
expect(wrapper.find('WithStyles(ForwardRef(Grid)) WithStyles(ForwardRef(Link)) IiifIcon').length).toBe(1);
expect(wrapper.find('WithStyles(ForwardRef(Grid)) CopyToClipboard WithStyles(ForwardRef(Button))').length).toBe(1);
expect(wrapper.find('WithStyles(ForwardRef(Grid)) WithStyles(ForwardRef(Typography))[variant="body1"]').get(0).props.children).toEqual(
'Drag & drop IIIF icon to add this resource to any IIIF viewer.',
);
expect(wrapper.find('WithStyles(ForwardRef(Grid)) WithStyles(ForwardRef(Typography))[variant="body1"]').get(2).props.children).toEqual(
'Copy & paste the resource\'s manifest into any IIIF viewer.',
);
});

it('renders the link with IIIF Drag & Drop Compliant URL (passing the manifest in a param)', () => {
expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="body1"] WithStyles(ForwardRef(Link))').props().href).toEqual(
'http://example.com/abc/iiif/manifest?manifest=http://example.com/abc/iiif/manifest',
);
const link = wrapper.find('WithStyles(ForwardRef(Grid)) WithStyles(ForwardRef(Link))').at(0);
expect(link.props().href).toEqual('http://example.com/abc/iiif/manifest?manifest=http://example.com/abc/iiif/manifest');
});

describe('when an info link is configured/passed in as a prop', () => {
it('renders Drag and Drop link with the passed URL as the base (where users will go if they click)', () => {
wrapper = createWrapper({ dragAndDropInfoLink: 'http://iiif.io/' });

expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="body1"] WithStyles(ForwardRef(Link))').at(0).props().href).toEqual(
'http://iiif.io/?manifest=http://example.com/abc/iiif/manifest',
);
});

it('renders a "What is IIIF" link', () => {
wrapper = createWrapper({ dragAndDropInfoLink: 'http://iiif.io/' });
const link = wrapper.find('WithStyles(ForwardRef(Typography))[variant="body1"] WithStyles(ForwardRef(Link))').at(1);
wrapper = createWrapper({ iiifInfoLink: 'http://iiif.io/' });
const link = wrapper.find('WithStyles(ForwardRef(Typography))[variant="body1"] WithStyles(ForwardRef(Link))');
expect(link.props().children).toEqual('What is IIIF?');
expect(link.props().href).toEqual('http://iiif.io/');
});
Expand Down
2 changes: 1 addition & 1 deletion demo/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const config = {
loadedManifest: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest',
}],
miradorSharePlugin: {
dragAndDropInfoLink: 'https://iiif.io',
iiifInfoLink: 'https://iiif.io',
embedOption: {
enabled: true,
embedUrlReplacePattern: [
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mirador-share-plugin",
"version": "0.12.0",
"version": "0.13.0",
"description": "mirador-share-plugin React component",
"main": "lib/index.js",
"module": "es/index.js",
Expand All @@ -20,7 +20,9 @@
"test:coverage": "jest --coverage",
"test:watch": "jest --watch"
},
"dependencies": {},
"dependencies": {
"notistack": "^3.0.1"
},
"peerDependencies": {
"@material-ui/core": "^4.7.2",
"@material-ui/icons": "^4.5.1",
Expand Down
97 changes: 72 additions & 25 deletions src/MiradorShareDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import Divider from '@material-ui/core/Divider';
import Link from '@material-ui/core/Link';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { SnackbarProvider, enqueueSnackbar } from 'notistack';
import { getManifestoInstance } from 'mirador/dist/es/src/state/selectors/manifests';
import { getContainerId } from 'mirador/dist/es/src/state/selectors/config';
import ScrollIndicatedDialogContent from 'mirador/dist/es/src/containers/ScrollIndicatedDialogContent';
Expand All @@ -34,7 +36,7 @@ const mapStateToProps = (state, { windowId }) => {
embedIframeTitle: embedOption.embedIframeTitle,
manifestIdReplacePattern: miradorSharePluginConfig.shareLink
&& miradorSharePluginConfig.shareLink.manifestIdReplacePattern,
dragAndDropInfoLink: miradorSharePluginConfig.dragAndDropInfoLink,
iiifInfoLink: miradorSharePluginConfig.iiifInfoLink,
manifestId: (getManifestoInstance(state, { windowId }) || {}).id,
open: (state.windowDialogs[windowId] && state.windowDialogs[windowId].openDialog === 'share'),
syncIframeDimensions: embedOption.syncIframeDimensions,
Expand Down Expand Up @@ -63,29 +65,21 @@ export class MiradorShareDialog extends Component {
}

dragAndDropUrl() {
const { dragAndDropInfoLink, manifestId } = this.props;
let baseUrl;

baseUrl = manifestId;

if (dragAndDropInfoLink) {
baseUrl = dragAndDropInfoLink;
}
const { manifestId } = this.props;
const baseUrl = manifestId;

return `${baseUrl}?manifest=${manifestId}`;
}

whatIsThisLink() {
const { dragAndDropInfoLink } = this.props;
const { iiifInfoLink } = this.props;

if (!dragAndDropInfoLink) return null;
if (!iiifInfoLink) return null;

return (
<React.Fragment>
{' '}
[
<Link href={dragAndDropInfoLink}>What is IIIF?</Link>
]
<Link href={iiifInfoLink}>What is IIIF?</Link>
</React.Fragment>
);
}
Expand Down Expand Up @@ -135,6 +129,13 @@ export class MiradorShareDialog extends Component {
Share
</Typography>
</DialogTitle>
<SnackbarProvider
maxSnack={1}
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
}}
/>
<ScrollIndicatedDialogContent>
{displayShareLink && (
<React.Fragment>
Expand All @@ -149,7 +150,19 @@ export class MiradorShareDialog extends Component {
/>
{' '}
<CopyToClipboard text={shareLinkText}>
<Button className={classes.copyButton} variant="outlined" color="primary" aria-label="Copy link">Copy</Button>
<Button
className={classes.copyButton}
variant="outlined"
color="primary"
aria-label="Copy link to clipboard"
onClick={() => enqueueSnackbar((
<Typography variant="body1">
Link copied to clipboard!
</Typography>
), { variant: 'success' })}
>
Copy
</Button>
</CopyToClipboard>
</div>
<Divider />
Expand All @@ -168,14 +181,41 @@ export class MiradorShareDialog extends Component {
<Divider />
</React.Fragment>
)}
<Typography className={classes.h3} variant="h3">Alternate viewer</Typography>
<Typography variant="body1">
<Link href={this.dragAndDropUrl()} className={classes.iiifLink}>
<IiiifIcon className={classes.iiifIcon} />
</Link>
Drag & drop this icon to any IIIF viewer.
{this.whatIsThisLink()}
</Typography>
<Typography className={classes.h3} variant="h3">Add to another viewer</Typography>
<Grid container spacing={1} className={classes.grid}>
<Grid item xs>
<Typography variant="body1">
Drag & drop IIIF icon to add this resource to any IIIF viewer.
</Typography>
<Link href={this.dragAndDropUrl()} className={classes.iiifLink}>
<IiiifIcon className={classes.iiifIcon} />
</Link>
</Grid>
<Grid item xs={1}>
<Typography variant="body1">or</Typography>
</Grid>
<Grid item xs>
<Typography variant="body1">
Copy & paste the resource&apos;s manifest into any IIIF viewer.
</Typography>
<CopyToClipboard text={this.dragAndDropUrl()}>
<Button
className={classes.copyButton}
variant="outlined"
color="primary"
aria-label="Copy manifest to clipboard"
onClick={() => enqueueSnackbar((
<Typography variant="body1">
Manifest copied to clipboard!
</Typography>
), { variant: 'success' })}
>
Copy
</Button>
</CopyToClipboard>
</Grid>
</Grid>
<Typography variant="body1">{this.whatIsThisLink()}</Typography>
</ScrollIndicatedDialogContent>
<DialogActions>
<Button onClick={closeShareDialog} color="primary">
Expand All @@ -196,12 +236,13 @@ MiradorShareDialog.propTypes = {
iiifLink: PropTypes.string,
inputContainer: PropTypes.string,
shareLinkInput: PropTypes.string,
grid: PropTypes.string,
}).isRequired,
closeShareDialog: PropTypes.func.isRequired,
containerId: PropTypes.string.isRequired,
displayEmbedOption: PropTypes.bool,
displayShareLink: PropTypes.bool,
dragAndDropInfoLink: PropTypes.string,
iiifInfoLink: PropTypes.string,
embedIframeAttributes: PropTypes.string,
embedIframeTitle: PropTypes.string,
embedUrlReplacePattern: PropTypes.arrayOf(
Expand All @@ -227,7 +268,7 @@ MiradorShareDialog.propTypes = {
MiradorShareDialog.defaultProps = {
displayEmbedOption: false,
displayShareLink: false,
dragAndDropInfoLink: null,
iiifInfoLink: 'https://iiif.io',
embedIframeAttributes: 'allowfullscreen frameborder="0"',
embedIframeTitle: 'Image viewer',
embedUrlReplacePattern: [],
Expand All @@ -252,6 +293,8 @@ const styles = theme => ({
},
iiifIcon: {
verticalAlign: 'text-bottom',
cursor: 'grab',
paddingTop: '12px',
},
inputContainer: {
alignItems: 'flex-end',
Expand All @@ -262,6 +305,10 @@ const styles = theme => ({
shareLinkInput: {
paddingTop: '12px',
},
grid: {
textAlign: 'center',
paddingTop: '12px',
},
});

export default {
Expand Down
27 changes: 24 additions & 3 deletions src/MiradorShareEmbed.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { createMuiTheme, withStyles } from '@material-ui/core/styles';
import { createTheme, withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import FormControl from '@material-ui/core/FormControl';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import FormLabel from '@material-ui/core/FormLabel';
import Radio from '@material-ui/core/Radio';
import RadioGroup from '@material-ui/core/RadioGroup';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { SnackbarProvider, enqueueSnackbar } from 'notistack';
import EmbedSizeIcon from './EmbedSizeIcon';

/**
Expand Down Expand Up @@ -62,7 +64,7 @@ class MiradorShareEmbed extends Component {
const { classes } = this.props;
const { selectedSize } = this.state;
const sizes = MiradorShareEmbed.sizes();
const iconColor = createMuiTheme().palette.grey[500];
const iconColor = createTheme().palette.grey[500];
const icon = (width, height) => (
<EmbedSizeIcon
fillColor={iconColor}
Expand Down Expand Up @@ -139,6 +141,13 @@ class MiradorShareEmbed extends Component {

return (
<React.Fragment>
<SnackbarProvider
maxSnack={1}
anchorOrigin={{
vertical: 'top',
horizontal: 'center',
}}
/>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend" className={classes.legend}>Select viewer size</FormLabel>
<RadioGroup
Expand All @@ -162,7 +171,19 @@ class MiradorShareEmbed extends Component {
variant="filled"
/>
<CopyToClipboard text={this.embedCode()}>
<Button className={classes.copyButton} variant="outlined" color="primary" aria-label="Copy code">Copy</Button>
<Button
className={classes.copyButton}
variant="outlined"
color="primary"
aria-label="Copy code to clipboard"
onClick={() => enqueueSnackbar((
<Typography variant="body1">
Code copied to clipboard!
</Typography>
), { variant: 'success' })}
>
Copy
</Button>
</CopyToClipboard>
</div>
</FormControl>
Expand Down
4 changes: 2 additions & 2 deletions src/miradorSharePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({
});

const mapStateToProps = (state, { windowId }) => ({
dragAndDropInfoLink: state.config.miradorSharePlugin
&& state.config.miradorSharePlugin.dragAndDropInfoLink,
iiifInfoLink: state.config.miradorSharePlugin
&& state.config.miradorSharePlugin.iiifInfoLink,
manifestId: getManifestoInstance(state, { windowId }).id,
});

Expand Down

0 comments on commit 449c29b

Please sign in to comment.