diff --git a/__tests__/src/components/WindowSideBarCanvasPanel.test.js b/__tests__/src/components/WindowSideBarCanvasPanel.test.js index e01de7a023d63ddbb5747e67434e9a7e6450650e..d38027ec1e54b53c2acbb8c7a96f094368f8adaa 100644 --- a/__tests__/src/components/WindowSideBarCanvasPanel.test.js +++ b/__tests__/src/components/WindowSideBarCanvasPanel.test.js @@ -7,7 +7,6 @@ import manifesto from 'manifesto.js'; import { WindowSideBarCanvasPanel } from '../../../src/components/WindowSideBarCanvasPanel'; import { CanvasThumbnail } from '../../../src/components/CanvasThumbnail'; import manifestJson from '../../fixtures/version-2/019.json'; -import { getIdAndLabelOfCanvases } from '../../../src/state/selectors'; /** * Helper function to create a shallow wrapper around WindowSideBarCanvasPanel @@ -68,21 +67,20 @@ describe('WindowSideBarCanvasPanel', () => { it('should set the correct labels', () => { const wrapper = createWrapper(); - const canvases = manifesto.create(manifestJson).getSequences()[0].getCanvases(); - const idsAndLabels = getIdAndLabelOfCanvases(canvases); + expect(wrapper .find(List) .find(Typography) .at(0) .render() - .text()).toBe(idsAndLabels[0].label); + .text()).toBe('Test 19 Canvas: 1'); expect(wrapper .find(List) .find(Typography) .at(1) .render() - .text()).toBe(idsAndLabels[1].label); + .text()).toBe('Image 1'); }); it('should call the onClick handler of a list item', () => { @@ -90,4 +88,36 @@ describe('WindowSideBarCanvasPanel', () => { wrapper.find(ListItem).at(1).simulate('click'); expect(setCanvas).toHaveBeenCalledTimes(1); }); + + describe('getIdAndLabelOfCanvases', () => { + it('should return id and label of each canvas in manifest', () => { + const canvases = manifesto + .create(manifestJson) + .getSequences()[0] + .getCanvases(); + const wrapper = createWrapper({ canvases }); + const received = wrapper.instance().getIdAndLabelOfCanvases(canvases); + const expected = [ + { + id: 'http://iiif.io/api/presentation/2.0/example/fixtures/canvas/24/c1.json', + label: 'Test 19 Canvas: 1', + }, + { + id: 'https://purl.stanford.edu/fr426cg9537/iiif/canvas/fr426cg9537_1', + label: 'Image 1', + }, + { + id: 'https://purl.stanford.edu/rz176rt6531/iiif/canvas/rz176rt6531_1', + label: 'Image 2', + }, + ]; + expect(received).toEqual(expected); + }); + + it('should return empty array if canvas if empty', () => { + const wrapper = createWrapper({ canvases: [] }); + const received = wrapper.instance().getIdAndLabelOfCanvases([]); + expect(received).toEqual([]); + }); + }); }); diff --git a/__tests__/src/selectors/index.test.js b/__tests__/src/selectors/index.test.js index cd61d8a72deb2ac4dc4e45dfa747d996e0bfa2d1..c2391dfede742134be0bb4c5b15ff55c9540a2b9 100644 --- a/__tests__/src/selectors/index.test.js +++ b/__tests__/src/selectors/index.test.js @@ -1,25 +1,17 @@ -import manifesto from 'manifesto.js'; import manifestFixture001 from '../../fixtures/version-2/001.json'; -import manifestFixture002 from '../../fixtures/version-2/002.json'; -import manifestFixture019 from '../../fixtures/version-2/019.json'; -import manifestFixtureWithAProvider from '../../fixtures/version-3/with_a_provider.json'; import { getCanvasLabel, getCompanionWindowForPosition, getAnnotationResourcesByMotivation, getIdAndContentOfResources, getLanguagesFromConfigWithCurrent, - getSelectedCanvas, - getSelectedCanvases, getThumbnailNavigationPosition, getSelectedAnnotationIds, getSelectedTargetAnnotations, getSelectedTargetsAnnotations, getSelectedTargetAnnotationResources, getWindowViewType, - getIdAndLabelOfCanvases, getCompanionWindowsOfWindow, - getWindowTitles, } from '../../../src/state/selectors'; import Annotation from '../../../src/lib/Annotation'; import AnnotationResource from '../../../src/lib/AnnotationResource'; @@ -77,23 +69,36 @@ describe('getWindowViewType', () => { describe('getCanvasLabel', () => { it('should return label of the canvas', () => { - const canvas = manifesto - .create(manifestFixture001) - .getSequences()[0] - .getCanvases()[0]; - - const received = getCanvasLabel(canvas); + const state = { manifests: { a: { json: manifestFixture001 } } }; + const received = getCanvasLabel(state, { manifestId: 'a', canvasIndex: 0 }); expect(received).toBe('Whole Page'); }); it('should return undefined if the canvas is undefined', () => { - expect(getCanvasLabel(undefined)).toBeUndefined(); + const state = { manifests: { } }; + expect(getCanvasLabel(state, { manifestId: 'b', canvasIndex: 0 })).toBeUndefined(); }); it('should return the canvas index as (+1) as string if no label given', () => { - const canvas = { getLabel: () => [] }; - const received = getCanvasLabel(canvas, 42); - expect(received).toBe('43'); + const manifest = { + '@context': 'http://iiif.io/api/presentation/2/context.json', + '@id': + 'http://iiif.io/api/presentation/2.1/example/fixtures/19/manifest.json', + '@type': 'sc:Manifest', + sequences: [ + { + canvases: [ + { + '@id': 'some-canvas-without-a-label', + }, + ], + }, + ], + }; + + const state = { manifests: { a: { json: manifest } } }; + const received = getCanvasLabel(state, { manifestId: 'a', canvasIndex: 0 }); + expect(received).toBe('1'); }); }); @@ -202,36 +207,6 @@ describe('getIdAndContentOfResources', () => { }); }); -describe('getIdAndLabelOfCanvases', () => { - it('should return id and label of each canvas in manifest', () => { - const canvases = manifesto - .create(manifestFixture019) - .getSequences()[0] - .getCanvases(); - const received = getIdAndLabelOfCanvases(canvases); - const expected = [ - { - id: 'http://iiif.io/api/presentation/2.0/example/fixtures/canvas/24/c1.json', - label: 'Test 19 Canvas: 1', - }, - { - id: 'https://purl.stanford.edu/fr426cg9537/iiif/canvas/fr426cg9537_1', - label: 'Image 1', - }, - { - id: 'https://purl.stanford.edu/rz176rt6531/iiif/canvas/rz176rt6531_1', - label: 'Image 2', - }, - ]; - expect(received).toEqual(expected); - }); - - it('should return empty array if canvas if empty', () => { - const received = getIdAndLabelOfCanvases([]); - expect(received).toEqual([]); - }); -}); - describe('getCompanionWindowForPosition', () => { const state = { windows: { a: { companionWindowIds: ['abc'] } }, diff --git a/src/components/GalleryView.js b/src/components/GalleryView.js index 9c9e66b4a09bf1fdefa69af35dbbbd90ffdb9313..5b348e60b074ae4a087279a39e34bf9becdeb404 100644 --- a/src/components/GalleryView.js +++ b/src/components/GalleryView.js @@ -2,7 +2,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Typography from '@material-ui/core/Typography'; import ManifestoCanvas from '../lib/ManifestoCanvas'; -import { getCanvasLabel } from '../state/selectors'; import { CanvasThumbnail } from './CanvasThumbnail'; import ns from '../config/css-ns'; @@ -52,7 +51,7 @@ export class GalleryView extends Component { aspectRatio={manifestoCanvas.aspectRatio} /> <Typography variant="caption"> - {getCanvasLabel(canvas, canvas.index)} + {manifestoCanvas.getLabel()} </Typography> </div> ); diff --git a/src/components/ThumbnailNavigation.js b/src/components/ThumbnailNavigation.js index 2b185d1c1225ab9c2951a7ca47f1c21c41834715..2626aaadbb367d23dfe07edb4e5afab6c22dae20 100644 --- a/src/components/ThumbnailNavigation.js +++ b/src/components/ThumbnailNavigation.js @@ -5,7 +5,6 @@ import Grid from 'react-virtualized/dist/commonjs/Grid'; import GridListTile from '@material-ui/core/GridListTile'; import GridListTileBar from '@material-ui/core/GridListTileBar'; import Typography from '@material-ui/core/Typography'; -import { getCanvasLabel } from '../state/selectors'; import { CanvasThumbnail } from './CanvasThumbnail'; import ManifestoCanvas from '../lib/ManifestoCanvas'; import ns from '../config/css-ns'; @@ -97,7 +96,7 @@ export class ThumbnailNavigation extends Component { classes={{ root: classes.root }} title={( <Typography classes={{ root: classes.title }} variant="caption"> - {getCanvasLabel(canvas, canvas.index)} + {manifestoCanvas.getLabel()} </Typography> )} /> diff --git a/src/components/WindowSideBarCanvasPanel.js b/src/components/WindowSideBarCanvasPanel.js index 80eae9a8e822f1344acb91fe46a7d5685923bbe1..f8f641fa7e2713a75ee135d4e3308d8f140cfb57 100644 --- a/src/components/WindowSideBarCanvasPanel.js +++ b/src/components/WindowSideBarCanvasPanel.js @@ -10,7 +10,6 @@ import Select from '@material-ui/core/Select'; import { CanvasThumbnail } from './CanvasThumbnail'; import ManifestoCanvas from '../lib/ManifestoCanvas'; import CompanionWindow from '../containers/CompanionWindow'; -import { getIdAndLabelOfCanvases } from '../state/selectors'; /** * a panel showing the canvases for a given manifest @@ -24,6 +23,16 @@ export class WindowSideBarCanvasPanel extends Component { this.handleVariantChange = this.handleVariantChange.bind(this); } + /** @private */ + getIdAndLabelOfCanvases() { + const { canvases } = this.props; + + return canvases.map((canvas, index) => ({ + id: canvas.id, + label: new ManifestoCanvas(canvas).getLabel(), + })); + } + /** */ handleVariantChange(event) { this.setState({ variant: event.target.value }); @@ -88,7 +97,7 @@ export class WindowSideBarCanvasPanel extends Component { const { variant } = this.state; - const canvasesIdAndLabel = getIdAndLabelOfCanvases(canvases); + const canvasesIdAndLabel = this.getIdAndLabelOfCanvases(canvases); return ( <CompanionWindow title={t('canvasIndex')} diff --git a/src/containers/OpenSeadragonViewer.js b/src/containers/OpenSeadragonViewer.js index cbc4861798567df5b782a9ba6e18c51364c1dc9e..d0aecea6e5f6abe235e13986d836459018a5d520 100644 --- a/src/containers/OpenSeadragonViewer.js +++ b/src/containers/OpenSeadragonViewer.js @@ -9,7 +9,6 @@ import * as actions from '../state/actions'; import { getCanvasLabel, getSelectedAnnotationIds, - getSelectedCanvas, getSelectedTargetAnnotationResources, } from '../state/selectors'; @@ -22,10 +21,7 @@ const mapStateToProps = ({ viewers, windows, manifests, annotations, }, { windowId, currentCanvases }) => ({ viewer: viewers[windowId], - label: getCanvasLabel( - getSelectedCanvas({ windows, manifests }, { windowId }), - windows[windowId].canvasIndex, - ), + label: getCanvasLabel({ windows, manifests }, { windowId, canvasIndex: 'selected' }), annotations: getSelectedTargetAnnotationResources( { annotations }, currentCanvases.map(c => c.id), diff --git a/src/containers/ViewerInfo.js b/src/containers/ViewerInfo.js index 9581824ccab9a2b41fb6bd6b5c79ace79cae1d14..ad0fcfd101cae2559d82297fba95bb783c78825b 100644 --- a/src/containers/ViewerInfo.js +++ b/src/containers/ViewerInfo.js @@ -18,7 +18,7 @@ const mapStateToProps = (state, props) => { return { canvasCount: canvases.length, canvasIndex, - canvasLabel: getCanvasLabel(canvases[canvasIndex], canvasIndex), + canvasLabel: getCanvasLabel(state, { windowId, canvasIndex }), }; }; diff --git a/src/containers/WindowCanvasNavigationControls.js b/src/containers/WindowCanvasNavigationControls.js index b76793a10ee93704145d1466876ce19d1d31bca8..a8b8384879ab07c538f816113fa62e11a48b292b 100644 --- a/src/containers/WindowCanvasNavigationControls.js +++ b/src/containers/WindowCanvasNavigationControls.js @@ -3,17 +3,13 @@ import { compose } from 'redux'; import { withPlugins } from '../extend'; import { getCanvasLabel, - getSelectedCanvas, } from '../state/selectors'; import { WindowCanvasNavigationControls } from '../components/WindowCanvasNavigationControls'; /** */ const mapStateToProps = (state, { windowId }) => ({ window: state.windows[windowId], - canvasLabel: getCanvasLabel( - getSelectedCanvas(state, { windowId }), - state.windows[windowId].canvasIndex, - ), + canvasLabel: getCanvasLabel(state, { windowId, canvasIndex: 'selected' }), visible: state.workspace.focusedWindowId === windowId, }); diff --git a/src/containers/WindowSideBarInfoPanel.js b/src/containers/WindowSideBarInfoPanel.js index dd10cbe92196623d7b6b013a4f96c2178e484509..e55cc7a520ad0537a6f9c6330c49e50adc32ffc5 100644 --- a/src/containers/WindowSideBarInfoPanel.js +++ b/src/containers/WindowSideBarInfoPanel.js @@ -20,11 +20,8 @@ import { WindowSideBarInfoPanel } from '../components/WindowSideBarInfoPanel'; * @private */ const mapStateToProps = (state, { windowId }) => ({ - canvasLabel: getCanvasLabel( - getSelectedCanvas(state, { windowId }), - state.windows[windowId].canvasIndex, - ), - canvasDescription: getCanvasDescription(getSelectedCanvas(state, { windowId })), + canvasLabel: getCanvasLabel(state, { windowId, canvasIndex: 'selected' }), + canvasDescription: getCanvasDescription(state, { windowId, canvasIndex: 'selected' }), canvasMetadata: getDestructuredMetadata(getSelectedCanvas(state, { windowId })), manifestLabel: getManifestTitle(state, { windowId }), manifestDescription: getManifestDescription(state, { windowId }), diff --git a/src/lib/ManifestoCanvas.js b/src/lib/ManifestoCanvas.js index 684ae96ee85cbe8157c1d9e67f5efe1957ccb5eb..221691aa9e04e20e87d003aacb7938435d7bcfb2 100644 --- a/src/lib/ManifestoCanvas.js +++ b/src/lib/ManifestoCanvas.js @@ -79,6 +79,15 @@ export default class ManifestoCanvas { ); } + /** + * Get the canvas label + */ + getLabel() { + return this.canvas.getLabel().length > 0 + ? this.canvas.getLabel().map(label => label.value)[0] + : String(this.canvas.index + 1); + } + /** * Creates a canonical image request for a thumb * @param {Number} height diff --git a/src/state/selectors/canvases.js b/src/state/selectors/canvases.js index 26d74e5dac2ca23be28bc48141b0a56c37e0f73e..8051a0d019dd4bcf1270989d0e457888b978834d 100644 --- a/src/state/selectors/canvases.js +++ b/src/state/selectors/canvases.js @@ -2,18 +2,27 @@ import { createSelector } from 'reselect'; import CanvasGroupings from '../../lib/CanvasGroupings'; import { getManifestoInstance } from './manifests'; +export const getCanvases = createSelector( + [getManifestoInstance], + manifest => manifest && manifest.getSequences()[0].getCanvases(), +); + /** -* Return the current canvas selected in a window +* Return the canvas selected by an index * @param {object} state * @param {object} props * @param {string} props.manifestId * @param {string} props.windowId * @return {Object} */ -export const getSelectedCanvas = createSelector( +export const getCanvas = createSelector( [ getManifestoInstance, - (state, { windowId }) => state.windows[windowId].canvasIndex, + (state, { windowId, canvasIndex }) => ( + canvasIndex === 'selected' + ? state.windows[windowId].canvasIndex + : canvasIndex + ), ], (manifest, canvasIndex) => manifest && manifest @@ -21,6 +30,18 @@ export const getSelectedCanvas = createSelector( .getCanvasByIndex(canvasIndex), ); +/** +* Return the current canvas selected in a window +* @param {object} state +* @param {object} props +* @param {string} props.manifestId +* @param {string} props.windowId +* @return {Object} +*/ +export function getSelectedCanvas(state, props) { + return getCanvas(state, { ...props, canvasIndex: 'selected' }); +} + /** * Return the current canvases selected in a window * For book view returns 2, for single returns 1 @@ -32,12 +53,12 @@ export const getSelectedCanvas = createSelector( */ export const getSelectedCanvases = createSelector( [ - getManifestoInstance, + getCanvases, (state, { windowId }) => state.windows[windowId], ], - (manifest, { canvasIndex, view }) => manifest + (canvases, { canvasIndex, view }) => canvases && new CanvasGroupings( - manifest.getSequences()[0].getCanvases(), + canvases, view, ).getCanvases(canvasIndex), ); diff --git a/src/state/selectors/index.js b/src/state/selectors/index.js index 44e3f7fc1743b7248d9df40cdb734ec8f1bef497..fb5f2f433329dc23d8e9840b12e19654a5acb8c9 100644 --- a/src/state/selectors/index.js +++ b/src/state/selectors/index.js @@ -2,23 +2,12 @@ import { createSelector } from 'reselect'; import filter from 'lodash/filter'; import flatten from 'lodash/flatten'; import Annotation from '../../lib/Annotation'; +import { getCanvas } from './canvases'; export * from './canvases'; export * from './manifests'; export * from './windows'; -/** -* Return ids and labels of canvases -* @ param {Array} canvases -* @return {Array} - [ {id: 'id', label: 'label' }, ... ] -*/ -export function getIdAndLabelOfCanvases(canvases) { - return canvases.map((canvas, index) => ({ - id: canvas.id, - label: getCanvasLabel(canvas, index), - })); -} - /** * Return annotations for an array of targets * @param {object} state @@ -114,25 +103,24 @@ export const getWindowViewType = createSelector( * @param {object} canvas * @return {String|Integer} */ -export function getCanvasLabel(canvas, canvasIndex) { - if (!canvas) { - return undefined; - } - if (canvas.getLabel().length > 0) { - return canvas.getLabel().map(label => label.value)[0]; - } - return String(canvasIndex + 1); -} +export const getCanvasLabel = createSelector( + [getCanvas], + canvas => (canvas && ( + canvas.getLabel().length > 0 + ? canvas.getLabel().map(label => label.value)[0] + : String(canvas.index + 1) + )), +); /** * Return canvas description * @param {object} canvas * @param {String} */ -export function getCanvasDescription(canvas) { - return canvas - && canvas.getProperty('description'); -} +export const getCanvasDescription = createSelector( + [getCanvas], + canvas => canvas && canvas.getProperty('description'), +); /** * Return compantion window ids from a window diff --git a/src/state/selectors/manifests.js b/src/state/selectors/manifests.js index 2d57c9607f1d696b19c279bf7c3b31b877cec9e9..3a25e301d38531bd4d49b4dca0e5d7243841a28c 100644 --- a/src/state/selectors/manifests.js +++ b/src/state/selectors/manifests.js @@ -6,7 +6,7 @@ import ManifestoCanvas from '../../lib/ManifestoCanvas'; export function getManifest(state, { manifestId, windowId }) { return state.manifests[ manifestId - || (windowId && state.windows[windowId] && state.windows[windowId].manifestId) + || (windowId && state.windows && state.windows[windowId] && state.windows[windowId].manifestId) ]; }