diff --git a/__tests__/integration/mirador/window_sidebar.test.js b/__tests__/integration/mirador/window_sidebar.test.js new file mode 100644 index 0000000000000000000000000000000000000000..9a8ad697bfb99804f483448ad09d1c6b54a88172 --- /dev/null +++ b/__tests__/integration/mirador/window_sidebar.test.js @@ -0,0 +1,35 @@ +/* global miradorInstance */ + +describe('Window Sidebars', () => { + beforeAll(async () => { + await page.goto('http://127.0.0.1:4488/__tests__/integration/mirador/'); + }); + + it('renders and updates canvas level metadata', async () => { + await expect(page).toClick('#addBtn'); + await expect(page).toFill('#manifestURL', 'http://localhost:5000/api/001'); + await expect(page).toClick('#fetchBtn'); + // TODO: Refactor the app so we get rid of the wait + await page.waitFor(1000); + await expect(page).toMatchElement('li', { text: 'http://localhost:5000/api/001' }); + await expect(page).toClick('li button', { text: 'http://localhost:5000/api/001' }); + + await expect(page).toMatchElement( + 'h3', + { text: 'Bodleian Library Human Freaks 2 (33)' }, + ); + + const windows = await page.evaluate(() => ( + miradorInstance.store.getState().windows + )); + + const windowId = Object.values(windows) + .find(window => window.manifestId === 'http://localhost:5000/api/001') + .id; + + await expect(page).toMatchElement(`#${windowId} button[aria-label="Toggle window sidebar"]`); + await expect(page).toClick(`#${windowId} button[aria-label="Toggle window sidebar"]`); + + await expect(page).toMatchElement(`#${windowId} button[aria-label="Open information companion window"]`); + }); +}); diff --git a/__tests__/src/components/LabelValueMetadata.test.js b/__tests__/src/components/LabelValueMetadata.test.js new file mode 100644 index 0000000000000000000000000000000000000000..7eaa41c70b6a15aed121cf5b549fffcf0594718d --- /dev/null +++ b/__tests__/src/components/LabelValueMetadata.test.js @@ -0,0 +1,52 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import LabelValueMetadata from '../../../src/components/LabelValueMetadata'; + +describe('LabelValueMetadata', () => { + let wrapper; + let labelValuePair; + + describe('when the labelValuePair has content', () => { + beforeEach(() => { + labelValuePair = [ + { + label: 'Label 1', + value: 'Value 1', + }, + { + label: 'Label 2', + value: 'Value 2', + }, + ]; + wrapper = shallow( + <LabelValueMetadata labelValuePairs={labelValuePair} />, + ); + }); + + it('renders a dt/dd for each label/value pair', () => { + expect(wrapper.find('dl').length).toEqual(1); + + expect(wrapper.find('dt').length).toEqual(2); + expect(wrapper.find('dt').first().text()).toEqual('Label 1'); + expect(wrapper.find('dt').last().text()).toEqual('Label 2'); + + expect(wrapper.find('dd').length).toEqual(2); + expect(wrapper.find('dd').first().text()).toEqual('Value 1'); + expect(wrapper.find('dd').last().text()).toEqual('Value 2'); + }); + }); + + describe('when the labelValuePair has no content', () => { + beforeEach(() => { + labelValuePair = []; + wrapper = shallow( + <LabelValueMetadata labelValuePairs={labelValuePair} />, + ); + }); + + it('renders an empty fragment instead of an empty dl', () => { + expect(wrapper.find('dl').length).toEqual(0); + expect(wrapper.matchesElement(<></>)).toBe(true); + }); + }); +}); diff --git a/__tests__/src/components/WindowSideBarInfoPanel.test.js b/__tests__/src/components/WindowSideBarInfoPanel.test.js index 21c6fabc32f12d3076bea7c6d4862de33376c7fa..ea9d1f7a2214d86e42c75237d0733643bf235deb 100644 --- a/__tests__/src/components/WindowSideBarInfoPanel.test.js +++ b/__tests__/src/components/WindowSideBarInfoPanel.test.js @@ -1,41 +1,74 @@ import React from 'react'; import { shallow } from 'enzyme'; import Typography from '@material-ui/core/Typography'; -import createStore from '../../../src/state/createStore'; -import * as actions from '../../../src/state/actions'; import WindowSideBarInfoPanel from '../../../src/components/WindowSideBarInfoPanel'; -import fixture from '../../fixtures/version-2/001.json'; +import LabelValueMetadata from '../../../src/components/LabelValueMetadata'; describe('WindowSideBarInfoPanel', () => { let wrapper; - let manifest; - const store = createStore(); - - beforeEach(() => { - store.dispatch(actions.receiveManifest('foo', fixture)); - manifest = store.getState().manifests.foo; - wrapper = shallow( - <WindowSideBarInfoPanel manifest={manifest} />, - ).dive(); + + describe('when metadata is present', () => { + beforeEach(() => { + wrapper = shallow( + <WindowSideBarInfoPanel + canvasLabel="The Canvas Label" + canvasDescription="The Canvas Description" + canvasMetadata={[{ label: {}, value: {} }]} + manifestLabel="The Manifest Label" + manifestDescription="The Manifest Description" + />, + ).dive(); + }); + + it('renders canvas level label, description, and metadata', () => { + expect( + wrapper.find('WithStyles(Typography)[variant="h3"]').first().matchesElement( + <Typography>The Canvas Label</Typography>, + ), + ).toBe(true); + + expect( + wrapper.find('WithStyles(Typography)[variant="body2"]').first().matchesElement( + <Typography>The Canvas Description</Typography>, + ), + ).toBe(true); + + expect(wrapper.find(LabelValueMetadata).length).toBe(2); // one for canvas one for manifest + }); + + it('renders manifest level label, description, and metadata', () => { + expect( + wrapper.find('WithStyles(Typography)[variant="h3"]').last().matchesElement( + <Typography>The Manifest Label</Typography>, + ), + ).toBe(true); + + expect( + wrapper.find('WithStyles(Typography)[variant="body2"]').last().matchesElement( + <Typography>The Manifest Description</Typography>, + ), + ).toBe(true); + + expect(wrapper.find(LabelValueMetadata).length).toBe(2); // one for canvas one for manifest + }); }); - it('renders without an error', () => { - expect( - wrapper.find('WithStyles(Typography)[variant="h2"]').first().matchesElement( - <Typography>aboutThisItem</Typography>, - ), - ).toBe(true); - - expect( - wrapper.find('WithStyles(Typography)[variant="h3"]').first().matchesElement( - <Typography>Bodleian Library Human Freaks 2 (33)</Typography>, - ), - ).toBe(true); - - expect( - wrapper.find('WithStyles(Typography)[variant="body2"]').first().matchesElement( - <Typography>[Handbill of Mr. Becket, [1787] ]</Typography>, - ), - ).toBe(true); + describe('when metadata is not present', () => { + beforeEach(() => { + wrapper = shallow( + <WindowSideBarInfoPanel />, + ).dive(); + }); + + it('does not render empty elements', () => { + expect( + wrapper.find('WithStyles(Typography)[variant="h2"]').first().matchesElement( + <Typography>aboutThisItem</Typography>, + ), + ).toBe(true); + + expect(wrapper.find('WithStyles(Typography)[variant="h3"]').length).toEqual(0); + expect(wrapper.find('WithStyles(Typography)[variant="body2"]').length).toEqual(0); + }); }); }); diff --git a/__tests__/src/components/WindowSideBarPanel.test.js b/__tests__/src/components/WindowSideBarPanel.test.js index f31c05019910c90a36cb66ede06d229e6d24a5f7..49caad0e50aa4c874fcd7f8e215d315307895615 100644 --- a/__tests__/src/components/WindowSideBarPanel.test.js +++ b/__tests__/src/components/WindowSideBarPanel.test.js @@ -1,24 +1,14 @@ import React from 'react'; import { shallow } from 'enzyme'; -import createStore from '../../../src/state/createStore'; -import * as actions from '../../../src/state/actions'; import WindowSideBarPanel from '../../../src/components/WindowSideBarPanel'; import WindowSideBarInfoPanel from '../../../src/containers/WindowSideBarInfoPanel'; -import fixture from '../../fixtures/version-2/001.json'; describe('WindowSideBarPanel', () => { let wrapper; - let manifest; - const store = createStore(); - - beforeEach(() => { - store.dispatch(actions.receiveManifest('foo', fixture)); - manifest = store.getState().manifests.foo; - }); describe('when the sideBarPanel is set to "info"', () => { beforeEach(() => { - wrapper = shallow(<WindowSideBarPanel sideBarPanel="info" manifest={manifest} />); + wrapper = shallow(<WindowSideBarPanel windowId="abc123" sideBarPanel="info" />); }); it('renders the WindowSideBarInfoPanel', () => { @@ -28,7 +18,7 @@ describe('WindowSideBarPanel', () => { describe('when the sideBarPanel is set to "closed" (or any other unknown value)', () => { beforeEach(() => { - wrapper = shallow(<WindowSideBarPanel sideBarPanel="closed" manifest={manifest} />); + wrapper = shallow(<WindowSideBarPanel windowId="abc123" sideBarPanel="closed" />); }); it('does not render any panel component', () => { diff --git a/__tests__/src/selectors/index.test.js b/__tests__/src/selectors/index.test.js index 1e7b7a0566550ad919d686b111a01c40dfc5e21b..63e42b16fd99f84a7b2b9883406bf5427e41d422 100644 --- a/__tests__/src/selectors/index.test.js +++ b/__tests__/src/selectors/index.test.js @@ -1,9 +1,15 @@ 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 { + getCanvasLabel, + getDestructuredMetadata, + getSelectedCanvas, getWindowManifest, getManifestLogo, getManifestCanvases, + getManifestDescription, getThumbnailNavigationPosition, getManifestTitle, getWindowViewType, @@ -139,3 +145,112 @@ describe('getWindowViewType', () => { expect(received).toBeUndefined(); }); }); + +describe('getManifestDescription', () => { + it('should return manifest description', () => { + const manifest = { manifestation: manifesto.create(manifestFixture001) }; + const received = getManifestDescription(manifest); + expect(received).toBe('[Handbill of Mr. Becket, [1787] ]'); + }); + + it('should return undefined if manifest undefined', () => { + const received = getManifestDescription(undefined); + expect(received).toBeUndefined(); + }); + + it('should return undefined if no manifestation', () => { + const manifest = {}; + const received = getManifestDescription(manifest); + expect(received).toBeUndefined(); + }); +}); + +describe('getSelectedCanvas', () => { + const state = { + windows: { + a: { + id: 'a', + manifestId: 'x', + canvasIndex: 1, + }, + }, + manifests: { + x: { + id: 'x', + manifestation: manifesto.create(manifestFixture019), + }, + }, + }; + + const noManifestationState = { + windows: { + a: { + id: 'a', + manifestId: 'x', + canvasIndex: 1, + }, + }, + manifests: { + x: { + id: 'x', + }, + }, + }; + + it('should return canvas based on the canvas index stored window state', () => { + const selectedCanvas = getSelectedCanvas(state, 'a'); + + expect(selectedCanvas.id).toEqual( + 'https://purl.stanford.edu/fr426cg9537/iiif/canvas/fr426cg9537_1', + ); + }); + + it('should return an empty object when there is no manifestation to get a canvas from', () => { + const selectedCanvas = getSelectedCanvas(noManifestationState, 'a'); + + expect(selectedCanvas).toEqual({}); + }); +}); + +describe('getCanvasLabel', () => { + it('should return label of the canvas', () => { + const canvas = manifesto + .create(manifestFixture001) + .getSequences()[0] + .getCanvases()[0]; + + const received = getCanvasLabel(canvas); + expect(received).toBe('Whole Page'); + }); + + it('should return the given canvas index (+1) if the canvas is undefined', () => { + expect(getCanvasLabel(undefined)).toBe(1); + expect(getCanvasLabel(undefined, 2)).toBe(3); + }); + + it('should return the canvas index (+1) if no manifestation', () => { + const canvas = { getLabel: () => {} }; + const received = getCanvasLabel(canvas); + expect(received).toBe(1); + }); +}); + +describe('getDestructuredMetadata', () => { + it('should return the first value of label/value attributes for each object in the array ', () => { + const iiifResource = manifesto.create(manifestFixture002); + const received = getDestructuredMetadata(iiifResource); + const expected = [{ + label: 'date', + value: 'some date', + }]; + + expect(received).toEqual(expected); + }); + + it('returns an empty array if there is no metadata', () => { + const iiifResource = manifesto.create(manifestFixture019); + const received = getDestructuredMetadata(iiifResource); + + expect(received).toEqual([]); + }); +}); diff --git a/src/components/LabelValueMetadata.js b/src/components/LabelValueMetadata.js new file mode 100644 index 0000000000000000000000000000000000000000..4721dc32d56290d0e7ae9bec12fd16e2f5cc137f --- /dev/null +++ b/src/components/LabelValueMetadata.js @@ -0,0 +1,40 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +/** + * Renders label/value pair metadata in a dl + * @prop {object} labelValuePair + */ +class LabelValueMetadata extends Component { + /** + * render + * @return {String} - HTML markup for the component + */ + render() { + const { labelValuePairs } = this.props; + + if (labelValuePairs.length === 0) { + return (<></>); + } + + /* eslint-disable react/no-array-index-key */ + // Disabling array index key for dt/dd elements as + // they are intended to display metadata that will not + // need to be re-rendered internally in any meaningful way + return ( + <dl> + {labelValuePairs.reduce((acc, labelValuePair, i) => acc.concat([ + <dt key={`label-${i}`}>{labelValuePair.label}</dt>, + <dd key={`value-${i}`}>{labelValuePair.value}</dd>, + ]), [])} + </dl> + ); + /* eslint-enable react/no-array-index-key */ + } +} + +LabelValueMetadata.propTypes = { + labelValuePairs: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types, +}; + +export default LabelValueMetadata; diff --git a/src/components/Window.js b/src/components/Window.js index 2d1fec5604b0aa076fd570c83bd5777eb63d7f24..380c1c8383a26237e7f19ec7be96dbcbb56dd500 100644 --- a/src/components/Window.js +++ b/src/components/Window.js @@ -16,7 +16,7 @@ class Window extends Component { render() { const { manifest, window } = this.props; return ( - <div className={ns('window')}> + <div id={window.id} className={ns('window')}> <WindowTopBar windowId={window.id} manifest={manifest} diff --git a/src/components/WindowMiddleContent.js b/src/components/WindowMiddleContent.js index 766547a3528a327168961ab22a4606fdd2360d6a..c1dd1cca2536de1d3dd00b67e997da9e4f13e3ae 100644 --- a/src/components/WindowMiddleContent.js +++ b/src/components/WindowMiddleContent.js @@ -35,12 +35,7 @@ class WindowMiddleContent extends Component { const { manifest, window } = this.props; return ( <div className={ns('window-middle-content')}> - <WindowSideBar - windowId={window.id} - manifest={manifest} - sideBarOpen={window.sideBarOpen} - sideBarPanel={window.sideBarPanel} - /> + <WindowSideBar windowId={window.id} /> <CompanionWindow windowId={window.id} manifest={manifest} diff --git a/src/components/WindowSideBar.js b/src/components/WindowSideBar.js index 4221c0fcbda3c75a850e73c1c27a1a8ad5955bbe..31d4b7200d0adef39574b0d210bd80114dd4c887 100644 --- a/src/components/WindowSideBar.js +++ b/src/components/WindowSideBar.js @@ -5,7 +5,7 @@ import Drawer from '@material-ui/core/Drawer'; import { withStyles } from '@material-ui/core/styles'; import List from '@material-ui/core/List'; import WindowSideBarButtons from '../containers/WindowSideBarButtons'; -import WindowSideBarPanel from './WindowSideBarPanel'; +import WindowSideBarPanel from '../containers/WindowSideBarPanel'; import ns from '../config/css-ns'; /** @@ -18,7 +18,7 @@ class WindowSideBar extends Component { */ render() { const { - classes, manifest, windowId, sideBarOpen, sideBarPanel, + classes, windowId, sideBarOpen, sideBarPanel, } = this.props; return ( @@ -38,7 +38,7 @@ class WindowSideBar extends Component { }} > <List> - <WindowSideBarButtons windowId={windowId} sideBarPanel={sideBarPanel} /> + <WindowSideBarButtons windowId={windowId} /> </List> </Drawer> <Drawer @@ -55,11 +55,7 @@ class WindowSideBar extends Component { style: { position: 'absolute', width: '200px' }, }} > - <WindowSideBarPanel - manifest={manifest} - windowId={windowId} - sideBarPanel={sideBarPanel} - /> + <WindowSideBarPanel windowId={windowId} /> </Drawer> </> ); @@ -69,14 +65,12 @@ class WindowSideBar extends Component { WindowSideBar.propTypes = { classes: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types, - manifest: PropTypes.object, // eslint-disable-line react/forbid-prop-types windowId: PropTypes.string.isRequired, sideBarOpen: PropTypes.bool, sideBarPanel: PropTypes.string, }; WindowSideBar.defaultProps = { - manifest: {}, sideBarOpen: false, sideBarPanel: 'closed', }; diff --git a/src/components/WindowSideBarInfoPanel.js b/src/components/WindowSideBarInfoPanel.js index aa646786848c2b78dd24c9efa59b7790c86a93d9..9766310542c71b49fbf390bb8fd7816a8b9e78f4 100644 --- a/src/components/WindowSideBarInfoPanel.js +++ b/src/components/WindowSideBarInfoPanel.js @@ -1,65 +1,65 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import Divider from '@material-ui/core/Divider'; import Typography from '@material-ui/core/Typography'; import { withStyles } from '@material-ui/core/styles'; +import LabelValueMetadata from './LabelValueMetadata'; import ns from '../config/css-ns'; /** * WindowSideBarInfoPanel */ class WindowSideBarInfoPanel extends Component { - /** - * manifestLabel - get the label from the manifesto manifestation - * @return String - */ - manifestLabel() { - const { manifest } = this.props; - - if (manifest.manifestation) { - return manifest.manifestation.getLabel().map(label => label.value)[0]; - } - return ''; - } - - /** - * manifestDescription - get the description from the manifesto manifestation - * @return String - */ - manifestDescription() { - const { manifest } = this.props; - - if (manifest.manifestation) { - return manifest.manifestation.getDescription().map(label => label.value); - } - return ''; - } - /** * render * @return */ render() { - const { classes, t } = this.props; + const { + canvasDescription, + canvasLabel, + canvasMetadata, + classes, + manifestDescription, + manifestLabel, + manifestMetadata, + t, + } = this.props; return ( <div className={ns('window-sidebar-info-panel')}> <Typography variant="h2" className={classes.windowSideBarH2}>{t('aboutThisItem')}</Typography> - <Typography variant="h3" className={classes.windowSideBarH3}>{this.manifestLabel()}</Typography> - <Typography variant="body2">{this.manifestDescription()}</Typography> + {canvasLabel && <Typography variant="h3" className={classes.windowSideBarH3}>{canvasLabel}</Typography>} + {canvasDescription && <Typography variant="body2">{canvasDescription}</Typography>} + {canvasMetadata && <LabelValueMetadata labelValuePairs={canvasMetadata} />} + <Divider /> + {manifestLabel && <Typography variant="h3" className={classes.windowSideBarH3}>{manifestLabel}</Typography>} + {manifestDescription && <Typography variant="body2">{manifestDescription}</Typography>} + {manifestMetadata && <LabelValueMetadata labelValuePairs={manifestMetadata} />} </div> ); } } WindowSideBarInfoPanel.propTypes = { + canvasDescription: PropTypes.string, + canvasLabel: PropTypes.string, + canvasMetadata: PropTypes.array, // eslint-disable-line react/forbid-prop-types classes: PropTypes.object, // eslint-disable-line react/forbid-prop-types - manifest: PropTypes.object, // eslint-disable-line react/forbid-prop-types + manifestLabel: PropTypes.string, + manifestDescription: PropTypes.string, + manifestMetadata: PropTypes.array, // eslint-disable-line react/forbid-prop-types t: PropTypes.func, }; WindowSideBarInfoPanel.defaultProps = { + canvasDescription: null, + canvasLabel: null, + canvasMetadata: [], classes: {}, - manifest: {}, + manifestLabel: null, + manifestDescription: null, + manifestMetadata: [], t: key => key, }; diff --git a/src/components/WindowSideBarPanel.js b/src/components/WindowSideBarPanel.js index 2d647b70178e3c29f8e028f6d16f5f0dd77c1f2f..a0dd1bfeca168b1f2c7252737707518a3567125d 100644 --- a/src/components/WindowSideBarPanel.js +++ b/src/components/WindowSideBarPanel.js @@ -12,10 +12,10 @@ class WindowSideBarPanel extends Component { * @return React Component */ activePanelComponent() { - const { manifest, sideBarPanel } = this.props; + const { windowId, sideBarPanel } = this.props; switch (sideBarPanel) { case 'info': - return <WindowSideBarInfoPanel manifest={manifest} />; + return <WindowSideBarInfoPanel windowId={windowId} />; default: return null; } @@ -35,11 +35,10 @@ class WindowSideBarPanel extends Component { } WindowSideBarPanel.propTypes = { - manifest: PropTypes.object, // eslint-disable-line react/forbid-prop-types sideBarPanel: PropTypes.string, + windowId: PropTypes.string.isRequired, }; WindowSideBarPanel.defaultProps = { - manifest: {}, sideBarPanel: 'closed', // Closed will fall out to the default null case for the actiuve panel }; diff --git a/src/containers/WindowSideBar.js b/src/containers/WindowSideBar.js index fbdc2c2f599ea654bde3c7a0410f5ef86ed9a4a3..0a7232205cf9b63c7f35e1baca9a067e1d12d210 100644 --- a/src/containers/WindowSideBar.js +++ b/src/containers/WindowSideBar.js @@ -1,4 +1,24 @@ +import { connect } from 'react-redux'; +import { compose } from 'redux'; import miradorWithPlugins from '../lib/miradorWithPlugins'; import WindowSideBar from '../components/WindowSideBar'; -export default miradorWithPlugins(WindowSideBar); +/** + * mapStateToProps - to hook up connect + * @memberof WindowSideBar + * @private + */ +const mapStateToProps = (state, props) => ( + { + sideBarOpen: state.windows[props.windowId].sideBarOpen, + sideBarPanel: state.windows[props.windowId].sideBarPanel, + } +); + +const enhance = compose( + connect(mapStateToProps, null), + miradorWithPlugins, + // further HOC +); + +export default enhance(WindowSideBar); diff --git a/src/containers/WindowSideBarButtons.js b/src/containers/WindowSideBarButtons.js index e4a652b3483c0e250c4d81a0f7fb49d13bf650aa..b95a16ffd930441dff96d2d0844efaee542a0b9d 100644 --- a/src/containers/WindowSideBarButtons.js +++ b/src/containers/WindowSideBarButtons.js @@ -17,8 +17,19 @@ const mapDispatchToProps = (dispatch, props) => ({ ), }); + +/** + * mapStateToProps - used to hook up connect to state + * @memberof WindowSideButtons + * @private + */ +const mapStateToProps = (state, { windowId }) => ({ + sideBarPanel: state.windows[windowId].sideBarPanel, +}); + + const enhance = compose( - connect(null, mapDispatchToProps), + connect(mapStateToProps, mapDispatchToProps), miradorWithPlugins, withNamespaces(), // further HOC go here diff --git a/src/containers/WindowSideBarInfoPanel.js b/src/containers/WindowSideBarInfoPanel.js index 0e2434944b72965b9656fc6a828a77b7ab0f2b43..59b77179421df2a37c4a0b3b85f02a1326dcda20 100644 --- a/src/containers/WindowSideBarInfoPanel.js +++ b/src/containers/WindowSideBarInfoPanel.js @@ -1,9 +1,36 @@ import { compose } from 'redux'; +import { connect } from 'react-redux'; import { withNamespaces } from 'react-i18next'; import miradorWithPlugins from '../lib/miradorWithPlugins'; +import { + getDestructuredMetadata, + getCanvasLabel, + getManifestDescription, + getManifestTitle, + getSelectedCanvas, + getWindowManifest, +} from '../state/selectors'; import WindowSideBarInfoPanel from '../components/WindowSideBarInfoPanel'; +/** + * mapStateToProps - to hook up connect + * @memberof WindowSideBarInfoPanel + * @private + */ +const mapStateToProps = (state, { windowId }) => ({ + canvasLabel: getCanvasLabel( + getSelectedCanvas(state, windowId), + state.windows[windowId].canvasIndex, + ), + canvasDescription: getSelectedCanvas(state, windowId).getProperty('description'), + canvasMetadata: getDestructuredMetadata(getSelectedCanvas(state, windowId)), + manifestLabel: getManifestTitle(getWindowManifest(state, windowId)), + manifestDescription: getManifestDescription(getWindowManifest(state, windowId)), + manifestMetadata: getDestructuredMetadata(getWindowManifest(state, windowId).manifestation), +}); + const enhance = compose( + connect(mapStateToProps, null), withNamespaces(), miradorWithPlugins, // further HOC diff --git a/src/containers/WindowSideBarPanel.js b/src/containers/WindowSideBarPanel.js index 1c271c43d54b3cad031d846a3a1a469e879beaf5..81c331df2f704de278cc340496083aac08526a37 100644 --- a/src/containers/WindowSideBarPanel.js +++ b/src/containers/WindowSideBarPanel.js @@ -1,4 +1,15 @@ +import { connect } from 'react-redux'; +import { compose } from 'redux'; import miradorWithPlugins from '../lib/miradorWithPlugins'; import WindowSideBarPanel from '../components/WindowSideBarPanel'; -export default miradorWithPlugins(WindowSideBarPanel); +/** */ +const mapStateToProps = (state, { windowId }) => ({ + sideBarPanel: state.windows[windowId].sideBarPanel, +}); + +export default compose( + connect(mapStateToProps, null), + miradorWithPlugins, + // further HOC +)(WindowSideBarPanel); diff --git a/src/state/selectors/index.js b/src/state/selectors/index.js index 36b3189cc482135b5f3691906e89d948ff6d416b..b7ed6c2d335bdef94d198de49dafc4fdbe246dbe 100644 --- a/src/state/selectors/index.js +++ b/src/state/selectors/index.js @@ -34,6 +34,24 @@ export function getManifestCanvases(manifest) { return manifest.manifestation.getSequences()[0].getCanvases(); } +/** +* Return the current canvas selected in a window +* @param {object} state +* @param {String} windowId +* @return {Object} +*/ +export function getSelectedCanvas(state, windowId) { + const manifest = getWindowManifest(state, windowId); + const { canvasIndex } = state.windows[windowId]; + + if (!manifest.manifestation) { + return {}; + } + + return manifest.manifestation.getSequences()[0].getCanvasByIndex(canvasIndex); +} + + /** Return position of thumbnail navigation in a certain window. * @param {object} state * @param {String} windowId @@ -63,3 +81,44 @@ export function getManifestTitle(manifest) { export function getWindowViewType(state, windowId) { return state.windows[windowId] && state.windows[windowId].view; } + +/** +* Return manifest description +* @param {object} manifest +* @return {String} +*/ +export function getManifestDescription(manifest) { + return manifest + && manifest.manifestation + && manifest.manifestation.getDescription().map(label => label.value)[0]; +} + +/** +* Return canvas label, or alternatively return the given index + 1 to be displayed +* @param {object} canvas +* @return {String|Integer} +*/ +export function getCanvasLabel(canvas, canvasIndex) { + return (canvas + && canvas.getLabel() + && canvas.getLabel().map(label => label.value)[0]) + || (canvasIndex || 0) + 1; +} + +/** +* Return canvas metadata in a label / value structure +* This is a potential seam for pulling the i18n locale from +* state and plucking out the appropriate language. +* For now we're just getting the first. +* @param {object} IIIF Resource +* @return {Array[Object]} +*/ +export function getDestructuredMetadata(iiifResoruce) { + return (iiifResoruce + && iiifResoruce.getMetadata() + && iiifResoruce.getMetadata().map(resource => ({ + label: resource.label[0].value, + value: resource.value[0].value, + })) + ); +}