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

Merge pull request #3304 from MImranAsghar/multi-seqs-display

Multi seqs display
parents 0b658ff8 33c63acc
No related branches found
No related tags found
No related merge requests found
{
"@context": "http://iiif.io/api/presentation/2/context.json",
"@id": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/manifest.json",
"@type": "sc:Manifest",
"label": "Urn\u00e4sch, Gemeindearchiv Urn\u00e4sch, Fragment",
"metadata": [
{
"label": "Location",
"value": "Urn\u00e4sch"
}
],
"description": [
{
"@value": "These are two well preserved fragments of a Psalterium iuxta Hebraeos, which were probably written in the 10th century at the monastery of St. Gall, following the model of Cod. Sang. 19. In 1963 both fragments were detached from a messenger bag; they are held in the town archive of Urn\u00e4sch (Appenzell Ausserrhoden).",
"@language": "en"
}
],
"license": "http://creativecommons.org/licenses/by-nc/4.0/",
"attribution": "e-codices - Virtual Manuscript Library of Switzerland",
"logo": "https://www.e-codices.ch/img/logo-for-iiif-manifest.png",
"service": [
{
"@context": "https://www.w3.org/ns/webmention",
"@id": "https://www.e-codices.unifr.ch/webmention/receive",
"profile": "https://www.w3.org/ns/webmention",
"label": "e-codices Webmention Service"
}
],
"related": "https://www.e-codices.ch/en/list/one/gau/Fragment",
"within": "https://www.e-codices.ch/en/list/gau",
"seeAlso": [
{
"@id": "https://www.e-codices.unifr.ch/xml/tei_published/gau-Fragment_Solovey.xml",
"@format": "application/tei+xml"
}
],
"sequences": [
{
"@id": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/sequence/Sequence-1740.json",
"@type": "sc:Sequence",
"label": [
{
"@value": "Standard",
"@language": "de"
}
],
"canvases": [
{
"@id": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/canvas/gau-Fragment_frag001a.json",
"@type": "sc:Canvas",
"label": "fragm1a",
"height": 6132,
"width": 8176,
"images": [
{
"@id": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/annotation/gau-Fragment_frag001a.json",
"@type": "oa:Annotation",
"motivation": "sc:painting",
"on": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/canvas/gau-Fragment_frag001a.json",
"resource": {
"@id": "https://www.e-codices.unifr.ch/loris/gau/gau-Fragment/gau-Fragment_frag001a.jp2/full/full/0/default/jpg",
"@type": "dctypes:Image",
"format": "image/jpeg",
"height": 6132,
"width": 8176,
"service": {
"@context": "http://iiif.io/api/image/2/context.json",
"@id": "https://www.e-codices.unifr.ch/loris/gau/gau-Fragment/gau-Fragment_frag001a.jp2",
"profile": "http://iiif.io/api/image/2/level2.json"
}
}
}
]
}
]
},
{
"@id": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/sequence/Sequence-1741.json",
"@type": "sc:Sequence",
"label": [
{
"@value": "Lesefreundliche Sequenz",
"@language": "de"
}
],
"canvases": [
{
"@id": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/canvas/gau-Fragment_frag001a_1r.json",
"@type": "sc:Canvas",
"label": "fragm1a_1r",
"height": 8176,
"width": 6132,
"images": [
{
"@id": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/annotation/gau-Fragment_frag001a_1r.json",
"@type": "oa:Annotation",
"motivation": "sc:painting",
"on": "https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/canvas/gau-Fragment_frag001a_1r.json",
"resource": {
"@id": "https://www.e-codices.unifr.ch/loris/gau/gau-Fragment/gau-Fragment_frag001a_1r.jp2/full/full/0/default/jpg",
"@type": "dctypes:Image",
"format": "image/jpeg",
"height": 8176,
"width": 6132,
"service": {
"@context": "http://iiif.io/api/image/2/context.json",
"@id": "https://www.e-codices.unifr.ch/loris/gau/gau-Fragment/gau-Fragment_frag001a_1r.jp2",
"profile": "http://iiif.io/api/image/2/level2.json"
}
}
}
]
}
]
}
],
"structures": []
}
\ No newline at end of file
/* global miradorInstance */
describe('Window Sidebar Sequence Dropdown', () => {
beforeAll(async () => {
await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/blank.html');
await expect(page).toClick('#addBtn');
await expect(page).toClick('.mirador-add-resource-button');
await expect(page).toFill('#manifestURL', 'http://localhost:4488/__tests__/fixtures/version-2/multipleSequences.json');
await expect(page).toClick('#fetchBtn');
await expect(page).toMatchElement('[data-manifestid="http://localhost:4488/__tests__/fixtures/version-2/multipleSequences.json"] button');
await expect(page).toClick('[data-manifestid="http://localhost:4488/__tests__/fixtures/version-2/multipleSequences.json"] button');
});
it('allows the user to switch the sequence', async () => {
const windows = await page.evaluate(() => (
miradorInstance.store.getState().windows
));
const windowId = Object.values(windows)
.find(window => window.manifestId === 'http://localhost:4488/__tests__/fixtures/version-2/multipleSequences.json')
.id;
await expect(page).toMatchElement(`#${windowId} button[aria-label="Toggle sidebar"]`);
await expect(page).toClick(`#${windowId} button[aria-label="Toggle sidebar"]`);
await expect(page).toMatchElement(`#${windowId} button[aria-label="Index"]`);
await expect(page).toClick(`#${windowId} button[aria-label="Index"]`);
await expect(page).toClick('#mui-component-select-sequenceId');
await expect(page).toMatchElement('#sequence-1');
await expect(page).toClick('#sequence-1');
await expect(page).toMatchElement('p', { text: 'fragm1a_1r' });
});
});
...@@ -12,6 +12,14 @@ import manifestJson from '../../fixtures/version-2/019.json'; ...@@ -12,6 +12,14 @@ import manifestJson from '../../fixtures/version-2/019.json';
*/ */
function createWrapper(props) { function createWrapper(props) {
const canvases = Utils.parseManifest(manifestJson).getSequences()[0].getCanvases(); const canvases = Utils.parseManifest(manifestJson).getSequences()[0].getCanvases();
let sequences;
if (props.multipleSequences) {
sequences = [{ id: 'a', label: 'seq1' },
{ id: 'b', label: 'seq2' }];
} else {
sequences = Utils.parseManifest(manifestJson).getSequences();
}
return shallow( return shallow(
<WindowSideBarCanvasPanel <WindowSideBarCanvasPanel
...@@ -24,6 +32,7 @@ function createWrapper(props) { ...@@ -24,6 +32,7 @@ function createWrapper(props) {
config={{ canvasNavigation: { height: 100 } }} config={{ canvasNavigation: { height: 100 } }}
updateVariant={() => {}} updateVariant={() => {}}
selectedCanvases={[canvases[1]]} selectedCanvases={[canvases[1]]}
sequences={sequences}
variant="item" variant="item"
{...props} {...props}
/>, />,
...@@ -32,23 +41,37 @@ function createWrapper(props) { ...@@ -32,23 +41,37 @@ function createWrapper(props) {
describe('WindowSideBarCanvasPanel', () => { describe('WindowSideBarCanvasPanel', () => {
it('renders SidebarIndexList', () => { it('renders SidebarIndexList', () => {
const wrapper = createWrapper(); const wrapper = createWrapper({ multipleSequences: false });
expect(wrapper.find(CompanionWindow).props().title).toBe('canvasIndex'); expect(wrapper.find(CompanionWindow).props().title).toBe('canvasIndex');
expect(wrapper.find(SidebarIndexList).length).toBe(1); expect(wrapper.find(SidebarIndexList).length).toBe(1);
}); });
it('without a treeStructure will not render the table of contents tab', () => { it('without a treeStructure will not render the table of contents tab', () => {
const wrapper = createWrapper(); const wrapper = createWrapper({ multipleSequences: false });
expect( expect(
compact(wrapper.find(CompanionWindow).props().titleControls.props.children) compact(wrapper.find(CompanionWindow).props().titleControls.props.children)
.length, .length,
).toBe(2); ).toBe(2);
}); });
it('renders form control when multiple sequences present', () => {
const wrapper = createWrapper({ multipleSequences: true });
expect(wrapper.find(CompanionWindow).props().titleControls.props.children[0]
.type.displayName.includes('FormControl')).toBe(true);
});
it('renders correct number of sequences in form control', () => {
const wrapper = createWrapper({ multipleSequences: true });
expect(wrapper.find(CompanionWindow).props().titleControls.props.children[0]
.props.children.props.children.length).toBe(2);
});
describe('handleVariantChange', () => { describe('handleVariantChange', () => {
it('updates the variant', () => { it('updates the variant', () => {
const updateVariant = jest.fn(); const updateVariant = jest.fn();
const wrapper = createWrapper({ updateVariant }); const wrapper = createWrapper({ multipleSequences: false, updateVariant });
wrapper.instance().handleVariantChange({}, 'item'); wrapper.instance().handleVariantChange({}, 'item');
expect(updateVariant).toHaveBeenCalledWith('item'); expect(updateVariant).toHaveBeenCalledWith('item');
}); });
......
...@@ -10,6 +10,9 @@ import TocIcon from '@material-ui/icons/SortSharp'; ...@@ -10,6 +10,9 @@ import TocIcon from '@material-ui/icons/SortSharp';
import ThumbnailListIcon from '@material-ui/icons/ViewListSharp'; import ThumbnailListIcon from '@material-ui/icons/ViewListSharp';
import Typography from '@material-ui/core/Typography'; import Typography from '@material-ui/core/Typography';
import ArrowForwardIcon from '@material-ui/icons/ArrowForwardSharp'; import ArrowForwardIcon from '@material-ui/icons/ArrowForwardSharp';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import MenuItem from '@material-ui/core/MenuItem';
import CompanionWindow from '../containers/CompanionWindow'; import CompanionWindow from '../containers/CompanionWindow';
import SidebarIndexList from '../containers/SidebarIndexList'; import SidebarIndexList from '../containers/SidebarIndexList';
import SidebarIndexTableOfContents from '../containers/SidebarIndexTableOfContents'; import SidebarIndexTableOfContents from '../containers/SidebarIndexTableOfContents';
...@@ -21,8 +24,13 @@ export class WindowSideBarCanvasPanel extends Component { ...@@ -21,8 +24,13 @@ export class WindowSideBarCanvasPanel extends Component {
/** */ /** */
constructor(props) { constructor(props) {
super(props); super(props);
this.handleSequenceChange = this.handleSequenceChange.bind(this);
this.handleVariantChange = this.handleVariantChange.bind(this); this.handleVariantChange = this.handleVariantChange.bind(this);
this.state = {
sequenceSelectionOpened: false,
};
this.containerRef = React.createRef(); this.containerRef = React.createRef();
} }
...@@ -35,6 +43,18 @@ export class WindowSideBarCanvasPanel extends Component { ...@@ -35,6 +43,18 @@ export class WindowSideBarCanvasPanel extends Component {
: resource.id; : resource.id;
} }
/** @private */
async handleSequenceChange(event) {
const { setCanvas, updateSequence } = this.props;
await updateSequence(event.target.value);
const { windowId, canvases } = this.props;
const firstCanvasId = canvases[0].id;
setCanvas(windowId, firstCanvasId);
this.setState({ sequenceSelectionOpened: false });
}
/** @private */ /** @private */
handleVariantChange(event, value) { handleVariantChange(event, value) {
const { updateVariant } = this.props; const { updateVariant } = this.props;
...@@ -47,16 +67,21 @@ export class WindowSideBarCanvasPanel extends Component { ...@@ -47,16 +67,21 @@ export class WindowSideBarCanvasPanel extends Component {
*/ */
render() { render() {
const { const {
canvases,
classes, classes,
collection, collection,
id, id,
showMultipart, showMultipart,
sequenceId,
sequences,
t, t,
toggleDraggingEnabled,
variant, variant,
showToc, showToc,
windowId, windowId,
} = this.props; } = this.props;
const { sequenceSelectionOpened } = this.state;
let listComponent; let listComponent;
if (variant === 'tableOfContents') { if (variant === 'tableOfContents') {
...@@ -84,6 +109,40 @@ export class WindowSideBarCanvasPanel extends Component { ...@@ -84,6 +109,40 @@ export class WindowSideBarCanvasPanel extends Component {
id={id} id={id}
windowId={windowId} windowId={windowId}
titleControls={( titleControls={(
<>
{
sequences && sequences.length > 1 && (
<FormControl>
<Select
MenuProps={{
anchorOrigin: {
horizontal: 'left',
vertical: 'bottom',
},
getContentAnchorEl: null,
}}
displayEmpty
value={sequenceId}
onChange={this.handleSequenceChange}
name="sequenceId"
open={sequenceSelectionOpened}
onOpen={(e) => {
toggleDraggingEnabled();
this.setState({ sequenceSelectionOpened: true });
}}
onClose={(e) => {
toggleDraggingEnabled();
this.setState({ sequenceSelectionOpened: false });
}}
classes={{ select: classes.select }}
className={classes.selectEmpty}
>
{ sequences.map((s, i) => <MenuItem id={`sequence-${i}`} value={s.id} key={s.id}><Typography variant="body2">{ WindowSideBarCanvasPanel.getUseableLabel(s, i) }</Typography></MenuItem>) }
</Select>
</FormControl>
)
}
<div className={classes.break} />
<Tabs <Tabs
value={variant} value={variant}
onChange={this.handleVariantChange} onChange={this.handleVariantChange}
...@@ -97,6 +156,7 @@ export class WindowSideBarCanvasPanel extends Component { ...@@ -97,6 +156,7 @@ export class WindowSideBarCanvasPanel extends Component {
<Tooltip title={t('itemList')} value="item"><Tab className={classes.variantTab} value="item" aria-label={t('itemList')} aria-controls={`tab-panel-${id}`} icon={<ItemListIcon />} /></Tooltip> <Tooltip title={t('itemList')} value="item"><Tab className={classes.variantTab} value="item" aria-label={t('itemList')} aria-controls={`tab-panel-${id}`} icon={<ItemListIcon />} /></Tooltip>
<Tooltip title={t('thumbnailList')} value="thumbnail"><Tab className={classes.variantTab} value="thumbnail" aria-label={t('thumbnailList')} aria-controls={`tab-panel-${id}`} icon={<ThumbnailListIcon />} /></Tooltip> <Tooltip title={t('thumbnailList')} value="thumbnail"><Tab className={classes.variantTab} value="thumbnail" aria-label={t('thumbnailList')} aria-controls={`tab-panel-${id}`} icon={<ThumbnailListIcon />} /></Tooltip>
</Tabs> </Tabs>
</>
)} )}
> >
<div id={`tab-panel-${id}`}> <div id={`tab-panel-${id}`}>
...@@ -120,18 +180,27 @@ export class WindowSideBarCanvasPanel extends Component { ...@@ -120,18 +180,27 @@ export class WindowSideBarCanvasPanel extends Component {
} }
WindowSideBarCanvasPanel.propTypes = { WindowSideBarCanvasPanel.propTypes = {
canvases: PropTypes.arrayOf(PropTypes.object),
classes: PropTypes.objectOf(PropTypes.string).isRequired, classes: PropTypes.objectOf(PropTypes.string).isRequired,
collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
sequenceId: PropTypes.string,
sequences: PropTypes.arrayOf(PropTypes.object),
setCanvas: PropTypes.func.isRequired,
showMultipart: PropTypes.func.isRequired, showMultipart: PropTypes.func.isRequired,
showToc: PropTypes.bool, showToc: PropTypes.bool,
t: PropTypes.func.isRequired, t: PropTypes.func.isRequired,
toggleDraggingEnabled: PropTypes.func.isRequired,
updateSequence: PropTypes.func.isRequired,
updateVariant: PropTypes.func.isRequired, updateVariant: PropTypes.func.isRequired,
variant: PropTypes.oneOf(['item', 'thumbnail', 'tableOfContents']).isRequired, variant: PropTypes.oneOf(['item', 'thumbnail', 'tableOfContents']).isRequired,
windowId: PropTypes.string.isRequired, windowId: PropTypes.string.isRequired,
}; };
WindowSideBarCanvasPanel.defaultProps = { WindowSideBarCanvasPanel.defaultProps = {
canvases: [],
collection: null, collection: null,
sequenceId: null,
sequences: [],
showToc: false, showToc: false,
}; };
...@@ -6,11 +6,14 @@ import { withPlugins } from '../extend/withPlugins'; ...@@ -6,11 +6,14 @@ import { withPlugins } from '../extend/withPlugins';
import * as actions from '../state/actions'; import * as actions from '../state/actions';
import { WindowSideBarCanvasPanel } from '../components/WindowSideBarCanvasPanel'; import { WindowSideBarCanvasPanel } from '../components/WindowSideBarCanvasPanel';
import { import {
getCanvases,
getCompanionWindow, getCompanionWindow,
getDefaultSidebarVariant, getDefaultSidebarVariant,
getSequenceTreeStructure, getSequenceTreeStructure,
getWindow, getWindow,
getManifestoInstance, getManifestoInstance,
getSequence,
getSequences,
} from '../state/selectors'; } from '../state/selectors';
/** /**
...@@ -24,11 +27,13 @@ const mapStateToProps = (state, { id, windowId }) => { ...@@ -24,11 +27,13 @@ const mapStateToProps = (state, { id, windowId }) => {
const collectionPath = window.collectionPath || []; const collectionPath = window.collectionPath || [];
const collectionId = collectionPath && collectionPath[collectionPath.length - 1]; const collectionId = collectionPath && collectionPath[collectionPath.length - 1];
return { return {
canvases: getCanvases(state, { windowId }),
collection: collectionId && getManifestoInstance(state, { manifestId: collectionId }), collection: collectionId && getManifestoInstance(state, { manifestId: collectionId }),
config, config,
sequenceId: getSequence(state, { windowId }).id,
sequences: getSequences(state, { windowId }),
showToc: treeStructure && treeStructure.nodes && treeStructure.nodes.length > 0, showToc: treeStructure && treeStructure.nodes && treeStructure.nodes.length > 0,
variant: companionWindow.variant variant: companionWindow.variant || getDefaultSidebarVariant(state, { windowId }),
|| getDefaultSidebarVariant(state, { windowId }),
}; };
}; };
...@@ -43,6 +48,9 @@ const mapDispatchToProps = (dispatch, { id, windowId }) => ({ ...@@ -43,6 +48,9 @@ const mapDispatchToProps = (dispatch, { id, windowId }) => ({
actions.addOrUpdateCompanionWindow(windowId, { content: 'collection', position: 'right' }), actions.addOrUpdateCompanionWindow(windowId, { content: 'collection', position: 'right' }),
), ),
toggleDraggingEnabled: () => dispatch(actions.toggleDraggingEnabled()), toggleDraggingEnabled: () => dispatch(actions.toggleDraggingEnabled()),
updateSequence: sequenceId => dispatch(
actions.updateWindow(windowId, { sequenceId }),
),
updateVariant: variant => dispatch( updateVariant: variant => dispatch(
actions.updateCompanionWindow(windowId, id, { variant }), actions.updateCompanionWindow(windowId, id, { variant }),
), ),
...@@ -53,6 +61,10 @@ const mapDispatchToProps = (dispatch, { id, windowId }) => ({ ...@@ -53,6 +61,10 @@ const mapDispatchToProps = (dispatch, { id, windowId }) => ({
* @param theme * @param theme
*/ */
const styles = theme => ({ const styles = theme => ({
break: {
flexBasis: '100%',
height: 0,
},
collectionNavigationButton: { collectionNavigationButton: {
textTransform: 'none', textTransform: 'none',
}, },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment