Skip to content

Commit

Permalink
Icon changes in CUL plugins for XML and canvas nav clarity
Browse files Browse the repository at this point in the history
- use page metaphor for canvas navigation
- use curly brace icon for XML to minimize confusion with navigation chevrons
- do not display canvas navigation when there is a single canvas
  • Loading branch information
barmintor committed Aug 15, 2024
1 parent 5fb121c commit 8b23083
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 3 deletions.
2 changes: 1 addition & 1 deletion __tests__/integration/mirador/cul.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
{ manifestId: 'https://iiif.harvardartmuseums.org/manifests/object/299843', provider: 'Other Organization' },
],
},
[...Mirador.culPlugins.downloadDialogPlugin].concat([...Mirador.culPlugins.viewXmlPlugin]).concat([...Mirador.culPlugins.citationsSidebarPlugin]).concat([...Mirador.culPlugins.videojsPlugin]).concat([...Mirador.culPlugins.canvasRelatedLinksPlugin]).concat([...Mirador.culPlugins.hintingSideBar])
[...Mirador.culPlugins.downloadDialogPlugin].concat([...Mirador.culPlugins.viewXmlPlugin]).concat([...Mirador.culPlugins.citationsSidebarPlugin]).concat([...Mirador.culPlugins.videojsPlugin]).concat([...Mirador.culPlugins.canvasRelatedLinksPlugin]).concat([...Mirador.culPlugins.hintingSideBar]).concat([...Mirador.culPlugins.viewerNavigation])
);
</script>
</body>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { render, screen } from 'test-utils';
import userEvent from '@testing-library/user-event';
import { PageIconViewerNavigation } from '../../../../../src/culPlugins/mirador-pageIconViewerNavigation/components/PageIconViewerNavigation';

/** create wrapper */
function createWrapper(props) {
return render(
<PageIconViewerNavigation
classes={{}}
canvases={[1, 2]}
t={k => (k)}
{...props}
/>,
);
}

describe('PageIconViewerNavigation', () => {
let setNextCanvas;
let setPreviousCanvas;
beforeEach(() => {
setNextCanvas = jest.fn();
setPreviousCanvas = jest.fn();
});
it('renders the component', () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: false,
setNextCanvas,
setPreviousCanvas,
});
const buttons = screen.queryAllByRole('button');
expect(buttons[0].closest('div')).toBeInTheDocument(); // eslint-disable-line testing-library/no-node-access
});
it('sets the page icon styles', () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: true,
setNextCanvas,
setPreviousCanvas,
});
expect(screen.getByRole('button', { name: 'previousCanvas' }).querySelector('svg')).toHaveStyle('transform: scale(-1, 1);'); // eslint-disable-line testing-library/no-node-access
expect(screen.getByRole('button', { name: 'nextCanvas' }).querySelector('svg')).not.toHaveStyle('transform: scale(-1, 1);'); // eslint-disable-line testing-library/no-node-access
});
describe('when next canvases are present', () => {
it('nextCanvas button is not disabled', () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: false,
setNextCanvas,
setPreviousCanvas,
});
expect(screen.getByRole('button', { name: 'nextCanvas' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'nextCanvas' })).toBeEnabled();
});
it('setNextCanvas function is called after click', async () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: false,
setNextCanvas,
setPreviousCanvas,
});
const user = userEvent.setup();
await user.click(screen.getByRole('button', { name: 'nextCanvas' }));
expect(setNextCanvas).toHaveBeenCalled();
});
});
describe('when next canvases are not present', () => {
it('nextCanvas button is disabled', async () => {
createWrapper({
hasNextCanvas: false,
hasPreviousCanvas: true,
setNextCanvas,
setPreviousCanvas,
});
expect(screen.getByRole('button', { name: 'nextCanvas' })).toBeDisabled();
});
});
describe('when previous canvases are present', () => {
it('previousCanvas button is not disabled', () => {
createWrapper({
hasNextCanvas: false,
hasPreviousCanvas: true,
setNextCanvas,
setPreviousCanvas,
});
expect(screen.getByRole('button', { name: 'previousCanvas' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'previousCanvas' })).toBeEnabled();
});
it('setPreviousCanvas function is called after click', async () => {
createWrapper({
hasNextCanvas: false,
hasPreviousCanvas: true,
setNextCanvas,
setPreviousCanvas,
});
const user = userEvent.setup();
await user.click(screen.getByRole('button', { name: 'previousCanvas' }));
expect(setPreviousCanvas).toHaveBeenCalled();
});
});
describe('when previous canvases are not present', () => {
it('disabled on previousCanvas button', () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: false,
setNextCanvas,
setPreviousCanvas,
});
expect(screen.getByRole('button', { name: 'previousCanvas' })).toBeDisabled();
});
});
describe('when neither previous nor next canvases are not present', () => {
it('disabled on previousCanvas button', () => {
const rendered = createWrapper({

Check warning on line 114 in __tests__/src/culPlugins/mirador-pageIconViewerNavigation/components/PageIconViewerNavigation.test.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'rendered' is assigned a value but never used

Check warning on line 114 in __tests__/src/culPlugins/mirador-pageIconViewerNavigation/components/PageIconViewerNavigation.test.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'rendered' is assigned a value but never used
hasNextCanvas: false,
hasPreviousCanvas: false,
setNextCanvas,
setPreviousCanvas,
});
const buttons = screen.queryAllByRole('button');
expect(buttons.length).toEqual(0);
});
});
describe('when viewingDirection is right-to-left', () => {
it('changes the page icon styles', () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: true,
setNextCanvas,
setPreviousCanvas,
viewingDirection: 'right-to-left',
});
expect(screen.getByRole('button', { name: 'previousCanvas' }).querySelector('svg')).not.toHaveStyle('transform: scale(-1, 1);'); // eslint-disable-line testing-library/no-node-access
expect(screen.getByRole('button', { name: 'nextCanvas' }).querySelector('svg')).toHaveStyle('transform: scale(-1, 1);'); // eslint-disable-line testing-library/no-node-access
});

