diff --git a/CHANGELOG b/CHANGELOG index 3f37553be..de007ea37 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,7 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Updated -- Display non-aggregated filter values [SCC-5125](https://newyorkpubliclibrary.atlassian.net/browse/SCC-5125) +- Displayed non-aggregated filter values [SCC-5125](https://newyorkpubliclibrary.atlassian.net/browse/SCC-5125) +- Updated series links in bib details [SCC-5195](https://newyorkpubliclibrary.atlassian.net/browse/SCC-5195) ## [1.8.2] 2026-04-09 diff --git a/__test__/fixtures/bibFixtures.ts b/__test__/fixtures/bibFixtures.ts index 7ee8dc1fb..752237925 100644 --- a/__test__/fixtures/bibFixtures.ts +++ b/__test__/fixtures/bibFixtures.ts @@ -1231,6 +1231,677 @@ export const bibWithItems = { }, } +export const bibWithSeries = { + resource: { + "@context": + "http://discovery-api-qa.nypl.org/api/v0.1/discovery/context_all.jsonld", + "@type": ["nypl:Item", "nypl:Resource"], + "@id": "res:b16470373", + buildingLocationIds: ["rc"], + carrierType: [ + { + "@id": "carriertypes:nc", + prefLabel: "volume", + }, + ], + contributorLiteral: ["Spada, Pietro, 1935-"], + contributorsDisplay: [ + { + display: "Spada, Pietro, 1935-, editor", + "@value": "Spada, Pietro, 1935-", + }, + ], + createdString: ["2006"], + createdYear: 2006, + creatorLiteral: ["Rossini, Gioacchino, 1792-1868"], + creatorsDisplay: [ + { + display: "Rossini, Gioacchino, 1792-1868", + "@value": "Rossini, Gioacchino, 1792-1868", + }, + ], + dateStartYear: 2006, + dateString: ["2006"], + dates: [ + { + range: { + lt: "2007", + gte: "2006", + }, + raw: "061108s2006 it uua i n lat cccm4a ", + tag: "s", + }, + ], + dimensions: ["31 cm."], + extent: ["1 score ([ii], 18 p.) ;"], + idOclc: ["75399987"], + identifier: [ + { + "@type": "bf:ShelfMark", + "@value": "JMG 06-1852", + }, + { + "@type": "nypl:Bnumber", + "@value": "16470373", + }, + { + "@type": "nypl:Oclc", + "@value": "75399987", + }, + { + "@type": "bf:Identifier", + "@value": "BS. 1607 Boccaccini & Spada", + }, + { + "@type": "bf:Identifier", + "@value": "(OCoLC)72521271", + }, + { + "@type": "bf:Identifier", + "@value": "(OCoLC)75399987", + }, + { + "@type": "bf:Identifier", + "@value": "(WaOLN)M060000006", + }, + ], + issuance: [ + { + "@id": "urn:biblevel:m", + prefLabel: "monograph/item", + }, + ], + itemAggregations: [ + { + "@type": "nypl:Aggregation", + "@id": "res:location", + id: "location", + field: "location", + values: [ + { + value: "loc:rcpm2", + count: 1, + label: "Offsite", + }, + ], + }, + { + "@type": "nypl:Aggregation", + "@id": "res:format", + id: "format", + field: "format", + values: [ + { + value: "Notated music", + count: 1, + label: "Notated music", + }, + ], + }, + { + "@type": "nypl:Aggregation", + "@id": "res:status", + id: "status", + field: "status", + values: [ + { + value: "status:a", + count: 1, + label: "Available", + }, + ], + }, + ], + items: [ + { + "@id": "res:i17270445", + "@type": ["bf:Item"], + accessMessage: [ + { + "@id": "accessMessage:2", + prefLabel: "Request in advance", + }, + ], + catalogItemType: [ + { + "@id": "catalogItemType:57", + prefLabel: "printed music limited circ MaRLI", + }, + ], + eddFulfillment: { + "@id": "fulfillment:recap-edd", + }, + eddRequestable: true, + formatLiteral: ["Notated music"], + holdingLocation: [ + { + "@id": "loc:rcpm2", + prefLabel: "Offsite", + }, + ], + idBarcode: ["33433073832424"], + identifier: [ + { + "@type": "bf:ShelfMark", + "@value": "JMG 06-1852", + }, + { + "@type": "bf:Barcode", + "@value": "33433073832424", + }, + ], + owner: [ + { + "@id": "orgs:1002", + prefLabel: + "New York Public Library for the Performing Arts, Dorothy and Lewis B. Cullman Center", + }, + ], + physFulfillment: { + "@id": "fulfillment:recap-offsite", + }, + physRequestable: true, + physicalLocation: ["JMG 06-1852"], + recapCustomerCode: ["NP"], + requestable: [true], + shelfMark: ["JMG 06-1852"], + specRequestable: false, + status: [ + { + "@id": "status:a", + prefLabel: "Available", + }, + ], + uri: "i17270445", + idNyplSourceId: { + "@type": "SierraNypl", + "@value": "17270445", + }, + }, + ], + language: [ + { + "@id": "lang:lat", + prefLabel: "Latin", + }, + ], + lccClassification: ["M2018.R83 C57"], + materialType: [ + { + "@id": "resourcetypes:not", + prefLabel: "Notated music", + }, + ], + mediaType: [ + { + "@id": "mediatypes:n", + prefLabel: "unmediated", + }, + ], + note: [ + { + noteType: "Note", + "@type": "bf:Note", + prefLabel: "For vocal soloists (TTB) with orchestra.", + }, + { + noteType: "Note", + "@type": "bf:Note", + prefLabel: + "Preface in Italian with English, French, and German translations.", + }, + { + noteType: "Language", + "@type": "bf:Note", + prefLabel: "Latin words.", + }, + ], + numCheckinCardItems: 0, + numElectronicResources: 0, + numItemDatesParsed: 0, + numItemVolumesParsed: 0, + numItemsMatched: 1, + numItemsTotal: 1, + nyplSource: ["sierra-nypl"], + parallelContributorLiteral: [null], + parallelContributorsDisplay: [], + parallelCreatorLiteral: [null], + parallelCreatorsDisplay: [], + parallelSeriesAddedEntry: [], + parallelSeriesAddedEntryDisplay: [], + parallelSubjectLiteral: [], + physicalDescription: ["1 score ([ii], 18 p.) ; 31 cm."], + placeOfPublication: ["Pavona di Albano Laziale, Roma"], + popularity: 1, + publicationStatement: [ + "Pavona di Albano Laziale, Roma : Boccaccini & Spada, c2006.", + ], + publisherLiteral: ["Boccaccini & Spada"], + series: ["Inediti e rarità rossiniane ;"], + seriesDisplay: [ + { + display: "Inediti e rarità rossiniane ; 12", + "@value": "Inediti e rarità rossiniane ;", + }, + ], + seriesUniformTitle: [ + "Rossini, Gioacchino 1792-1868. Works. Selections (Boccaccini & Spada editore) ;", + ], + seriesUniformTitleDisplay: [ + { + display: + "Rossini, Gioacchino 1792-1868. Works. Selections (Boccaccini & Spada editore) ; 12.", + "@value": + "Rossini, Gioacchino 1792-1868. Works. Selections (Boccaccini & Spada editore) ;", + }, + ], + shelfMark: ["JMG 06-1852"], + subjectLiteral: ["Sacred vocal trios with orchestra -- Scores."], + title: ["Christe : per 2 tenori, basso ed orchestra"], + titleDisplay: [ + "Christe : per 2 tenori, basso ed orchestra / Gioacchino Rossini ; a cura di Pietro Spada.", + ], + type: ["nypl:Item"], + updatedAt: 1776009782717, + uri: "b16470373", + updatedAtDate: "2026-04-12T16:03:02.717Z", + hasItemVolumes: false, + hasItemDates: false, + collection: [ + { + "@id": "pam", + prefLabel: "Music Division", + buildingLocationLabel: + "The New York Public Library for the Performing Arts (LPA)", + locationsPath: "locations/lpa/music-division", + }, + ], + format: [ + { + "@id": "c", + prefLabel: "Notated music", + }, + ], + electronicResources: [], + }, + annotatedMarc: { + id: "16470373", + nyplSource: "sierra-nypl", + fields: [ + { + label: "Author", + values: [ + { + content: "Rossini, Gioacchino, 1792-1868.", + source: { + ind1: "1", + ind2: " ", + content: null, + marcTag: "100", + fieldTag: "a", + subfields: [ + { + tag: "a", + content: "Rossini, Gioacchino,", + }, + { + tag: "d", + content: "1792-1868.", + }, + ], + }, + }, + ], + }, + { + label: "Title", + values: [ + { + content: + "Christe : per 2 tenori, basso ed orchestra / Gioacchino Rossini ; a cura di Pietro Spada.", + source: { + ind1: "1", + ind2: "0", + content: null, + marcTag: "245", + fieldTag: "t", + subfields: [ + { + tag: "a", + content: "Christe :", + }, + { + tag: "b", + content: "per 2 tenori, basso ed orchestra /", + }, + { + tag: "c", + content: "Gioacchino Rossini ; a cura di Pietro Spada.", + }, + ], + }, + }, + ], + }, + { + label: "Imprint", + values: [ + { + content: + "Pavona di Albano Laziale, Roma : Boccaccini & Spada, c2006.", + source: { + ind1: " ", + ind2: " ", + content: null, + marcTag: "260", + fieldTag: "p", + subfields: [ + { + tag: "a", + content: "Pavona di Albano Laziale, Roma :", + }, + { + tag: "b", + content: "Boccaccini & Spada,", + }, + { + tag: "c", + content: "c2006.", + }, + ], + }, + }, + ], + }, + { + label: "Format", + values: [ + { + content: "Partitura.", + source: { + ind1: " ", + ind2: " ", + content: null, + marcTag: "254", + fieldTag: "e", + subfields: [ + { + tag: "a", + content: "Partitura.", + }, + ], + }, + }, + ], + }, + { + label: "Description", + values: [ + { + content: "1 score ([ii], 18 p.) ; 31 cm.", + source: { + ind1: " ", + ind2: " ", + content: null, + marcTag: "300", + fieldTag: "r", + subfields: [ + { + tag: "a", + content: "1 score ([ii], 18 p.) ;", + }, + { + tag: "c", + content: "31 cm.", + }, + ], + }, + }, + ], + }, + { + label: "Playing Time", + values: [ + { + content: "000700", + source: { + ind1: " ", + ind2: " ", + content: null, + marcTag: "306", + fieldTag: "r", + subfields: [ + { + tag: "a", + content: "000700", + }, + ], + }, + }, + ], + }, + { + label: "Series", + values: [ + { + content: "Inediti e rarità rossiniane ; 12", + source: { + ind1: "1", + ind2: " ", + content: null, + marcTag: "490", + fieldTag: "s", + subfields: [ + { + tag: "a", + content: "Inediti e rarità rossiniane ;", + }, + { + tag: "v", + content: "12", + }, + ], + }, + }, + { + content: + "Rossini, Gioacchino 1792-1868. Works. Selections (Boccaccini & Spada editore) ; 12.", + source: { + ind1: " ", + ind2: "0", + content: null, + marcTag: "830", + fieldTag: "s", + subfields: [ + { + tag: "a", + content: "Rossini, Gioacchino", + }, + { + tag: "d", + content: "1792-1868.", + }, + { + tag: "t", + content: "Works.", + }, + { + tag: "k", + content: "Selections (Boccaccini & Spada editore) ;", + }, + { + tag: "v", + content: "12.", + }, + ], + }, + }, + ], + }, + { + label: "Note", + values: [ + { + content: "For vocal soloists (TTB) with orchestra.", + source: { + ind1: " ", + ind2: " ", + content: null, + marcTag: "500", + fieldTag: "n", + subfields: [ + { + tag: "a", + content: "For vocal soloists (TTB) with orchestra.", + }, + ], + }, + }, + { + content: + "Preface in Italian with English, French, and German translations.", + source: { + ind1: " ", + ind2: " ", + content: null, + marcTag: "500", + fieldTag: "n", + subfields: [ + { + tag: "a", + content: + "Preface in Italian with English, French, and German translations.", + }, + ], + }, + }, + ], + }, + { + label: "Language", + values: [ + { + content: "Latin words.", + source: { + ind1: " ", + ind2: " ", + content: null, + marcTag: "546", + fieldTag: "n", + subfields: [ + { + tag: "a", + content: "Latin words.", + }, + ], + }, + }, + ], + }, + { + label: "Subject", + values: [ + { + content: "Sacred vocal trios with orchestra -- Scores.", + source: { + ind1: " ", + ind2: "0", + content: null, + marcTag: "650", + fieldTag: "d", + subfields: [ + { + tag: "a", + content: "Sacred vocal trios with orchestra", + }, + { + tag: "v", + content: "Scores.", + }, + ], + }, + }, + ], + }, + { + label: "Added Author", + values: [ + { + content: "Spada, Pietro, 1935- Editor", + source: { + ind1: "1", + ind2: " ", + content: null, + marcTag: "700", + fieldTag: "b", + subfields: [ + { + tag: "a", + content: "Spada, Pietro,", + }, + { + tag: "d", + content: "1935-", + }, + { + tag: "4", + content: "edt", + }, + ], + }, + }, + ], + }, + { + label: "Publisher No.", + values: [ + { + content: "BS. 1607 Boccaccini & Spada", + source: { + ind1: "2", + ind2: "2", + content: null, + marcTag: "028", + fieldTag: "l", + subfields: [ + { + tag: "a", + content: "BS. 1607", + }, + { + tag: "b", + content: "Boccaccini & Spada", + }, + ], + }, + }, + ], + }, + { + label: "Research Call Number", + values: [ + { + content: "JMG 06-1852", + source: { + ind1: "8", + ind2: " ", + content: null, + marcTag: "852", + fieldTag: "q", + subfields: [ + { + tag: "h", + content: "JMG 06-1852", + }, + ], + }, + }, + ], + }, + ], + }, +} + export const bibNoItems = { resource: { "@context": @@ -8971,7 +9642,7 @@ export const noParallels = { publicationStatement: ["[Paris, France] : Gallimard, c2005."], publisherLiteral: ["Gallimard,"], series: ["Childhood"], - seriesStatement: ["Haute enfance"], + seriesDisplay: [{ display: "Childhood", "@value": "Childhood" }], shelfMark: ["JFC 06-438"], subjectLiteral: [ "Authors, French -- 20th century -- Biography.", @@ -12771,7 +13442,6 @@ export const bibWithSubjectHeadings = { popularity: 1, publicationStatement: ["[Paris, France] : Gallimard, c2005."], publisherLiteral: ["Gallimard"], - seriesStatement: ["Haute enfance"], shelfMark: ["JFC 06-438"], subjectLiteral: [ "Cortanze, Gérard de -- Childhood and youth.", diff --git a/src/components/BibPage/BibDetail.test.tsx b/src/components/BibPage/BibDetail.test.tsx index 190357f08..218a55e41 100644 --- a/src/components/BibPage/BibDetail.test.tsx +++ b/src/components/BibPage/BibDetail.test.tsx @@ -38,7 +38,6 @@ describe("BibDetail component", () => { expect(screen.getByText("French")).toBeInTheDocument() expect(screen.getByText("Series")).toBeInTheDocument() expect(screen.queryAllByText("Childhood")[0]).toBeInTheDocument() - expect(screen.getByText("Series statement")).toBeInTheDocument() expect(screen.queryAllByText(/Haute enfance/)[0]).toBeInTheDocument() }) it("merges annotated MARC and resource fields without label duplicates", () => { @@ -175,8 +174,8 @@ describe("BibDetail component", () => { render(, { wrapper: MemoryRouterProvider, }) - const seriesStatement = screen.getByText("Childhood") - expect(seriesStatement).toHaveAttribute( + const series = screen.getByText("Childhood") + expect(series).toHaveAttribute( "href", expect.stringContaining("/search?filters[series][0]=Childhood") ) diff --git a/src/components/BibPage/BibDetail.tsx b/src/components/BibPage/BibDetail.tsx index ffc4535db..f9d262bff 100644 --- a/src/components/BibPage/BibDetail.tsx +++ b/src/components/BibPage/BibDetail.tsx @@ -105,13 +105,13 @@ export const BrowseLinkDetailElement = ({ url: `/browse${ browseType === "subjects" ? "" : "/authors/" }?q=${encodeURIComponentWithPeriods( - urlInfo.urlLabel + urlInfo.urlText )}&search_scope=starts_with`, - urlLabel: `[${indexLinkLabel}]`, + urlText: `[${indexLinkLabel}]`, }, "internal", true, - `${indexLinkLabel} for "${urlInfo.urlLabel}"` + `${indexLinkLabel} for "${urlInfo.urlText}"` )} @@ -125,10 +125,12 @@ const LinkElement = ( isBold = false, ariaLabel?: string ) => { - return ( - <> + const { text, urlText, url: href } = url + + if (!text) { + return ( - {url.urlLabel} + {url.urlText} - {url.text && {url.text}} + ) + } + + const parts = text.split(urlText) + + return ( + <> + {parts.map((part, index) => ( + + {part} + {index < parts.length - 1 && ( + + {urlText} + + )} + + ))} ) } diff --git a/src/components/HoldPages/HoldRequestItemDetails.tsx b/src/components/HoldPages/HoldRequestItemDetails.tsx index 7463e1b50..1d7c9faaf 100644 --- a/src/components/HoldPages/HoldRequestItemDetails.tsx +++ b/src/components/HoldPages/HoldRequestItemDetails.tsx @@ -25,7 +25,7 @@ const HoldRequestItemDetails = ({ item }: HoldRequestItemDetailsProps) => { > {LinkedDetailElement({ label: "Title", - value: [{ url: `${PATHS.BIB}/${item.bibId}`, urlLabel: item.bibTitle }], + value: [{ url: `${PATHS.BIB}/${item.bibId}`, urlText: item.bibTitle }], link: "internal", })} diff --git a/src/models/BibDetails.ts b/src/models/BibDetails.ts index 476ba9af0..b65e838e7 100644 --- a/src/models/BibDetails.ts +++ b/src/models/BibDetails.ts @@ -7,7 +7,7 @@ import type { AnyBibDetail, MarcLinkedDetail, AnyMarcDetail, - ContributorEntry, + DisplayPackedEntry, } from "../types/bibDetailsTypes" import { convertToSentenceCase, @@ -78,39 +78,87 @@ export default class BibDetails { ? "creatorsDisplay" : "contributorsDisplay" - const mapDisplay = (arr?: ContributorEntry[]): BibDetailURL[] => + const mapDisplay = (arr?: DisplayPackedEntry[]): BibDetailURL[] => Array.isArray(arr) ? arr.map(({ display, "@value": name }) => { - const [, roles] = display.split(name) return { url: getContributorSearchURL(name), - urlLabel: name, - text: roles?.trim() || undefined, + urlText: name, + text: display, } }) : [] const displayValues = mapDisplay(this.bib[displayField]) - // Set of displayed names for deduping - const seen = new Set(displayValues.map((v) => v.urlLabel)) - // Literals (fallback) const literalValues: string[] = this.bib[literalField] || [] - const literals: BibDetailURL[] = literalValues - .filter((name) => !seen.has(name)) - .map((name) => ({ - url: getContributorSearchURL(name), - urlLabel: name, - })) + const literals: BibDetailURL[] = literalValues.map((name) => ({ + url: getContributorSearchURL(name), + urlText: name, + })) + + return displayValues?.length > 0 + ? { + label, + link: "internal", + value: displayValues, + } + : literals?.length > 0 + ? { + label, + link: "internal", + value: literals, + } + : null + } + + buildLinkedSeriesDetail(literalField): LinkedBibDetail | null { + let displayField = "seriesDisplay" + let label = "Series" + switch (literalField) { + case "seriesAddedEntry": + displayField = "seriesAddedEntryDisplay" + label = "Series added entry" + break + case "seriesUniformTitle": + displayField = "seriesUniformTitleDisplay" + label = "Series uniform title" + break + } + + const getSeriesSearchUrl = (name: string) => + `/search?filters[series][0]=${encodeURIComponentWithPeriods(name)}` - const combinedValues = [...displayValues, ...literals] + const displayData: DisplayPackedEntry[] = this.bib[displayField] || [] + const displayValues: BibDetailURL[] = displayData.map( + ({ display, "@value": name }) => { + return { + url: getSeriesSearchUrl(name), + urlText: name, + text: display, + } + } + ) + + // Literals (fallback) + const literalValues: string[] = this.bib[literalField] || [] + const literals: BibDetailURL[] = literalValues.map((name) => ({ + url: getSeriesSearchUrl(name), + urlText: name, + })) - return combinedValues?.length > 0 + return displayValues?.length > 0 ? { label, link: "internal", - value: combinedValues, + value: displayValues, + } + : literals?.length > 0 + ? { + label, + link: "internal", + value: literals, } : null } @@ -124,7 +172,7 @@ export default class BibDetails { if (label === "Connect to:") { const urlValues = values.map(({ label, content }) => ({ url: content, - urlLabel: label, + urlText: label, })) const detail = this.buildExternalLinkedDetail( "Connect to:", @@ -212,6 +260,8 @@ export default class BibDetails { "addedAuthorTitle", "placeOfPublication", "series", + "seriesAddedEntry", + "seriesUniformTitle", "uniformTitle", "subjectLiteral", "titleAlt", @@ -228,7 +278,8 @@ export default class BibDetails { { field: "summary", label: "Summary" }, { field: "donor", label: "Donor/sponsor" }, { field: "series", label: "Series" }, - { field: "seriesStatement", label: "Series statement" }, + { field: "seriesAddedEntry", label: "Series added entry" }, + { field: "seriesUniformTitle", label: "Series uniform title" }, { field: "uniformTitle", label: "Uniform title" }, { field: "titleAlt", label: "Alternative title" }, { field: "formerTitle", label: "Former title" }, @@ -259,10 +310,38 @@ export default class BibDetails { ) } + // Series uniform title values display under Series added entry + combineSeriesAddedEntries(resourceEndpointDetails: AnyBibDetail[]) { + const addedEntry = resourceEndpointDetails.find( + (d) => d.label === "Series added entry" + ) as LinkedBibDetail + const uniformTitle = resourceEndpointDetails.find( + (d) => d.label === "Series uniform title" + ) as LinkedBibDetail + + if (uniformTitle) { + if (addedEntry) { + addedEntry.value = [...addedEntry.value, ...uniformTitle.value] + return resourceEndpointDetails.filter( + (d) => d.label !== "Series uniform title" + ) + } + uniformTitle.label = "Series added entry" + } + + return resourceEndpointDetails + } + combineBibDetailsData( resourceEndpointDetails: AnyBibDetail[], annotatedMarcDetails: AnyMarcDetail[] ): AnyBibDetail[] { + // Merge Series added entry and Series uniform title fields + resourceEndpointDetails = this.combineSeriesAddedEntries( + resourceEndpointDetails + ) + + // Normalize and merge bib and annotated marc fields const normalizeValues = (val: any) => { if (!val) return [] if (Array.isArray(val)) { @@ -271,12 +350,12 @@ export default class BibDetails { .map((v) => typeof v === "string" ? v.trim() - : v?.content?.trim() || v?.urlLabel?.trim() + : v?.content?.trim() || v?.urlText?.trim() ) } if (typeof val === "string") return [val.trim()] if (val?.content) return [val.content.trim()] - return [val?.urlLabel?.trim()] + return [val?.urlText?.trim()] } const labelsSet = new Set(resourceEndpointDetails.map((d) => d.label)) @@ -358,14 +437,23 @@ export default class BibDetails { label: string field: string }): LinkedBibDetail { - const value = this.bib[fieldMapping.field] - if (!value?.length) return null if (fieldMapping.field === "contributorLiteral") { return this.buildLinkedContributorDetail( "contributorLiteral", "Additional authors" ) } + if ( + fieldMapping.field === "seriesAddedEntry" || + fieldMapping.field === "series" || + fieldMapping.field === "seriesUniformTitle" + ) { + return this.buildLinkedSeriesDetail(fieldMapping.field) + } + + const value = this.bib[fieldMapping.field] + if (!value?.length) return null + return { link: "internal", label: convertToSentenceCase(fieldMapping.label), @@ -381,7 +469,7 @@ export default class BibDetails { v )}` } - return { url: internalUrl, urlLabel: v } + return { url: internalUrl, urlText: v } }), } } @@ -513,7 +601,7 @@ export default class BibDetails { }) .map((sc) => ({ url: sc.url, - urlLabel: sc.label, + urlText: sc.label, })) return this.buildExternalLinkedDetail(convertToSentenceCase(label), values) } diff --git a/src/models/modelTests/BibDetails.test.ts b/src/models/modelTests/BibDetails.test.ts index 0f2c6bfbb..08d33a21e 100644 --- a/src/models/modelTests/BibDetails.test.ts +++ b/src/models/modelTests/BibDetails.test.ts @@ -9,6 +9,7 @@ import { princetonRecord, bibWithItems, physicalDescriptionBib, + bibWithSeries, } from "../../../__test__/fixtures/bibFixtures" import type { LinkedBibDetail } from "../../types/bibDetailsTypes" import BibDetailsModel from "../BibDetails" @@ -45,6 +46,12 @@ describe("Bib Details model", () => { bibWithSubjectHeadings.resource, bibWithSubjectHeadings.annotatedMarc ) + + const bibWithSeriesModel = new BibDetailsModel( + bibWithSeries.resource, + bibWithSeries.annotatedMarc + ) + describe("owner", () => { it("populates owner when owner is present", () => { const partnerBib = new BibDetailsModel(princetonRecord) @@ -157,7 +164,7 @@ describe("Bib Details model", () => { label: "Supplementary content", value: [ { - urlLabel: "Image", + urlText: "Image", url: "http://images.contentreserve.com/ImageType-100/0293-1/{C87D2BB9-0E13-4851-A9E2-547643F41A0E}Img100.jpg", }, ], @@ -170,7 +177,7 @@ describe("Bib Details model", () => { label: "Supplementary content", value: [ { - urlLabel: "Image", + urlText: "Image", url: "http://images.contentreserve.com/ImageType-100/0293-1/{C87D2BB9-0E13-4851-A9E2-547643F41A0E}Img100.jpg", }, ], @@ -203,7 +210,7 @@ describe("Bib Details model", () => { value: [ { url: "/search?filters[placeOfPublication][0]=Mansfield%2C%20Ohio", - urlLabel: "Mansfield, Ohio", + urlText: "Mansfield, Ohio", }, ], }, @@ -218,7 +225,7 @@ describe("Bib Details model", () => { value: [ { url: "/search?filters[donor][0]=Gift%20of%20the%20DeWitt%20Wallace%20Endowment%20Fund%2C%20named%20in%20honor%20of%20the%20founder%20of%20Reader's%20Digest", - urlLabel: + urlText: "Gift of the DeWitt Wallace Endowment Fund, named in honor of the founder of Reader's Digest", }, ], @@ -252,6 +259,22 @@ describe("Bib Details model", () => { expect(subjects.link).toBe("internal") expect(subjects.value[0].url).toContain("/browse/subjects/") }) + it("creates series fields and merges series uniform title into series added entry", () => { + const series = bibWithSeriesModel.bottomDetails.find( + (d) => d.label === "Series" + ) as LinkedBibDetail + expect(series.link).toBe("internal") + expect(series.value[0].url).toContain("search?filters[series][0]") + expect(series.value[0].urlText).toEqual("Inediti e rarità rossiniane ;") + expect(series.value[0].text).toEqual("Inediti e rarità rossiniane ; 12") + const seriesAddedEntry = bibWithSeriesModel.bottomDetails.find( + (d) => d.label === "Series added entry" + ) as LinkedBibDetail + // Value is from seriesUniformTitle + expect(seriesAddedEntry.value[0].urlText).toEqual( + "Rossini, Gioacchino 1792-1868. Works. Selections (Boccaccini & Spada editore) ;" + ) + }) }) describe("parallels", () => { diff --git a/src/types/bibDetailsTypes.ts b/src/types/bibDetailsTypes.ts index ac48a4b15..eb47555dc 100644 --- a/src/types/bibDetailsTypes.ts +++ b/src/types/bibDetailsTypes.ts @@ -38,8 +38,8 @@ export interface MarcLinkedDetail { export interface BibDetailURL { url: string - urlLabel?: string - // Unlinked text following the URL + urlText?: string + // Unlinked text surrounding (and including) the linked urlText text?: string } @@ -48,7 +48,7 @@ export interface FieldMapping { field: string } -export interface ContributorEntry { +export interface DisplayPackedEntry { display: string "@value": string }