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

Merge pull request #1902 from ProjectMirador/initial-manifests

Add support for configuration initial manifests for the viewer
parents d80a2cb6 92efb1aa
No related branches found
No related tags found
No related merge requests found
...@@ -19,7 +19,23 @@ ...@@ -19,7 +19,23 @@
{ {
loadedManifest: 'https://iiif.bodleian.ox.ac.uk/iiif/manifest/e32a277e-91e2-4a6d-8ba6-cc4bad230410.json', loadedManifest: 'https://iiif.bodleian.ox.ac.uk/iiif/manifest/e32a277e-91e2-4a6d-8ba6-cc4bad230410.json',
thumbnailNavigationPosition: 'off', thumbnailNavigationPosition: 'off',
}] }],
manifests: {
"http://media.nga.gov/public/manifests/nga_highlights.json": { provider: "National Gallery of Art"},
"https://data.ucd.ie/api/img/manifests/ucdlib:33064": { provider: "Irish Architectural Archive"},
"https://wellcomelibrary.org/iiif/b18035723/manifest": { provider: "Wellcome Library"},
"http://dams.llgc.org.uk/iiif/2.0/4389767/manifest.json": { provider: "The National Library of Wales"},
"https://demos.biblissima.fr/iiif/metadata/florus-dispersus/manifest.json": { provider: "Biblissima"},
"http://beta.biblissima.fr/iiif/manifest/ark:/43093/desc57cb76cd3739a24a9277b6669d95b5f3a590e771": { provider: "Biblissima"},
"https://www.e-codices.unifr.ch/metadata/iiif/gau-Fragment/manifest.json": { provider: "e-codices - Virtual Manuscript Library of Switzerland"},
"https://wellcomelibrary.org/iiif/collection/b18031511": { provider: "Wellcome Library"},
"https://gallica.bnf.fr/iiif/ark:/12148/btv1b10022508f/manifest.json": { provider: "Bibliothèque nationale de France"},
"https://manifests.britishart.yale.edu/Osbornfa1": { provider: "Beinecke Rare Book and Manuscript Library, Yale University"},
"https://iiif.biblissima.fr/chateauroux/B360446201_MS0005/manifest.json": { provider: "Biblissima"},
"https://iiif.durham.ac.uk/manifests/trifle/32150/t1/m4/q7/t1m4q77fr328/manifest": { provider: "Durham University Library"},
// "https://iiif.vam.ac.uk/collections/O1023003/manifest.json": { provider: "Ocean liners"},
"http://storiiies.cogapp.com/holbein/manifest.json": { provider: "National Gallery, London"}
}
}); });
</script> </script>
</body> </body>
......
...@@ -45,7 +45,7 @@ describe('manifest actions', () => { ...@@ -45,7 +45,7 @@ describe('manifest actions', () => {
it('dispatches the REQUEST_MANIFEST action', () => { it('dispatches the REQUEST_MANIFEST action', () => {
store.dispatch(actions.fetchManifest('https://purl.stanford.edu/sn904cj3429/iiif/manifest')); store.dispatch(actions.fetchManifest('https://purl.stanford.edu/sn904cj3429/iiif/manifest'));
expect(store.getActions()).toEqual([ expect(store.getActions()).toEqual([
{ manifestId: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', type: 'REQUEST_MANIFEST' }, { manifestId: 'https://purl.stanford.edu/sn904cj3429/iiif/manifest', type: 'REQUEST_MANIFEST', properties: { isFetching: true } },
]); ]);
}); });
it('dispatches the REQUEST_MANIFEST and then RECEIVE_MANIFEST', () => { it('dispatches the REQUEST_MANIFEST and then RECEIVE_MANIFEST', () => {
......
...@@ -2,60 +2,60 @@ import React from 'react'; ...@@ -2,60 +2,60 @@ import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import ManifestListItem from '../../../src/components/ManifestListItem'; import ManifestListItem from '../../../src/components/ManifestListItem';
describe('ManifestListItem', () => { /** */
it('renders without an error', () => { function createWrapper(props) {
const addWindow = jest.fn(); return shallow(
const wrapper = shallow(
<ManifestListItem <ManifestListItem
manifestId="http://example.com" manifestId="http://example.com"
title="xyz" title="xyz"
ready ready
addWindow={addWindow} addWindow={() => {}}
fetchManifest={() => {}}
t={t => t} t={t => t}
{...props}
/>, />,
).dive(); ).dive(); // to unwrapp HOC created by withStyle()
}
describe('ManifestListItem', () => {
it('renders without an error', () => {
const wrapper = createWrapper();
expect(wrapper.find('.mirador-manifest-list-item').length).toBe(1); expect(wrapper.find('.mirador-manifest-list-item').length).toBe(1);
expect(wrapper.find('WithStyles(ButtonBase)').length).toBe(1); expect(wrapper.find('WithStyles(ButtonBase)').length).toBe(1);
expect(wrapper.find('WithStyles(ButtonBase) WithStyles(Typography)').children().text()).toEqual('xyz'); expect(wrapper.find('WithStyles(ButtonBase) WithStyles(Typography)').children().text()).toEqual('xyz');
}); });
it('renders a placeholder element until real data is available', () => { it('renders a placeholder element until real data is available', () => {
const addWindow = jest.fn(); const wrapper = createWrapper({ ready: false });
const wrapper = shallow(
<ManifestListItem manifestId="http://example.com" addWindow={addWindow} />,
).dive();
expect(wrapper.find('.mirador-manifest-list-item').length).toBe(1); expect(wrapper.find('.mirador-manifest-list-item').length).toBe(1);
expect(wrapper.find('ReactPlaceholder').length).toBe(1); expect(wrapper.find('ReactPlaceholder').length).toBe(1);
}); });
it('renders an error message if fetching the manifest failed', () => {
const wrapper = createWrapper({ error: 'This is an error message' });
expect(wrapper.find('WithStyles(Paper)').length).toBe(1);
expect(wrapper.find('WithStyles(Paper)').children().text()).toEqual('This is an error message');
});
it('updates and adds window when button clicked', () => { it('updates and adds window when button clicked', () => {
const addWindow = jest.fn(); const addWindow = jest.fn();
const wrapper = shallow( const wrapper = createWrapper({ addWindow });
<ManifestListItem manifestId="http://example.com" title="xyz" addWindow={addWindow} />,
).dive();
wrapper.find('WithStyles(ButtonBase)').simulate('click'); wrapper.find('WithStyles(ButtonBase)').simulate('click');
expect(addWindow).toHaveBeenCalledTimes(1); expect(addWindow).toHaveBeenCalledTimes(1);
}); });
it('uses the manifest id if the title is not available', () => { it('uses the manifest id if the title is not available', () => {
const addWindow = jest.fn(); const wrapper = createWrapper({ ready: true, title: null });
const wrapper = shallow(
<ManifestListItem manifestId="http://example.com" ready addWindow={addWindow} />,
).dive();
expect(wrapper.find('WithStyles(ButtonBase)').length).toBe(1); expect(wrapper.find('WithStyles(ButtonBase)').length).toBe(1);
expect(wrapper.find('WithStyles(ButtonBase) WithStyles(Typography)').children().text()).toEqual('http://example.com'); expect(wrapper.find('WithStyles(ButtonBase) WithStyles(Typography)').children().text()).toEqual('http://example.com');
}); });
it('displays the provider information', () => { it('displays the provider information', () => {
const addWindow = jest.fn(); const wrapper = createWrapper({ provider: 'ACME' });
const wrapper = shallow(
<ManifestListItem manifestId="http://example.com" ready provider="ACME" addWindow={addWindow} />,
).dive();
expect(wrapper.find('WithStyles(Typography).mirador-manifest-list-item-provider').children().text()).toEqual('ACME'); expect(wrapper.find('WithStyles(Typography).mirador-manifest-list-item-provider').children().text()).toEqual('ACME');
}); });
it('displays a placeholder provider if no information is given', () => { it('displays a placeholder provider if no information is given', () => {
const addWindow = jest.fn(); const wrapper = createWrapper();
const wrapper = shallow(
<ManifestListItem manifestId="http://example.com" ready addWindow={addWindow} />,
).dive();
expect(wrapper.find('WithStyles(Typography).mirador-manifest-list-item-provider').children().text()).toEqual('addedFromUrl'); expect(wrapper.find('WithStyles(Typography).mirador-manifest-list-item-provider').children().text()).toEqual('addedFromUrl');
}); });
}); });
...@@ -44,7 +44,8 @@ describe('MiradorViewer', () => { ...@@ -44,7 +44,8 @@ describe('MiradorViewer', () => {
it('transforms config values to actions to dispatch to store', () => { it('transforms config values to actions to dispatch to store', () => {
instance = new MiradorViewer({ instance = new MiradorViewer({
id: 'mirador', id: 'mirador',
windows: [{ windows: [
{
loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843', loadedManifest: 'https://iiif.harvardartmuseums.org/manifests/object/299843',
canvasIndex: 2, canvasIndex: 2,
}, },
...@@ -54,9 +55,12 @@ describe('MiradorViewer', () => { ...@@ -54,9 +55,12 @@ describe('MiradorViewer', () => {
view: 'book', view: 'book',
}, },
], ],
manifests: {
'http://media.nga.gov/public/manifests/nga_highlights.json': { provider: 'National Gallery of Art' },
},
}); });
const { windows } = instance.store.getState(); const { windows, manifests } = instance.store.getState();
const windowIds = Object.keys(windows); const windowIds = Object.keys(windows);
expect(Object.keys(windowIds).length).toBe(2); expect(Object.keys(windowIds).length).toBe(2);
expect(windows[windowIds[0]].canvasIndex).toBe(2); expect(windows[windowIds[0]].canvasIndex).toBe(2);
...@@ -65,6 +69,10 @@ describe('MiradorViewer', () => { ...@@ -65,6 +69,10 @@ describe('MiradorViewer', () => {
expect(windows[windowIds[1]].thumbnailNavigationPosition).toBe('off'); expect(windows[windowIds[1]].thumbnailNavigationPosition).toBe('off');
expect(windows[windowIds[0]].view).toBe('single'); expect(windows[windowIds[0]].view).toBe('single');
expect(windows[windowIds[1]].view).toBe('book'); expect(windows[windowIds[1]].view).toBe('book');
const manifestIds = Object.keys(manifests);
expect(Object.keys(manifestIds).length).toBe(2);
expect(manifests['http://media.nga.gov/public/manifests/nga_highlights.json'].provider).toBe('National Gallery of Art');
}); });
}); });
}); });
...@@ -9,7 +9,6 @@ describe('manifests reducer', () => { ...@@ -9,7 +9,6 @@ describe('manifests reducer', () => {
})).toEqual({ })).toEqual({
abc123: { abc123: {
id: 'abc123', id: 'abc123',
isFetching: true,
}, },
}); });
}); });
......
...@@ -25,10 +25,30 @@ const handleOpenButtonClick = (event, manifest, addWindow) => { ...@@ -25,10 +25,30 @@ const handleOpenButtonClick = (event, manifest, addWindow) => {
/** */ /** */
class ManifestListItem extends React.Component { class ManifestListItem extends React.Component {
/** */
componentDidMount() {
const {
fetchManifest, manifestId, ready, isFetching, error,
} = this.props;
if (!ready && !error && !isFetching) fetchManifest(manifestId);
}
/** */ /** */
render() { render() {
const { const {
manifestId, ready, title, thumbnail, logo, addWindow, handleClose, size, classes, provider, t, manifestId,
ready,
title,
thumbnail,
logo,
addWindow,
handleClose,
size,
classes,
provider,
t,
error,
} = this.props; } = this.props;
const placeholder = ( const placeholder = (
...@@ -48,6 +68,14 @@ class ManifestListItem extends React.Component { ...@@ -48,6 +68,14 @@ class ManifestListItem extends React.Component {
</Grid> </Grid>
); );
if (error) {
return (
<Paper elevation={1} className={classes.root} data-manifestid={manifestId}>
{error}
</Paper>
);
}
return ( return (
<Paper elevation={1} className={classes.root} data-manifestid={manifestId}> <Paper elevation={1} className={classes.root} data-manifestid={manifestId}>
<ReactPlaceholder <ReactPlaceholder
...@@ -113,6 +141,9 @@ ManifestListItem.propTypes = { ...@@ -113,6 +141,9 @@ ManifestListItem.propTypes = {
classes: PropTypes.object, // eslint-disable-line react/forbid-prop-types classes: PropTypes.object, // eslint-disable-line react/forbid-prop-types
provider: PropTypes.string, provider: PropTypes.string,
t: PropTypes.func, t: PropTypes.func,
fetchManifest: PropTypes.func.isRequired,
error: PropTypes.string,
isFetching: PropTypes.bool,
}; };
ManifestListItem.defaultProps = { ManifestListItem.defaultProps = {
...@@ -125,6 +156,8 @@ ManifestListItem.defaultProps = { ...@@ -125,6 +156,8 @@ ManifestListItem.defaultProps = {
size: 0, size: 0,
provider: null, provider: null,
t: key => key, t: key => key,
error: null,
isFetching: false,
}; };
/** */ /** */
......
...@@ -13,6 +13,8 @@ const mapStateToProps = (state, { manifestId }) => { ...@@ -13,6 +13,8 @@ const mapStateToProps = (state, { manifestId }) => {
return { return {
ready: !!manifest.manifestation, ready: !!manifest.manifestation,
error: manifest.error,
isFetching: manifest.isFetching,
title: getManifestTitle(manifest), title: getManifestTitle(manifest),
logo: getManifestLogo(manifest), logo: getManifestLogo(manifest),
thumbnail: getManifestThumbnail(manifest), thumbnail: getManifestThumbnail(manifest),
...@@ -26,7 +28,7 @@ const mapStateToProps = (state, { manifestId }) => { ...@@ -26,7 +28,7 @@ const mapStateToProps = (state, { manifestId }) => {
* @memberof ManifestListItem * @memberof ManifestListItem
* @private * @private
*/ */
const mapDispatchToProps = { addWindow: actions.addWindow }; const mapDispatchToProps = { addWindow: actions.addWindow, fetchManifest: actions.fetchManifest };
const enhance = compose( const enhance = compose(
connect(mapStateToProps, mapDispatchToProps), connect(mapStateToProps, mapDispatchToProps),
......
...@@ -63,6 +63,12 @@ class MiradorViewer { ...@@ -63,6 +63,12 @@ class MiradorViewer {
thumbnailNavigationPosition, thumbnailNavigationPosition,
})); }));
}); });
Object.keys(mergedConfig.manifests || {}).forEach((manifestId) => {
this.store.dispatch(
actions.requestManifest(manifestId, mergedConfig.manifests[manifestId]),
);
});
} }
/** /**
......
...@@ -7,10 +7,11 @@ import ActionTypes from './action-types'; ...@@ -7,10 +7,11 @@ import ActionTypes from './action-types';
* @param {String} manifestId * @param {String} manifestId
* @memberof ActionCreators * @memberof ActionCreators
*/ */
export function requestManifest(manifestId) { export function requestManifest(manifestId, properties) {
return { return {
type: ActionTypes.REQUEST_MANIFEST, type: ActionTypes.REQUEST_MANIFEST,
manifestId, manifestId,
properties,
}; };
} }
...@@ -50,9 +51,10 @@ export function receiveManifestFailure(manifestId, error) { ...@@ -50,9 +51,10 @@ export function receiveManifestFailure(manifestId, error) {
* @param {String} manifestId * @param {String} manifestId
* @memberof ActionCreators * @memberof ActionCreators
*/ */
export function fetchManifest(manifestId) { export function fetchManifest(manifestId, properties) {
return ((dispatch) => { return ((dispatch) => {
dispatch(requestManifest(manifestId)); dispatch(requestManifest(manifestId, { ...properties, isFetching: true }));
return fetch(manifestId) return fetch(manifestId)
.then(response => response.json()) .then(response => response.json())
.then(json => dispatch(receiveManifest(manifestId, json))) .then(json => dispatch(receiveManifest(manifestId, json)))
......
...@@ -10,14 +10,16 @@ export const manifestsReducer = (state = {}, action) => { ...@@ -10,14 +10,16 @@ export const manifestsReducer = (state = {}, action) => {
return { return {
...state, ...state,
[action.manifestId]: { [action.manifestId]: {
...state[action.manifestId],
...action.properties,
id: action.manifestId, id: action.manifestId,
isFetching: true,
}, },
}; };
case ActionTypes.RECEIVE_MANIFEST: case ActionTypes.RECEIVE_MANIFEST:
return { return {
...state, ...state,
[action.manifestId]: { [action.manifestId]: {
...state[action.manifestId],
id: action.manifestId, id: action.manifestId,
manifestation: manifesto.create(action.manifestJson), manifestation: manifesto.create(action.manifestJson),
isFetching: false, isFetching: false,
...@@ -27,6 +29,7 @@ export const manifestsReducer = (state = {}, action) => { ...@@ -27,6 +29,7 @@ export const manifestsReducer = (state = {}, action) => {
return { return {
...state, ...state,
[action.manifestId]: { [action.manifestId]: {
...state[action.manifestId],
id: action.manifestId, id: action.manifestId,
error: action.error, error: action.error,
isFetching: false, isFetching: false,
......
...@@ -60,6 +60,10 @@ export function getManifestCanvases(manifest) { ...@@ -60,6 +60,10 @@ export function getManifestCanvases(manifest) {
return []; return [];
} }
if (!manifest.manifestation.getSequences || !manifest.manifestation.getSequences()[0]) {
return [];
}
return manifest.manifestation.getSequences()[0].getCanvases(); return manifest.manifestation.getSequences()[0].getCanvases();
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment