From d551c99357bb53ba00d6b4afb4d144f2720c1724 Mon Sep 17 00:00:00 2001 From: Jessie Keck <jessie.keck@gmail.com> Date: Fri, 14 Jun 2019 10:13:02 -0700 Subject: [PATCH] Add the annotation resources label to the SearchHit component. --- __tests__/src/components/SearchHit.test.js | 14 ++++ __tests__/src/selectors/searches.test.js | 79 ++++++++++++++++++++++ src/components/SearchHit.js | 6 ++ src/containers/SearchHit.js | 15 ++-- src/state/selectors/searches.js | 26 +++++++ 5 files changed, 133 insertions(+), 7 deletions(-) diff --git a/__tests__/src/components/SearchHit.test.js b/__tests__/src/components/SearchHit.test.js index 3e4f62085..ba11e0b36 100644 --- a/__tests__/src/components/SearchHit.test.js +++ b/__tests__/src/components/SearchHit.test.js @@ -35,4 +35,18 @@ describe('SearchHit', () => { wrapper.find('WithStyles(ForwardRef(ListItem))').simulate('click'); expect(selectContentSearchAnnotation).toHaveBeenCalledWith('window', ['foo']); }); + + describe('Annotation Labels', () => { + it('renders the annotationLabel if present', () => { + const wrapper = createWrapper({ annotationLabel: 'The Anno Label' }); + + expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="subtitle2"][children="The Anno Label"]').length).toEqual(1); + }); + + it('does not render the typography if no annotation label is present', () => { + const wrapper = createWrapper(); + + expect(wrapper.find('WithStyles(ForwardRef(Typography))[variant="subtitle2"]').length).toEqual(0); + }); + }); }); diff --git a/__tests__/src/selectors/searches.test.js b/__tests__/src/selectors/searches.test.js index 33603b41e..e50ae15fd 100644 --- a/__tests__/src/selectors/searches.test.js +++ b/__tests__/src/selectors/searches.test.js @@ -5,6 +5,8 @@ import { getSelectedContentSearchAnnotationIds, getSelectedContentSearchAnnotations, getSearchAnnotationForCompanionWindow, + getResourceAnnotationForSearchHit, + getResourceAnnotationLabel, } from '../../../src/state/selectors'; describe('getSearchResultsForWindow', () => { @@ -186,3 +188,80 @@ describe('getSelectedContentSearchAnnotations', () => { ).toEqual([]); }); }); + +describe('getResourceAnnotationForSearchHit', () => { + const companionWindowId = 'cwid'; + const annoId = 'annoId2'; + it('returns the resource annotation connected to the hit by ID', () => { + const state = { + searches: { + a: { + [companionWindowId]: { + json: { + '@id': 'yolo', + resources: [{ '@id': annoId }], + }, + }, + }, + }, + }; + + expect( + getResourceAnnotationForSearchHit( + state, { annotationUri: annoId, companionWindowId, windowId: 'a' }, + ).resource['@id'], + ).toEqual(annoId); + }); +}); + +describe('getResourceAnnotationLabel', () => { + const companionWindowId = 'cwid'; + const annoId = 'annoId2'; + it('returns the label from a LanguageMap JSON object', () => { + const state = { + companionWindows: { + [companionWindowId]: { locale: 'en' }, + }, + searches: { + a: { + [companionWindowId]: { + json: { + '@id': 'yolo', + resources: [{ + '@id': annoId, + label: { '@language': 'en', '@value': 'The Annotation Label' }, + }], + }, + }, + }, + }, + }; + + expect( + getResourceAnnotationLabel( + state, { annotationUri: annoId, companionWindowId, windowId: 'a' }, + ), + ).toEqual(['The Annotation Label']); + }); + + it('returns an empty array if the annotation resource does not have a label (to be consistent w/ the return of LanguageMap.parse)', () => { + const state = { + companionWindows: { + [companionWindowId]: { locale: 'en' }, + }, + searches: { + a: { + [companionWindowId]: { + json: { '@id': 'yolo', resources: [{ '@id': annoId }] }, + }, + }, + }, + }; + + expect( + getResourceAnnotationLabel( + state, { annotationUri: annoId, companionWindowId, windowId: 'a' }, + ), + ).toEqual([]); + }); +}); diff --git a/src/components/SearchHit.js b/src/components/SearchHit.js index 8e81154b3..6fed57af1 100644 --- a/src/components/SearchHit.js +++ b/src/components/SearchHit.js @@ -31,6 +31,7 @@ export class SearchHit extends Component { render() { const { adjacent, + annotationLabel, canvasLabel, classes, hit, @@ -65,6 +66,9 @@ export class SearchHit extends Component { <Chip component="span" label={index + 1} className={classes.hitCounter} /> {canvasLabel} </Typography> + {annotationLabel && ( + <Typography variant="subtitle2">{annotationLabel}</Typography> + )} <SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.before} /> {' '} <strong> @@ -86,6 +90,7 @@ export class SearchHit extends Component { SearchHit.propTypes = { adjacent: PropTypes.bool, + annotationLabel: PropTypes.string, canvasLabel: PropTypes.string, classes: PropTypes.objectOf(PropTypes.string), focused: PropTypes.bool, @@ -105,6 +110,7 @@ SearchHit.propTypes = { SearchHit.defaultProps = { adjacent: false, + annotationLabel: undefined, canvasLabel: undefined, classes: {}, focused: false, diff --git a/src/containers/SearchHit.js b/src/containers/SearchHit.js index 3cb7abf00..12401c9dc 100644 --- a/src/containers/SearchHit.js +++ b/src/containers/SearchHit.js @@ -7,8 +7,9 @@ import { SearchHit } from '../components/SearchHit'; import * as actions from '../state/actions'; import { getCanvasLabel, - getSearchAnnotationForCompanionWindow, getSelectedCanvases, + getResourceAnnotationForSearchHit, + getResourceAnnotationLabel, getSelectedContentSearchAnnotationIds, } from '../state/selectors'; @@ -18,17 +19,17 @@ import { * @private */ const mapStateToProps = (state, { hit, companionWindowId, windowId }) => { - const annotation = getSearchAnnotationForCompanionWindow( - state, { companionWindowId, windowId }, + const hitAnnotation = getResourceAnnotationForSearchHit( + state, { annotationUri: hit.annotations[0], companionWindowId, windowId }, + ); + const annotationLabel = getResourceAnnotationLabel( + state, { annotationUri: hit.annotations[0], companionWindowId, windowId }, ); - - const resourceAnnotations = annotation.resources; - const hitAnnotation = resourceAnnotations.find(r => r.id === hit.annotations[0]); - const selectedCanvasIds = getSelectedCanvases(state, { windowId }).map(canvas => canvas.id); return { adjacent: selectedCanvasIds.includes(hitAnnotation.targetId), + annotationLabel: annotationLabel[0], canvasLabel: hitAnnotation && getCanvasLabel(state, { canvasId: hitAnnotation.targetId, windowId, diff --git a/src/state/selectors/searches.js b/src/state/selectors/searches.js index 165c1c52d..f57602517 100644 --- a/src/state/selectors/searches.js +++ b/src/state/selectors/searches.js @@ -1,6 +1,8 @@ import { createSelector } from 'reselect'; +import { LanguageMap } from 'manifesto.js'; import Annotation from '../../lib/Annotation'; import { getWindow } from './windows'; +import { getManifestLocale } from './manifests'; export const getSearchResultsForWindow = createSelector( [ @@ -83,3 +85,27 @@ export const getSelectedContentSearchAnnotations = createSelector( ), })).filter(val => val.resources.length > 0), ); + +export const getResourceAnnotationForSearchHit = createSelector( + [ + getSearchAnnotationForCompanionWindow, + (state, { annotationUri }) => annotationUri, + ], + (searchAnnotations, annotationUri) => searchAnnotations.resources.find( + r => r.id === annotationUri, + ), +); + +export const getResourceAnnotationLabel = createSelector( + [ + getResourceAnnotationForSearchHit, + getManifestLocale, + ], + (resourceAnnotation, locale) => { + if ( + !(resourceAnnotation && resourceAnnotation.resource && resourceAnnotation.resource.label) + ) return []; + + return LanguageMap.parse(resourceAnnotation.resource.label, locale).map(label => label.value); + }, +); -- GitLab