diff --git a/__tests__/fixtures/version-2/related.json b/__tests__/fixtures/version-2/related.json new file mode 100644 index 0000000000..f87bf96636 --- /dev/null +++ b/__tests__/fixtures/version-2/related.json @@ -0,0 +1,16 @@ +{ + "@context": "http://iiif.io/api/presentation/2/context.json", + "@id": "http://example.com/iiif/manifest/related-urls.json", + "@type": "sc:Manifest", + "related": [ + "http://example.com/related1", + { + "@id": "http://example.com/related2" + }, + { + "@id": "http://example.com/related3", + "format": "text/html", + "label": "Something related" + } + ] +} diff --git a/__tests__/src/components/ManifestRelatedLinks.test.js b/__tests__/src/components/ManifestRelatedLinks.test.js index 234b5bab99..4bf7994f71 100644 --- a/__tests__/src/components/ManifestRelatedLinks.test.js +++ b/__tests__/src/components/ManifestRelatedLinks.test.js @@ -21,6 +21,16 @@ describe('ManifestRelatedLinks', () => { }, ]} manifestUrl="http://example.com/" + related={[ + { + value: 'http://example.com/related', + }, + { + format: 'video/ogg', + label: 'Video', + value: 'http://example.com/video', + }, + ]} renderings={[ { label: 'PDF Version', @@ -81,16 +91,42 @@ describe('ManifestRelatedLinks', () => { ).toBe(true); }); - it('renders manifest seeAlso information', () => { + it('renders related information', () => { expect( wrapper.find(Typography).at(5) .matchesElement( - iiif_seeAlso, + iiif_related, ), ).toBe(true); expect( wrapper.find(Typography).at(6) + .matchesElement( + http://example.com/related, + ), + ).toBe(true); + + expect( + wrapper.find(Typography).at(7) + .matchesElement( + + Video + (video/ogg) + , + ), + ).toBe(true); + }); + + it('renders manifest seeAlso information', () => { + expect( + wrapper.find(Typography).at(9) + .matchesElement( + iiif_seeAlso, + ), + ).toBe(true); + + expect( + wrapper.find(Typography).at(10) .matchesElement( A @@ -100,7 +136,7 @@ describe('ManifestRelatedLinks', () => { ).toBe(true); expect( - wrapper.find(Typography).at(8) + wrapper.find(Typography).at(12) .matchesElement( http://example.com/b, ), @@ -109,14 +145,14 @@ describe('ManifestRelatedLinks', () => { it('renders manifest links', () => { expect( - wrapper.find(Typography).at(9) + wrapper.find(Typography).at(13) .matchesElement( iiif_manifest, ), ).toBe(true); expect( - wrapper.find(Typography).at(10) + wrapper.find(Typography).at(14) .matchesElement( http://example.com/, ), diff --git a/__tests__/src/selectors/manifests.test.js b/__tests__/src/selectors/manifests.test.js index 3c174506f3..28b0461ad5 100644 --- a/__tests__/src/selectors/manifests.test.js +++ b/__tests__/src/selectors/manifests.test.js @@ -6,6 +6,7 @@ import manifestFixtureSn904cj3429 from '../../fixtures/version-2/sn904cj3429.jso import manifestFixturev3001 from '../../fixtures/version-3/001.json'; import manifestFixtureWithAProvider from '../../fixtures/version-3/with_a_provider.json'; import manifestFixtureFg165hz3589 from '../../fixtures/version-2/fg165hz3589.json'; +import manifestFixtureRelated from '../../fixtures/version-2/related.json'; import { getManifestoInstance, getManifestLocale, @@ -18,8 +19,10 @@ import { getManifestTitle, getManifestThumbnail, getManifestMetadata, + getManifestRelated, getManifestRelatedContent, getManifestRenderings, + getManifestSeeAlso, getManifestUrl, getMetadataLocales, getRequiredStatement, @@ -210,6 +213,33 @@ describe('getManifestRenderings', () => { }); }); +describe('getManifestRelated', () => { + it('should return manifest related', () => { + const state = { manifests: { x: { json: manifestFixtureRelated } } }; + const received = getManifestRelated(state, { manifestId: 'x' }); + expect(received).toEqual([ + { + value: 'http://example.com/related1', + }, + { + format: undefined, + label: null, + value: 'http://example.com/related2', + }, + { + format: 'text/html', + label: 'Something related', + value: 'http://example.com/related3', + }, + ]); + }); + + it('should return undefined if manifest undefined', () => { + const received = getManifestRelated({ manifests: {} }, { manifestId: 'x' }); + expect(received).toBeUndefined(); + }); +}); + describe('getManifestRelatedContent', () => { it('should return manifest seeAlso content', () => { const state = { manifests: { x: { json: manifestFixtureSn904cj3429 } } }; @@ -229,6 +259,25 @@ describe('getManifestRelatedContent', () => { }); }); +describe('getManifestSeeAlso', () => { + it('should return manifest seeAlso content', () => { + const state = { manifests: { x: { json: manifestFixtureSn904cj3429 } } }; + const received = getManifestSeeAlso(state, { manifestId: 'x' }); + expect(received).toEqual([ + { + format: 'application/mods+xml', + label: null, + value: 'https://purl.stanford.edu/sn904cj3429.mods', + }, + ]); + }); + + it('should return undefined if manifest undefined', () => { + const received = getManifestSeeAlso({ manifests: {} }, { manifestId: 'x' }); + expect(received).toBeUndefined(); + }); +}); + describe('getManifestUrl', () => { it('should return manifest url', () => { const state = { manifests: { x: { json: manifestFixtureWithAProvider } } }; diff --git a/src/components/ManifestRelatedLinks.js b/src/components/ManifestRelatedLinks.js index 533b4522d1..943d287ff3 100644 --- a/src/components/ManifestRelatedLinks.js +++ b/src/components/ManifestRelatedLinks.js @@ -20,6 +20,7 @@ export class ManifestRelatedLinks extends Component { classes, homepage, manifestUrl, + related, renderings, seeAlso, id, @@ -68,17 +69,34 @@ export class ManifestRelatedLinks extends Component { } )} + { related && ( + <> + {t('iiif_related')} + { + related.map(relatedItem => ( + + + {relatedItem.label || relatedItem.value} + + { relatedItem.format && ( + {` (${relatedItem.format})`} + )} + + )) + } + + )} { seeAlso && ( <> {t('iiif_seeAlso')} { - seeAlso.map(related => ( - - - {related.label || related.value} + seeAlso.map(seeAlsoItem => ( + + + {seeAlsoItem.label || seeAlsoItem.value} - { related.format && ( - {` (${related.format})`} + { seeAlsoItem.format && ( + {` (${seeAlsoItem.format})`} )} )) @@ -110,6 +128,11 @@ ManifestRelatedLinks.propTypes = { })), id: PropTypes.string.isRequired, manifestUrl: PropTypes.string, + related: PropTypes.arrayOf(PropTypes.shape({ + format: PropTypes.string, + label: PropTypes.string, + value: PropTypes.string, + })), renderings: PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.string, value: PropTypes.string, @@ -125,6 +148,7 @@ ManifestRelatedLinks.propTypes = { ManifestRelatedLinks.defaultProps = { homepage: null, manifestUrl: null, + related: null, renderings: null, seeAlso: null, t: key => key, diff --git a/src/containers/ManifestRelatedLinks.js b/src/containers/ManifestRelatedLinks.js index 005029f220..6bcc356778 100644 --- a/src/containers/ManifestRelatedLinks.js +++ b/src/containers/ManifestRelatedLinks.js @@ -5,8 +5,9 @@ import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { getManifestHomepage, - getManifestRelatedContent, + getManifestRelated, getManifestRenderings, + getManifestSeeAlso, getManifestUrl, } from '../state/selectors'; import { ManifestRelatedLinks } from '../components/ManifestRelatedLinks'; @@ -19,8 +20,9 @@ import { ManifestRelatedLinks } from '../components/ManifestRelatedLinks'; const mapStateToProps = (state, { id, windowId }) => ({ homepage: getManifestHomepage(state, { windowId }), manifestUrl: getManifestUrl(state, { windowId }), + related: getManifestRelated(state, { windowId }), renderings: getManifestRenderings(state, { windowId }), - seeAlso: getManifestRelatedContent(state, { windowId }), + seeAlso: getManifestSeeAlso(state, { windowId }), }); const styles = { diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index ebc6b43fcc..e732a7d2d2 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -59,6 +59,7 @@ "hideZoomControls": "Zoomsteuerung verbergen", "iiif_homepage": "Über diese Ressource", "iiif_manifest": "IIIF-Manifest", + "iiif_related": "Verwandtes", "iiif_renderings": "Alternative Formate", "iiif_seeAlso": "Siehe auch", "import" : "Importieren", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index b5102f3149..18878e9cbc 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -61,6 +61,7 @@ "hideZoomControls": "Hide zoom controls", "iiif_homepage": "About this resource", "iiif_manifest": "IIIF manifest", + "iiif_related": "Related", "iiif_renderings": "Alternate formats", "iiif_seeAlso": "See also", "import" : "Import", diff --git a/src/state/selectors/manifests.js b/src/state/selectors/manifests.js index b42eb27396..51e82c2851 100644 --- a/src/state/selectors/manifests.js +++ b/src/state/selectors/manifests.js @@ -153,13 +153,14 @@ export const getManifestRenderings = createSelector( /** * Return the IIIF v2/v3 seeAlso data from a manifest or null +* * @param {object} state * @param {object} props * @param {string} props.manifestId * @param {string} props.windowId * @return {String|null} */ -export const getManifestRelatedContent = createSelector( +export const getManifestSeeAlso = createSelector( [ getProperty('seeAlso'), getManifestLocale, @@ -175,6 +176,48 @@ export const getManifestRelatedContent = createSelector( )), ); +/** +* Return the IIIF v2/v3 seeAlso data from a manifest or null +* +* @param {object} state +* @param {object} props +* @param {string} props.manifestId +* @param {string} props.windowId +* @return {String|null} +* @deprecated This does not actually return the content of "related" and +* might be removed in a future version. +* @see getManifestSeeAlso +*/ +export const getManifestRelatedContent = getManifestSeeAlso; + +/** +* Return the IIIF v2 realated links manifest or null +* @param {object} state +* @param {object} props +* @param {string} props.manifestId +* @param {string} props.windowId +* @return {String|null} +*/ +export const getManifestRelated = createSelector( + [ + getProperty('related'), + getManifestLocale, + ], + (relatedLinks, locale) => relatedLinks + && asArray(relatedLinks).map(related => ( + typeof related === 'string' + ? { + value: related, + } + : { + format: related.format, + label: PropertyValue.parse(related.label, locale) + .getValue(), + value: related.id || related['@id'], + } + )), +); + /** * Return the IIIF requiredStatement (v3) or attribution (v2) data from a manifest or null * @param {object} state