it('sets the dir="rtl"', () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: true,
setNextCanvas,
setPreviousCanvas,
viewingDirection: 'right-to-left',
});
const buttons = screen.queryAllByRole('button');
expect(buttons[0].closest('div')).toHaveAttribute('dir', 'rtl'); // eslint-disable-line testing-library/no-node-access
});
});

describe('when viewingDirection is top-to-bottom', () => {
it('changes the page icon styles', () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: true,
setNextCanvas,
setPreviousCanvas,
viewingDirection: 'top-to-bottom',
});
expect(screen.getByRole('button', { name: 'previousCanvas' }).querySelector('svg')).toHaveStyle('transform: rotate(90deg) scale(-1, 1);'); // eslint-disable-line testing-library/no-node-access
expect(screen.getByRole('button', { name: 'nextCanvas' }).querySelector('svg')).toHaveStyle('transform: rotate(90deg);'); // eslint-disable-line testing-library/no-node-access
});
});
describe('when viewingDirection is bottom-to-top', () => {
it('changes the page icon styles', () => {
createWrapper({
hasNextCanvas: true,
hasPreviousCanvas: true,
setNextCanvas,
setPreviousCanvas,
viewingDirection: 'bottom-to-top',
});
expect(screen.getByRole('button', { name: 'previousCanvas' }).querySelector('svg')).toHaveStyle('transform: rotate(270deg) scale(-1, 1);'); // eslint-disable-line testing-library/no-node-access
expect(screen.getByRole('button', { name: 'nextCanvas' }).querySelector('svg')).toHaveStyle('transform: rotate(270deg);'); // eslint-disable-line testing-library/no-node-access
});
});
});
2 changes: 2 additions & 0 deletions src/culPlugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import citationsSidebarPlugin from './mirador-citations';
import downloadDialogPlugin from './mirador-downloaddialog';
import hintingSideBar from './mirador-hinting-sidebar';
import videojsPlugin from './mirador-videojs';
import viewerNavigation from './mirador-pageIconViewerNavigation';
import viewXmlPlugin from './mirador-viewXml';

