diff --git a/package-lock.json b/package-lock.json index 2bc24fc7e..5e037cc94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,6 +45,7 @@ "react-toastify": "11.0.2", "react-topbar-progress-indicator": "4.1.1", "sass": "1.58.1", + "turndown": "7.2.0", "typescript": "5.7.2", "use-deep-compare": "1.2.1", "vite-plugin-istanbul": "4.0.1", @@ -5367,6 +5368,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@mixmark-io/domino": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", + "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==" + }, "node_modules/@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -22025,11 +22031,6 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/domino": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", - "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" - }, "node_modules/dompurify": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.3.tgz", @@ -41971,11 +41972,11 @@ } }, "node_modules/turndown": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", - "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.0.tgz", + "integrity": "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==", "dependencies": { - "domino": "^2.1.6" + "@mixmark-io/domino": "^2.2.0" } }, "node_modules/tweetnacl": { diff --git a/package.json b/package.json index 1e5ed0b88..8134ef003 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "react-toastify": "11.0.2", "react-topbar-progress-indicator": "4.1.1", "sass": "1.58.1", + "turndown": "7.2.0", "typescript": "5.7.2", "use-deep-compare": "1.2.1", "vite-plugin-istanbul": "4.0.1", diff --git a/src/dataset/domain/repositories/DatasetRepository.ts b/src/dataset/domain/repositories/DatasetRepository.ts index 7da556119..dde215f28 100644 --- a/src/dataset/domain/repositories/DatasetRepository.ts +++ b/src/dataset/domain/repositories/DatasetRepository.ts @@ -6,7 +6,12 @@ import { DatasetsWithCount } from '../models/DatasetsWithCount' import { VersionUpdateType } from '../models/VersionUpdateType' export interface DatasetRepository { - getByPersistentId: (persistentId: string, version?: string) => Promise + getByPersistentId: ( + persistentId: string, + version?: string, + requestedVersion?: string, + keepRawFields?: boolean + ) => Promise getLocks(persistentId: string): Promise getByPrivateUrlToken: (privateUrlToken: string) => Promise getVersionDiff: ( diff --git a/src/dataset/domain/useCases/getDatasetByPersistentId.ts b/src/dataset/domain/useCases/getDatasetByPersistentId.ts index 9f60aa247..c9d7a39a1 100644 --- a/src/dataset/domain/useCases/getDatasetByPersistentId.ts +++ b/src/dataset/domain/useCases/getDatasetByPersistentId.ts @@ -4,9 +4,13 @@ import { Dataset } from '../models/Dataset' export async function getDatasetByPersistentId( datasetRepository: DatasetRepository, persistentId: string, - version?: string + version?: string, + requestedVersion?: string, + keepRawFields?: boolean ): Promise { - return datasetRepository.getByPersistentId(persistentId, version).catch((error: Error) => { - throw new Error(error.message) - }) + return datasetRepository + .getByPersistentId(persistentId, version, requestedVersion, keepRawFields) + .catch((error: Error) => { + throw new Error(error.message) + }) } diff --git a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts index 91c1d5232..dcdd7c400 100644 --- a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts +++ b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts @@ -172,10 +172,11 @@ export class DatasetJSDataverseRepository implements DatasetRepository { getByPersistentId( persistentId: string, version: string = DatasetNonNumericVersion.LATEST_PUBLISHED, - requestedVersion?: string + requestedVersion?: string, + keepRawFields?: boolean ): Promise { return getDataset - .execute(persistentId, version, includeDeaccessioned) + .execute(persistentId, version, includeDeaccessioned, keepRawFields) .then((jsDataset) => this.fetchDatasetDetails(jsDataset, version)) .then((datasetDetails) => { return this.fetchDownloadSizes(persistentId, version).then((downloadSizes) => { @@ -223,7 +224,8 @@ export class DatasetJSDataverseRepository implements DatasetRepository { return this.getByPersistentId( persistentId, DatasetNonNumericVersion.LATEST_PUBLISHED, - (requestedVersion = version) + (requestedVersion = version), + keepRawFields ) }) } diff --git a/src/metadata-block-info/domain/models/MetadataBlockInfo.ts b/src/metadata-block-info/domain/models/MetadataBlockInfo.ts index 9ace52756..7fd97174b 100644 --- a/src/metadata-block-info/domain/models/MetadataBlockInfo.ts +++ b/src/metadata-block-info/domain/models/MetadataBlockInfo.ts @@ -101,7 +101,7 @@ export interface MetadataBlockInfoDisplayFormat { export type MetadataBlockInfoDisplayFormatFields = Record -export type MetadataFieldInfo = Pick +export type MetadataFieldInfo = Pick export const METADATA_FIELD_DISPLAY_FORMAT_PLACEHOLDER = '#VALUE' export const METADATA_FIELD_DISPLAY_FORMAT_NAME_PLACEHOLDER = '#NAME' diff --git a/src/metadata-block-info/infrastructure/mappers/JSMetadataBlockInfoMapper.ts b/src/metadata-block-info/infrastructure/mappers/JSMetadataBlockInfoMapper.ts index fa6a88aec..58357cadc 100644 --- a/src/metadata-block-info/infrastructure/mappers/JSMetadataBlockInfoMapper.ts +++ b/src/metadata-block-info/infrastructure/mappers/JSMetadataBlockInfoMapper.ts @@ -23,7 +23,7 @@ export class JSMetadataBlockInfoMapper { const fields: MetadataBlockInfoDisplayFormatFields = {} Object.entries(jsMetadataBlockInfoFields).forEach(([key, value]) => { - fields[key] = { displayFormat: this.toDisplayFormat(value.displayFormat) } + fields[key] = { displayFormat: this.toDisplayFormat(value.displayFormat), type: value.type } if (value.typeClass === 'compound' && value.childMetadataFields) { this.processCompoundFields(value.childMetadataFields, fields) @@ -38,7 +38,7 @@ export class JSMetadataBlockInfoMapper { result: MetadataBlockInfoDisplayFormatFields ): void { Object.entries(jsFields).forEach(([key, value]) => { - result[key] = { displayFormat: this.toDisplayFormat(value.displayFormat) } + result[key] = { displayFormat: this.toDisplayFormat(value.displayFormat), type: value.type } }) } diff --git a/src/sections/dataset/DatasetProvider.tsx b/src/sections/dataset/DatasetProvider.tsx index 0b37299b9..aff1ca1e0 100644 --- a/src/sections/dataset/DatasetProvider.tsx +++ b/src/sections/dataset/DatasetProvider.tsx @@ -26,7 +26,13 @@ export function DatasetProvider({ const getDataset = useDeepCompareCallback(() => { if (searchParams.persistentId) { - return getDatasetByPersistentId(repository, searchParams.persistentId, searchParams.version) + return getDatasetByPersistentId( + repository, + searchParams.persistentId, + searchParams.version, + undefined, + true + ) } if (searchParams.privateUrlToken) { return getDatasetByPrivateUrlToken(repository, searchParams.privateUrlToken) diff --git a/src/sections/dataset/dataset-metadata/dataset-metadata-fields/DatasetMetadataFieldValueFormatted.tsx b/src/sections/dataset/dataset-metadata/dataset-metadata-fields/DatasetMetadataFieldValueFormatted.tsx index 0029e2f41..79087751c 100644 --- a/src/sections/dataset/dataset-metadata/dataset-metadata-fields/DatasetMetadataFieldValueFormatted.tsx +++ b/src/sections/dataset/dataset-metadata/dataset-metadata-fields/DatasetMetadataFieldValueFormatted.tsx @@ -1,3 +1,4 @@ +import TurndownService from 'turndown' import { METADATA_FIELD_DISPLAY_FORMAT_NAME_PLACEHOLDER, METADATA_FIELD_DISPLAY_FORMAT_PLACEHOLDER, @@ -17,6 +18,13 @@ interface DatasetMetadataFieldValueFormattedProps { metadataFieldValue: DatasetMetadataFieldValueModel metadataBlockDisplayFormatInfo: MetadataBlockInfoDisplayFormat } + +const turndownService = new TurndownService() + +function transformHtmlToMarkdown(source: string): string { + return turndownService.turndown(source) +} + export function DatasetMetadataFieldValueFormatted({ metadataBlockName, metadataFieldName, @@ -36,6 +44,12 @@ export function DatasetMetadataFieldValueFormatted({ t(`${metadataBlockName}.datasetField.${metadataFieldName}.name`) ) + if (metadataBlockDisplayFormatInfo.fields[metadataFieldName]?.type === 'TEXTBOX') { + const markdownValue = transformHtmlToMarkdown(valueFormattedWithNamesTranslated) + + return + } + return } @@ -80,9 +94,18 @@ function joinSubFields( metadataBlockInfo?: MetadataBlockInfoDisplayFormat ): string { return Object.entries(metadataSubField) - .map(([subFieldName, subFieldValue]) => - formatSubFieldValue(subFieldValue, metadataBlockInfo?.fields[subFieldName]?.displayFormat) - ) + .map(([subFieldName, subFieldValue]) => { + let formattedSubFieldValue = formatSubFieldValue( + subFieldValue, + metadataBlockInfo?.fields[subFieldName]?.displayFormat + ) + const subFieldType = metadataBlockInfo?.fields[subFieldName]?.type as string | undefined + + if (subFieldType === 'TEXTBOX') + formattedSubFieldValue = transformHtmlToMarkdown(formattedSubFieldValue) + + return formattedSubFieldValue + }) .join(' ') } diff --git a/src/sections/shared/hierarchy/BreadcrumbsGenerator.tsx b/src/sections/shared/hierarchy/BreadcrumbsGenerator.tsx index e5309d1d8..47e251c36 100644 --- a/src/sections/shared/hierarchy/BreadcrumbsGenerator.tsx +++ b/src/sections/shared/hierarchy/BreadcrumbsGenerator.tsx @@ -45,7 +45,10 @@ export function BreadcrumbsGenerator({ {item.name} }}> + linkProps={{ + page: Route.COLLECTIONS_BASE, + children: <>{item.name} + }}> {item.name} ) diff --git a/tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother.ts b/tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother.ts index fed19e33e..f151fb629 100644 --- a/tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother.ts +++ b/tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother.ts @@ -17,25 +17,25 @@ export class MetadataBlockInfoMother { return { name: MetadataBlockName.CITATION, fields: { - alternativePersistentId: { displayFormat: '' }, - publicationDate: { displayFormat: '' }, - citationDate: { displayFormat: '' }, - title: { displayFormat: '' }, - subject: { displayFormat: ';' }, - author: { displayFormat: '' }, - authorName: { displayFormat: '#VALUE' }, - authorAffiliation: { displayFormat: '(#VALUE)' }, - authorIdentifierScheme: { displayFormat: '- #VALUE:' }, - authorIdentifier: { displayFormat: '[#VALUE](https://orcid.org/#VALUE)' }, - datasetContact: { displayFormat: '#VALUE' }, - datasetContactName: { displayFormat: '#VALUE' }, - datasetContactAffiliation: { displayFormat: '(#VALUE)' }, - datasetContactEmail: { displayFormat: '[#VALUE](mailto:#VALUE)' }, - dsDescription: { displayFormat: '' }, - dsDescriptionValue: { displayFormat: '#VALUE' }, - producerURL: { displayFormat: '[#VALUE](#VALUE)' }, - producerLogoURL: { displayFormat: '![#NAME](#VALUE)' }, - dateOfCollectionStart: { displayFormat: '#NAME: #VALUE ' } + alternativePersistentId: { displayFormat: '', type: 'TEXT' }, + publicationDate: { displayFormat: '', type: 'DATE' }, + citationDate: { displayFormat: '', type: 'DATE' }, + title: { displayFormat: '', type: 'TEXT' }, + subject: { displayFormat: ';', type: 'TEXT' }, + author: { displayFormat: '', type: 'NONE' }, + authorName: { displayFormat: '#VALUE', type: 'TEXT' }, + authorAffiliation: { displayFormat: '(#VALUE)', type: 'TEXT' }, + authorIdentifierScheme: { displayFormat: '- #VALUE:', type: 'TEXT' }, + authorIdentifier: { displayFormat: '[#VALUE](https://orcid.org/#VALUE)', type: 'TEXT' }, + datasetContact: { displayFormat: '#VALUE', type: 'NONE' }, + datasetContactName: { displayFormat: '#VALUE', type: 'TEXT' }, + datasetContactAffiliation: { displayFormat: '(#VALUE)', type: 'TEXT' }, + datasetContactEmail: { displayFormat: '[#VALUE](mailto:#VALUE)', type: 'EMAIL' }, + dsDescription: { displayFormat: '', type: 'NONE' }, + dsDescriptionValue: { displayFormat: '#VALUE', type: 'TEXTBOX' }, + producerURL: { displayFormat: '[#VALUE](#VALUE)', type: 'NONE' }, + producerLogoURL: { displayFormat: '![#NAME](#VALUE)', type: 'URL' }, + dateOfCollectionStart: { displayFormat: '#NAME: #VALUE ', type: 'NONE' } }, ...props } diff --git a/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx b/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx index 5b7649f5c..ec60b915e 100644 --- a/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx @@ -323,5 +323,6 @@ describe('DatasetMetadata', () => { ) cy.findAllByTestId('ds-metadata-block-display-format-error').should('exist') + cy.contains('Error getting metadata block display info').should('not.exist') }) })