export default {
Expand All @@ -13,5 +14,6 @@ export default {
downloadDialogPlugin,
hintingSideBar,
videojsPlugin,
viewerNavigation,
viewXmlPlugin,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Component } from 'react';
import LtrNextPage from '@mui/icons-material/AutoStories';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import MiradorMenuButton from '../../../containers/MiradorMenuButton';
import ns from '../../../config/css-ns';

/**
*/
export class PageIconViewerNavigation extends Component {
/**
* Renders previous next canvas buttons, but only if there are such canvases
* The AutoStories icon appears to be a page turning, and is thus flipped
* via a scale transform rather than a 180deg rotation. Rotations
* for the reading direction can thus be applied uniformly to both but before
* the scale transform.
*/
render() {
const {
hasNextCanvas, hasPreviousCanvas, setNextCanvas, setPreviousCanvas, t,
viewingDirection,
} = this.props;

if (!hasPreviousCanvas && !hasNextCanvas) return (null);
let htmlDir = 'ltr';
const prevTransform = ['scale(-1, 1)'];
const nextTransform = [];
switch (viewingDirection) {
case 'top-to-bottom':
prevTransform.unshift('rotate(90deg)');
nextTransform.unshift('rotate(90deg)');
break;
case 'bottom-to-top':
prevTransform.unshift('rotate(270deg)');
nextTransform.unshift('rotate(270deg)');
break;
case 'right-to-left':
htmlDir = 'rtl';
nextTransform.push(prevTransform.pop());
break;
default:
break;
}
const previousIconStyle = (prevTransform.length > 0) ? { transform: prevTransform.join(' ') } : {};
const nextIconStyle = (nextTransform.length > 0) ? { transform: nextTransform.join(' ') } : {};
return (
<div
className={classNames(ns('osd-navigation'))}
dir={htmlDir}
>
<MiradorMenuButton
aria-label={t('previousCanvas')}
className={ns('previous-canvas-button')}
disabled={!hasPreviousCanvas}
onClick={() => { hasPreviousCanvas && setPreviousCanvas(); }}
>
<LtrNextPage style={previousIconStyle} />
</MiradorMenuButton>
<MiradorMenuButton
aria-label={t('nextCanvas')}
className={ns('next-canvas-button')}
disabled={!hasNextCanvas}
onClick={() => { hasNextCanvas && setNextCanvas(); }}
>
<LtrNextPage style={nextIconStyle} />
</MiradorMenuButton>
</div>
);
}
}

PageIconViewerNavigation.propTypes = {
hasNextCanvas: PropTypes.bool,
hasPreviousCanvas: PropTypes.bool,
setNextCanvas: PropTypes.func,
setPreviousCanvas: PropTypes.func,
t: PropTypes.func.isRequired,
viewingDirection: PropTypes.string,
};

PageIconViewerNavigation.defaultProps = {
hasNextCanvas: false,
hasPreviousCanvas: false,
setNextCanvas: () => {},
setPreviousCanvas: () => {},
viewingDirection: '',
};
32 changes: 32 additions & 0 deletions src/culPlugins/mirador-pageIconViewerNavigation/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
getContainerId,

Check warning on line 2 in src/culPlugins/mirador-pageIconViewerNavigation/index.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'getContainerId' is defined but never used

Check warning on line 2 in src/culPlugins/mirador-pageIconViewerNavigation/index.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'getContainerId' is defined but never used
getSequenceViewingDirection,
getNextCanvasGrouping,
getPreviousCanvasGrouping,
} from '../../state/selectors';
import { setNextCanvas, setPreviousCanvas } from '../../state/actions';

import { PageIconViewerNavigation } from './components/PageIconViewerNavigation';

export {
PageIconViewerNavigation,
};

export default [
{
component: PageIconViewerNavigation,
config: {},
mapDispatchToProps: (dispatch, { windowId }) => ({
setNextCanvas: (...args) => dispatch(setNextCanvas(windowId)),
setPreviousCanvas: (...args) => dispatch(setPreviousCanvas(windowId)),
}),
mapStateToProps: (state, { windowId }) => ({
hasNextCanvas: !!getNextCanvasGrouping(state, { windowId }),
hasPreviousCanvas: !!getPreviousCanvasGrouping(state, { windowId }),
viewingDirection: getSequenceViewingDirection(state, { windowId }),
}),
mode: 'wrap',
name: 'PageIconViewerNavigation',
target: 'ViewerNavigation',
},
];
4 changes: 2 additions & 2 deletions src/culPlugins/mirador-viewXml/MiradorViewXmlPlugin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import CodeIcon from '@mui/icons-material/Code';
import DataObject from '@mui/icons-material/DataObject';
import MiradorMenuButton from '../../containers/MiradorMenuButton';

self.$RefreshReg$ = () => {}; /* eslint-disable-line no-restricted-globals */
Expand All @@ -23,7 +23,7 @@ const MiradorViewXml = ({ config, container, updateConfig }) => {
dialogOpen: !dialogOpen,
})}
>
<CodeIcon />
<DataObject />
</MiradorMenuButton>
);
};
Expand Down

0 comments on commit 8b23083

Please sign in to comment.