From 8f7b563dc0839c3f8f84aa88b2d5efe6094d2e71 Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Tue, 7 Jul 2026 15:46:20 -0400 Subject: [PATCH 01/15] change: filemetadata export metadata button hide while it's not the latest ver --- CHANGELOG.md | 2 + .../mappers/JSDatasetVersionMapper.ts | 5 ++- .../DatasetJSDataverseRepository.ts | 38 ++++++++++++++++ .../FileJSDataverseRepository.ts | 13 ++++-- .../infrastructure/mappers/JSFileMapper.ts | 7 ++- .../file/file-metadata/FileMetadata.tsx | 26 +++++------ .../file/file-metadata/FileMetadata.spec.tsx | 43 +++++++++++++++++++ 7 files changed, 115 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46e13f875..221028b82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel ### Changed +- Hide "Export Metadata" on file pages that are not for the latest released dataset version. + ### Fixed ### Removed diff --git a/src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts b/src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts index ab12ee12f..866fac78a 100644 --- a/src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts +++ b/src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts @@ -18,7 +18,8 @@ export class JSDatasetVersionMapper { jsDatasetLastUpdateTime: string, jsDatasetPublicationDate?: string, jsDatasettermsOfAccess?: TermsOfAccess, - jsDatasetDeaccessionedNote?: string + jsDatasetDeaccessionedNote?: string, + isLatest = true ): DatasetVersion { return new DatasetVersion.Builder( jDatasetVersionId, @@ -26,7 +27,7 @@ export class JSDatasetVersionMapper { this.toVersionNumber(jsDatasetVersionInfo), this.toStatus(jsDatasetVersionInfo.state), jsDatasetCitation, - true, // TODO Connect with dataset version isLatest + isLatest, false, // TODO Connect with dataset version isInReview this.toStatus(jsDatasetVersionInfo.state), this.toSomeDatasetVersionHasBeenReleased(jsDatasetVersionInfo, jsDatasetPublicationDate), diff --git a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts index 0d489c490..0d1463503 100644 --- a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts +++ b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts @@ -3,6 +3,7 @@ import { Dataset, DatasetLock, DatasetNonNumericVersion, + DatasetNonNumericVersionSearchParam, TermsOfAccess } from '../../domain/models/Dataset' import { DatasetVersionDiff } from '../../domain/models/DatasetVersionDiff' @@ -88,6 +89,43 @@ export class DatasetJSDataverseRepository implements DatasetRepository { return requireAppConfig().backendUrl } + static isLatestReleasedVersion( + datasetId: number | string, + datasetVersionNumber?: string + ): Promise { + if (datasetVersionNumber === undefined) { + return Promise.resolve(true) + } + + return getDatasetVersionsSummaries + .execute(datasetId, 2, 0) + .then((datasetVersionSummarySubset) => { + const latestReleasedDatasetVersion = datasetVersionSummarySubset.summaries.find( + (summary) => + summary.versionNumber !== DatasetNonNumericVersion.DRAFT && + summary.versionNumber !== DatasetNonNumericVersionSearchParam.DRAFT + )?.versionNumber + + return ( + latestReleasedDatasetVersion !== undefined && + latestReleasedDatasetVersion === + DatasetJSDataverseRepository.toVersionSummaryVersion(datasetVersionNumber) + ) + }) + .catch(() => false) + } + + private static toVersionSummaryVersion(datasetVersionNumber: string): string { + if ( + datasetVersionNumber === DatasetNonNumericVersion.DRAFT || + datasetVersionNumber === DatasetNonNumericVersionSearchParam.DRAFT + ) { + return DatasetNonNumericVersionSearchParam.DRAFT + } + + return datasetVersionNumber + } + getAllWithCount( collectionId: string, paginationInfo: DatasetPaginationInfo diff --git a/src/files/infrastructure/FileJSDataverseRepository.ts b/src/files/infrastructure/FileJSDataverseRepository.ts index d37d012db..7c2441131 100644 --- a/src/files/infrastructure/FileJSDataverseRepository.ts +++ b/src/files/infrastructure/FileJSDataverseRepository.ts @@ -31,6 +31,7 @@ import { FileCriteria } from '../domain/models/FileCriteria' import { DomainFileMapper } from './mappers/DomainFileMapper' import { JSFileMapper } from './mappers/JSFileMapper' import { DatasetVersion, DatasetVersionNumber } from '../../dataset/domain/models/Dataset' +import { DatasetJSDataverseRepository } from '@/dataset/infrastructure/repositories/DatasetJSDataverseRepository' import { File } from '../domain/models/File' import { FilePaginationInfo } from '../domain/models/FilePaginationInfo' import { requireAppConfig } from '../../config' @@ -293,7 +294,11 @@ export class FileJSDataverseRepository implements FileRepository { FileJSDataverseRepository.getDownloadCountById(jsFile.id, jsFile.publicationDate), Promise.resolve(resolvedPermissions), FileJSDataverseRepository.getThumbnailById(jsFile.id), - FileJSDataverseRepository.getTabularDataById(jsFile.id, jsFile.tabularData) + FileJSDataverseRepository.getTabularDataById(jsFile.id, jsFile.tabularData), + DatasetJSDataverseRepository.isLatestReleasedVersion( + jsDataset.id, + datasetVersionNumber + ) ]) }) }) @@ -306,7 +311,8 @@ export class FileJSDataverseRepository implements FileRepository { downloadsCount, permissions, thumbnail, - tabularData + tabularData, + isLatestDatasetVersion ]) => JSFileMapper.toFile( jsFile, @@ -316,7 +322,8 @@ export class FileJSDataverseRepository implements FileRepository { downloadsCount, permissions, thumbnail, - tabularData + tabularData, + isLatestDatasetVersion ) ) .catch((error: ReadError) => { diff --git a/src/files/infrastructure/mappers/JSFileMapper.ts b/src/files/infrastructure/mappers/JSFileMapper.ts index 0389525d4..36c8f776b 100644 --- a/src/files/infrastructure/mappers/JSFileMapper.ts +++ b/src/files/infrastructure/mappers/JSFileMapper.ts @@ -48,7 +48,8 @@ export class JSFileMapper { downloadsCount: number, permissions: FilePermissions, thumbnail?: string, - tabularData?: FileTabularData + tabularData?: FileTabularData, + isLatestDatasetVersion = true ): File { const datasetVersion = JSDatasetVersionMapper.toVersion( jsDataset.versionId, @@ -57,7 +58,9 @@ export class JSFileMapper { datasetCitation, jsDataset.versionInfo.lastUpdateTime, jsDataset.publicationDate, - jsDataset.termsOfUse?.termsOfAccess + jsDataset.termsOfUse?.termsOfAccess, + undefined, + isLatestDatasetVersion ) return { id: this.toFileId(jsFile.id), diff --git a/src/sections/file/file-metadata/FileMetadata.tsx b/src/sections/file/file-metadata/FileMetadata.tsx index e15a41e55..aea555e50 100644 --- a/src/sections/file/file-metadata/FileMetadata.tsx +++ b/src/sections/file/file-metadata/FileMetadata.tsx @@ -41,18 +41,20 @@ export function FileMetadata({ return ( <> -
- -
+ {datasetVersion.isLatest && ( +
+ +
+ )} {t('metadata.title')} diff --git a/tests/component/sections/file/file-metadata/FileMetadata.spec.tsx b/tests/component/sections/file/file-metadata/FileMetadata.spec.tsx index 8bb3775fe..c242af442 100644 --- a/tests/component/sections/file/file-metadata/FileMetadata.spec.tsx +++ b/tests/component/sections/file/file-metadata/FileMetadata.spec.tsx @@ -11,6 +11,8 @@ import { import { FilePermissionsMother } from '../../../files/domain/models/FilePermissionsMother' import { DataverseInfoMockRepository } from '@/stories/shared-mock-repositories/info/DataverseInfoMockRepository' import { requireAppConfig } from '@/config' +import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' +import { DatasetMetadataExportFormatsMother } from '@tests/component/info/domain/models/DatasetMetadataExportFormatsMother' const appConfig = requireAppConfig() @@ -31,6 +33,47 @@ describe('FileMetadata', () => { cy.findByRole('button', { name: 'File Metadata' }).should('exist') }) + it('renders export metadata when the file is on the latest dataset version', () => { + const dataverseInfoRepository = new DataverseInfoMockRepository() + cy.stub(dataverseInfoRepository, 'getAvailableDatasetMetadataExportFormats').resolves( + DatasetMetadataExportFormatsMother.create() + ) + + cy.customMount( + + ) + + cy.findByRole('button', { name: 'Export Metadata' }).should('exist') + }) + + it('does not render export metadata when the file is not on the latest dataset version', () => { + const dataverseInfoRepository = new DataverseInfoMockRepository() + const getAvailableDatasetMetadataExportFormats = cy + .stub(dataverseInfoRepository, 'getAvailableDatasetMetadataExportFormats') + .resolves(DatasetMetadataExportFormatsMother.create()) + + cy.customMount( + + ) + + cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') + cy.wrap(getAvailableDatasetMetadataExportFormats).should('not.have.been.called') + }) + it('renders the file preview', () => { cy.customMount( Date: Fri, 10 Jul 2026 14:46:16 -0400 Subject: [PATCH 02/15] feat: exportMetadata use case, and integration --- package.json | 2 +- public/locales/en/shared.json | 1 + public/locales/es/shared.json | 1 + .../domain/models/ExportedDatasetMetadata.ts | 4 + .../domain/repositories/DatasetRepository.ts | 7 + .../domain/useCases/exportDatasetMetadata.ts | 12 + .../mappers/JSDatasetVersionMapper.ts | 5 +- .../DatasetJSDataverseRepository.ts | 86 ++----- .../FileJSDataverseRepository.ts | 13 +- .../infrastructure/mappers/JSFileMapper.ts | 7 +- src/sections/dataset/Dataset.tsx | 3 - src/sections/dataset/DatasetHelper.ts | 65 +++++ .../dataset-metadata/DatasetMetadata.tsx | 14 +- .../ExportMetadataDropdown.tsx | 83 +++++-- .../file/file-metadata/FileMetadata.tsx | 23 +- .../dataset/DatasetErrorMockRepository.ts | 14 ++ src/stories/dataset/DatasetMockRepository.ts | 17 ++ .../DatasetMetadata.stories.tsx | 2 - .../file-metadata/FileMetadata.stories.tsx | 23 +- .../dataset-metadata/DatasetMetadata.spec.tsx | 94 +++---- .../ExportMetadataDropdown.spec.tsx | 232 +++++++++++++----- .../file/file-metadata/FileMetadata.spec.tsx | 144 +++++------ .../e2e/sections/dataset/Dataset.spec.tsx | 55 +++++ .../e2e/sections/file/File.spec.tsx | 66 +++++ 24 files changed, 614 insertions(+), 359 deletions(-) create mode 100644 src/dataset/domain/models/ExportedDatasetMetadata.ts create mode 100644 src/dataset/domain/useCases/exportDatasetMetadata.ts create mode 100644 src/sections/dataset/DatasetHelper.ts diff --git a/package.json b/package.json index 7d813db74..50ad1c755 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "@dnd-kit/sortable": "8.0.0", "@dnd-kit/utilities": "3.2.2", "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.2.0-alpha.10", + "@iqss/dataverse-client-javascript": "2.2.0-pr463.799c2e1", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", diff --git a/public/locales/en/shared.json b/public/locales/en/shared.json index 68f7c63b9..8e95eec04 100644 --- a/public/locales/en/shared.json +++ b/public/locales/en/shared.json @@ -32,6 +32,7 @@ "allowPopups": "You must enable popups in your browser to open external tools in a new window or tab.", "externalToolOpeningFailed": "There was a problem opening the external tool. Please try again.", "exportMetadata": "Export Metadata", + "exportMetadataError": "There was a problem exporting the dataset metadata. Please try again.", "pageNumberNotFound": { "heading": "Page Number Not Found", "message": "The page number you requested does not exist. Please try a different page number." diff --git a/public/locales/es/shared.json b/public/locales/es/shared.json index 2d0eab09e..4dd420367 100644 --- a/public/locales/es/shared.json +++ b/public/locales/es/shared.json @@ -32,6 +32,7 @@ "allowPopups": "Debes habilitar las ventanas emergentes en tu navegador para abrir herramientas externas en una nueva ventana o pestaña.", "externalToolOpeningFailed": "Hubo un problema al abrir la herramienta externa. Por favor, inténtalo de nuevo.", "exportMetadata": "Exportar metadatos", + "exportMetadataError": "Hubo un problema al exportar los metadatos del dataset. Por favor, inténtalo de nuevo.", "pageNumberNotFound": { "heading": "Número de página no encontrado", "message": "El número de página solicitado no existe. Intenta con un número diferente." diff --git a/src/dataset/domain/models/ExportedDatasetMetadata.ts b/src/dataset/domain/models/ExportedDatasetMetadata.ts new file mode 100644 index 000000000..e6d259e73 --- /dev/null +++ b/src/dataset/domain/models/ExportedDatasetMetadata.ts @@ -0,0 +1,4 @@ +export type ExportedDatasetMetadata = { + content: string + contentType: string +} diff --git a/src/dataset/domain/repositories/DatasetRepository.ts b/src/dataset/domain/repositories/DatasetRepository.ts index b156dcc1c..68bb15122 100644 --- a/src/dataset/domain/repositories/DatasetRepository.ts +++ b/src/dataset/domain/repositories/DatasetRepository.ts @@ -13,6 +13,8 @@ import { CollectionSummary } from '@/collection/domain/models/CollectionSummary' import { DatasetVersionPaginationInfo } from '../models/DatasetVersionPaginationInfo' import { DatasetUploadLimits } from '../models/DatasetUploadLimits' import { DatasetReview } from '../models/DatasetReview' +import { ExportedDatasetMetadata } from '../models/ExportedDatasetMetadata' +import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' export interface DatasetRepository { getByPersistentId: ( @@ -62,6 +64,11 @@ export interface DatasetRepository { version: string, format: CitationFormat ) => Promise + exportDatasetMetadata: ( + datasetId: string | number, + exporter: string, + version?: DatasetNotNumberedVersion.LATEST_PUBLISHED | DatasetNotNumberedVersion.DRAFT + ) => Promise updateTermsOfAccess: (datasetId: string | number, termsOfAccess: TermsOfAccess) => Promise updateDatasetLicense: ( datasetId: string | number, diff --git a/src/dataset/domain/useCases/exportDatasetMetadata.ts b/src/dataset/domain/useCases/exportDatasetMetadata.ts new file mode 100644 index 000000000..e07fbff5a --- /dev/null +++ b/src/dataset/domain/useCases/exportDatasetMetadata.ts @@ -0,0 +1,12 @@ +import { ExportedDatasetMetadata } from '../models/ExportedDatasetMetadata' +import { DatasetRepository } from '../repositories/DatasetRepository' +import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' + +export function exportDatasetMetadata( + datasetRepository: DatasetRepository, + datasetId: string | number, + exporter: string, + version?: DatasetNotNumberedVersion.LATEST_PUBLISHED | DatasetNotNumberedVersion.DRAFT +): Promise { + return datasetRepository.exportDatasetMetadata(datasetId, exporter, version) +} diff --git a/src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts b/src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts index 866fac78a..ab12ee12f 100644 --- a/src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts +++ b/src/dataset/infrastructure/mappers/JSDatasetVersionMapper.ts @@ -18,8 +18,7 @@ export class JSDatasetVersionMapper { jsDatasetLastUpdateTime: string, jsDatasetPublicationDate?: string, jsDatasettermsOfAccess?: TermsOfAccess, - jsDatasetDeaccessionedNote?: string, - isLatest = true + jsDatasetDeaccessionedNote?: string ): DatasetVersion { return new DatasetVersion.Builder( jDatasetVersionId, @@ -27,7 +26,7 @@ export class JSDatasetVersionMapper { this.toVersionNumber(jsDatasetVersionInfo), this.toStatus(jsDatasetVersionInfo.state), jsDatasetCitation, - isLatest, + true, // TODO Connect with dataset version isLatest false, // TODO Connect with dataset version isInReview this.toStatus(jsDatasetVersionInfo.state), this.toSomeDatasetVersionHasBeenReleased(jsDatasetVersionInfo, jsDatasetPublicationDate), diff --git a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts index 0d1463503..5d7e834d2 100644 --- a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts +++ b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts @@ -3,7 +3,6 @@ import { Dataset, DatasetLock, DatasetNonNumericVersion, - DatasetNonNumericVersionSearchParam, TermsOfAccess } from '../../domain/models/Dataset' import { DatasetVersionDiff } from '../../domain/models/DatasetVersionDiff' @@ -45,8 +44,11 @@ import { getDatasetLinkedCollections, updateTermsOfAccess, updateDatasetLicense, + getDatasetStorageDriver, getDatasetUploadLimits, - getDatasetReviews + getDatasetReviews, + exportDatasetMetadata, + DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' import { JSDatasetMapper } from '../mappers/JSDatasetMapper' import { DatasetPaginationInfo } from '../../domain/models/DatasetPaginationInfo' @@ -60,13 +62,11 @@ import { DatasetDownloadCount } from '@/dataset/domain/models/DatasetDownloadCou import { DatasetVersionPaginationInfo } from '@/dataset/domain/models/DatasetVersionPaginationInfo' import { FormattedCitation, CitationFormat } from '@/dataset/domain/models/DatasetCitation' import { DatasetLicenseUpdateRequest } from '../../domain/models/DatasetLicenseUpdateRequest' -import { axiosInstance } from '@/axiosInstance' -import { requireAppConfig } from '../../../config' -import { AxiosResponse } from 'axios' import { JSDataverseReadErrorHandler } from '@/shared/helpers/JSDataverseReadErrorHandler' import { CollectionSummary } from '@/collection/domain/models/CollectionSummary' import { DatasetUploadLimits } from '@/dataset/domain/models/DatasetUploadLimits' import { DatasetReview } from '@/dataset/domain/models/DatasetReview' +import { ExportedDatasetMetadata } from '@/dataset/domain/models/ExportedDatasetMetadata' const includeDeaccessioned = true @@ -85,47 +85,6 @@ interface IDatasetDetails { } export class DatasetJSDataverseRepository implements DatasetRepository { - static get DATAVERSE_BACKEND_URL(): string { - return requireAppConfig().backendUrl - } - - static isLatestReleasedVersion( - datasetId: number | string, - datasetVersionNumber?: string - ): Promise { - if (datasetVersionNumber === undefined) { - return Promise.resolve(true) - } - - return getDatasetVersionsSummaries - .execute(datasetId, 2, 0) - .then((datasetVersionSummarySubset) => { - const latestReleasedDatasetVersion = datasetVersionSummarySubset.summaries.find( - (summary) => - summary.versionNumber !== DatasetNonNumericVersion.DRAFT && - summary.versionNumber !== DatasetNonNumericVersionSearchParam.DRAFT - )?.versionNumber - - return ( - latestReleasedDatasetVersion !== undefined && - latestReleasedDatasetVersion === - DatasetJSDataverseRepository.toVersionSummaryVersion(datasetVersionNumber) - ) - }) - .catch(() => false) - } - - private static toVersionSummaryVersion(datasetVersionNumber: string): string { - if ( - datasetVersionNumber === DatasetNonNumericVersion.DRAFT || - datasetVersionNumber === DatasetNonNumericVersionSearchParam.DRAFT - ) { - return DatasetNonNumericVersionSearchParam.DRAFT - } - - return datasetVersionNumber - } - getAllWithCount( collectionId: string, paginationInfo: DatasetPaginationInfo @@ -455,6 +414,15 @@ export class DatasetJSDataverseRepository implements DatasetRepository { ): Promise { return getDatasetCitationInOtherFormats.execute(datasetId, version, format) } + + exportDatasetMetadata( + datasetId: string | number, + exporter: string, + version?: DatasetNotNumberedVersion.LATEST_PUBLISHED | DatasetNotNumberedVersion.DRAFT + ): Promise { + return exportDatasetMetadata.execute(datasetId, exporter, version) + } + getAvailableCategories(datasetId: string | number): Promise { return getDatasetAvailableCategories.execute(datasetId) } @@ -471,30 +439,10 @@ export class DatasetJSDataverseRepository implements DatasetRepository { return getDatasetLinkedCollections.execute(datasetId) } - /* - TODO: This is a temporary solution as this use case doesn't exist in js-dataverse yet and the API should also return the file store type rather than name only. - After https://github.com/IQSS/dataverse/issues/11695 is implemented, create a js-dataverse use case. - */ private async getFileStore(datasetId: number): Promise { - return axiosInstance - .get( - `${DatasetJSDataverseRepository.DATAVERSE_BACKEND_URL}/api/datasets/${datasetId}/storageDriver` - ) - .then( - ( - res: AxiosResponse<{ - data: { - name: string - label: string - type: string - directDownload: boolean - directUpload: boolean - } - }> - ) => { - return res.data.data.name - } - ) + return getDatasetStorageDriver + .execute(datasetId) + .then((storageDriver) => storageDriver.name) .catch(() => { return undefined }) diff --git a/src/files/infrastructure/FileJSDataverseRepository.ts b/src/files/infrastructure/FileJSDataverseRepository.ts index 7c2441131..d37d012db 100644 --- a/src/files/infrastructure/FileJSDataverseRepository.ts +++ b/src/files/infrastructure/FileJSDataverseRepository.ts @@ -31,7 +31,6 @@ import { FileCriteria } from '../domain/models/FileCriteria' import { DomainFileMapper } from './mappers/DomainFileMapper' import { JSFileMapper } from './mappers/JSFileMapper' import { DatasetVersion, DatasetVersionNumber } from '../../dataset/domain/models/Dataset' -import { DatasetJSDataverseRepository } from '@/dataset/infrastructure/repositories/DatasetJSDataverseRepository' import { File } from '../domain/models/File' import { FilePaginationInfo } from '../domain/models/FilePaginationInfo' import { requireAppConfig } from '../../config' @@ -294,11 +293,7 @@ export class FileJSDataverseRepository implements FileRepository { FileJSDataverseRepository.getDownloadCountById(jsFile.id, jsFile.publicationDate), Promise.resolve(resolvedPermissions), FileJSDataverseRepository.getThumbnailById(jsFile.id), - FileJSDataverseRepository.getTabularDataById(jsFile.id, jsFile.tabularData), - DatasetJSDataverseRepository.isLatestReleasedVersion( - jsDataset.id, - datasetVersionNumber - ) + FileJSDataverseRepository.getTabularDataById(jsFile.id, jsFile.tabularData) ]) }) }) @@ -311,8 +306,7 @@ export class FileJSDataverseRepository implements FileRepository { downloadsCount, permissions, thumbnail, - tabularData, - isLatestDatasetVersion + tabularData ]) => JSFileMapper.toFile( jsFile, @@ -322,8 +316,7 @@ export class FileJSDataverseRepository implements FileRepository { downloadsCount, permissions, thumbnail, - tabularData, - isLatestDatasetVersion + tabularData ) ) .catch((error: ReadError) => { diff --git a/src/files/infrastructure/mappers/JSFileMapper.ts b/src/files/infrastructure/mappers/JSFileMapper.ts index 36c8f776b..0389525d4 100644 --- a/src/files/infrastructure/mappers/JSFileMapper.ts +++ b/src/files/infrastructure/mappers/JSFileMapper.ts @@ -48,8 +48,7 @@ export class JSFileMapper { downloadsCount: number, permissions: FilePermissions, thumbnail?: string, - tabularData?: FileTabularData, - isLatestDatasetVersion = true + tabularData?: FileTabularData ): File { const datasetVersion = JSDatasetVersionMapper.toVersion( jsDataset.versionId, @@ -58,9 +57,7 @@ export class JSFileMapper { datasetCitation, jsDataset.versionInfo.lastUpdateTime, jsDataset.publicationDate, - jsDataset.termsOfUse?.termsOfAccess, - undefined, - isLatestDatasetVersion + jsDataset.termsOfUse?.termsOfAccess ) return { id: this.toFileId(jsFile.id), diff --git a/src/sections/dataset/Dataset.tsx b/src/sections/dataset/Dataset.tsx index 68d5dfaa4..dc8dbf3db 100644 --- a/src/sections/dataset/Dataset.tsx +++ b/src/sections/dataset/Dataset.tsx @@ -30,7 +30,6 @@ import { ContactRepository } from '@/contact/domain/repositories/ContactReposito import { DatasetMetrics } from './dataset-metrics/DatasetMetrics' import { DatasetPublishingStatus } from '@/dataset/domain/models/Dataset' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' -import { useAnonymized } from './anonymized/AnonymizedContext' import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import { DatasetReviews } from './dataset-reviews/DatasetReviews' @@ -63,7 +62,6 @@ export function Dataset({ const publishCompleted = useCheckPublishCompleted(publishInProgress, dataset, datasetRepository) const [activeTab, setActiveTab] = useState(tab) const termsTabRef = useRef(null) - const { anonymizedView } = useAnonymized() useUpdateDatasetAlerts({ dataset, publishInProgress @@ -195,7 +193,6 @@ export function Dataset({
diff --git a/src/sections/dataset/DatasetHelper.ts b/src/sections/dataset/DatasetHelper.ts new file mode 100644 index 000000000..b676f6d31 --- /dev/null +++ b/src/sections/dataset/DatasetHelper.ts @@ -0,0 +1,65 @@ +import { + DatasetNonNumericVersion, + DatasetNonNumericVersionSearchParam, + DatasetPublishingStatus, + DatasetVersion +} from '@/dataset/domain/models/Dataset' +import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' +import { getDatasetVersionsSummaries } from '@/dataset/domain/useCases/getDatasetVersionsSummaries' +import { + DatasetVersionSummaryInfo, + DatasetVersionSummaryStringValues +} from '@/dataset/domain/models/DatasetVersionSummaryInfo' +import { DatasetVersionPaginationInfo } from '@/dataset/domain/models/DatasetVersionPaginationInfo' + +export class DatasetHelper { + static async canExportMetadata( + datasetRepository: DatasetRepository, + datasetId: number | string, + datasetVersion: DatasetVersion, + canUpdateDataset: boolean + ): Promise { + if (datasetVersion.publishingStatus === DatasetPublishingStatus.DRAFT) { + return canUpdateDataset + } + + if (datasetVersion.publishingStatus !== DatasetPublishingStatus.RELEASED) { + return false + } + + return this.isLatestPublishedVersion( + datasetRepository, + datasetId, + datasetVersion.number.toString() + ) + } + + private static async isLatestPublishedVersion( + datasetRepository: DatasetRepository, + datasetId: number | string, + datasetVersionNumber: string + ): Promise { + try { + const versionSummaries = await getDatasetVersionsSummaries( + datasetRepository, + datasetId, + new DatasetVersionPaginationInfo(1, 10) + ) + const latestPublishedVersion = versionSummaries.summaries.find((summary) => + this.isPublishedVersionSummary(summary) + ) + + return latestPublishedVersion?.versionNumber === datasetVersionNumber + } catch { + return false + } + } + + private static isPublishedVersionSummary(summary: DatasetVersionSummaryInfo): boolean { + return ( + summary.versionNumber !== DatasetNonNumericVersion.DRAFT && + summary.versionNumber !== DatasetNonNumericVersionSearchParam.DRAFT && + summary.summary !== DatasetVersionSummaryStringValues.versionDeaccessioned + ) + } +} diff --git a/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx b/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx index a253804dc..e216c6683 100644 --- a/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx +++ b/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx @@ -1,9 +1,5 @@ import { Accordion } from '@iqss/dataverse-design-system' -import { - Dataset, - DatasetPublishingStatus, - MetadataBlockName -} from '../../../dataset/domain/models/Dataset' +import { Dataset, MetadataBlockName } from '../../../dataset/domain/models/Dataset' import { DatasetMetadataBlock } from './dataset-metadata-block/DatasetMetadataBlock' import { MetadataBlockInfoRepository } from '../../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' @@ -11,14 +7,12 @@ import { ExportMetadataDropdown } from './export-metadata-dropdown/ExportMetadat interface DatasetMetadataProps { dataset: Dataset - anonymizedView: boolean metadataBlockInfoRepository: MetadataBlockInfoRepository dataverseInfoRepository: DataverseInfoRepository } export function DatasetMetadata({ dataset, - anonymizedView, metadataBlockInfoRepository, dataverseInfoRepository }: DatasetMetadataProps) { @@ -27,11 +21,7 @@ export function DatasetMetadata({
diff --git a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx index 0078d8fdd..d1e576096 100644 --- a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx +++ b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx @@ -1,36 +1,56 @@ +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { BoxArrowUpRight } from 'react-bootstrap-icons' import { DropdownButton, DropdownButtonItem } from '@iqss/dataverse-design-system' +import { toast } from 'react-toastify' import { useGetAvailableDatasetMetadataExportFormats } from '@/info/domain/hooks/useGetAvailableDatasetMetadataExportFormats' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' -import { QueryParamKey } from '@/sections/Route.enum' -import { requireAppConfig } from '@/config' +import { exportDatasetMetadata } from '@/dataset/domain/useCases/exportDatasetMetadata' +import { DatasetPublishingStatus, DatasetVersion } from '@/dataset/domain/models/Dataset' +import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' +import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' +import { DatasetHelper } from '@/sections/dataset/DatasetHelper' interface ExportMetadataDropdownProps { datasetPersistentId: string - datasetIsReleased: boolean - datasetIsDeaccessioned: boolean + datasetVersion: DatasetVersion canUpdateDataset: boolean - anonymizedView: boolean dataverseInfoRepository: DataverseInfoRepository } export const ExportMetadataDropdown = ({ datasetPersistentId, - datasetIsReleased, - datasetIsDeaccessioned, + datasetVersion, canUpdateDataset, - anonymizedView, dataverseInfoRepository }: ExportMetadataDropdownProps) => { - const appConfig = requireAppConfig() - + const { datasetRepository } = useDatasetRepositories() const { t } = useTranslation('shared') + const [shouldRender, setShouldRender] = useState(false) const { datasetMetadataExportFormats, isLoadingExportFormats, errorGetExportFormats } = useGetAvailableDatasetMetadataExportFormats({ dataverseInfoRepository }) - const shouldRender = - datasetIsReleased && (!datasetIsDeaccessioned || canUpdateDataset) && !anonymizedView + const datasetIsDraft = datasetVersion.publishingStatus === DatasetPublishingStatus.DRAFT + + useEffect(() => { + let isMounted = true + setShouldRender(false) + + void DatasetHelper.canExportMetadata( + datasetRepository, + datasetPersistentId, + datasetVersion, + canUpdateDataset + ).then((canExportMetadata) => { + if (isMounted) { + setShouldRender(canExportMetadata) + } + }) + + return () => { + isMounted = false + } + }, [datasetRepository, datasetPersistentId, datasetVersion, canUpdateDataset]) if (!shouldRender) return null @@ -43,6 +63,35 @@ export const ExportMetadataDropdown = ({ return null } + const handleExportMetadata = async (exporter: string) => { + const newWindow = window.open('', '_blank') + + if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') { + return + } + + try { + newWindow.document.title = t('exportMetadata') + + const version = datasetIsDraft ? DatasetNotNumberedVersion.DRAFT : undefined + const metadata = await exportDatasetMetadata( + datasetRepository, + datasetPersistentId, + exporter, + version + ) + + const blob = new Blob([metadata.content], { type: metadata.contentType }) + const url = URL.createObjectURL(blob) + newWindow.location.href = url + + setTimeout(() => URL.revokeObjectURL(url), 1000) + } catch { + if (!newWindow.closed) newWindow.close() + toast.error(t('exportMetadataError')) + } + } + return ( { if (!exportFormat.isVisibleInUserInterface) return null - const href = `${appConfig.backendUrl}/api/datasets/export?exporter=${key}&${ - QueryParamKey.PERSISTENT_ID - }=${encodeURIComponent(datasetPersistentId)}` - return ( - + handleExportMetadata(key)} + key={key}> {exportFormat.displayName} ) diff --git a/src/sections/file/file-metadata/FileMetadata.tsx b/src/sections/file/file-metadata/FileMetadata.tsx index aea555e50..d39157586 100644 --- a/src/sections/file/file-metadata/FileMetadata.tsx +++ b/src/sections/file/file-metadata/FileMetadata.tsx @@ -12,7 +12,6 @@ import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInf import { ExportMetadataDropdown } from '@/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown' import { File } from '@/files/domain/models/File' import styles from './FileMetadata.module.scss' -import { DatasetPublishingStatus } from '@/dataset/domain/models/Dataset' interface FileMetadataProps { name: string @@ -41,20 +40,14 @@ export function FileMetadata({ return ( <> - {datasetVersion.isLatest && ( -
- -
- )} +
+ +
{t('metadata.title')} diff --git a/src/stories/dataset/DatasetErrorMockRepository.ts b/src/stories/dataset/DatasetErrorMockRepository.ts index 97adb1a3f..2170e5fa8 100644 --- a/src/stories/dataset/DatasetErrorMockRepository.ts +++ b/src/stories/dataset/DatasetErrorMockRepository.ts @@ -15,6 +15,8 @@ import { CollectionSummary } from '@/collection/domain/models/CollectionSummary' import { DatasetVersionPaginationInfo } from '@/dataset/domain/models/DatasetVersionPaginationInfo' import { DatasetUploadLimits } from '@/dataset/domain/models/DatasetUploadLimits' import { DatasetReview } from '@/dataset/domain/models/DatasetReview' +import { ExportedDatasetMetadata } from '@/dataset/domain/models/ExportedDatasetMetadata' +import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' export class DatasetErrorMockRepository implements DatasetMockRepository { getAllWithCount: ( @@ -156,6 +158,18 @@ export class DatasetErrorMockRepository implements DatasetMockRepository { }) } + exportDatasetMetadata( + _datasetId: string | number, + _exporter: string, + _version?: DatasetNotNumberedVersion.LATEST_PUBLISHED | DatasetNotNumberedVersion.DRAFT + ): Promise { + return new Promise((_resolve, reject) => { + setTimeout(() => { + reject('Error thrown from mock') + }, FakerHelper.loadingTimout()) + }) + } + updateTermsOfAccess(_datasetId: string | number, _termsOfAccess: TermsOfAccess): Promise { return new Promise((_resolve, reject) => { setTimeout(() => { diff --git a/src/stories/dataset/DatasetMockRepository.ts b/src/stories/dataset/DatasetMockRepository.ts index e194cff2b..d63b81934 100644 --- a/src/stories/dataset/DatasetMockRepository.ts +++ b/src/stories/dataset/DatasetMockRepository.ts @@ -21,6 +21,8 @@ import { CollectionSummaryMother } from '@tests/component/collection/domain/mode import { DatasetVersionPaginationInfo } from '@/dataset/domain/models/DatasetVersionPaginationInfo' import { DatasetUploadLimits } from '@/dataset/domain/models/DatasetUploadLimits' import { DatasetReview } from '@/dataset/domain/models/DatasetReview' +import { ExportedDatasetMetadata } from '@/dataset/domain/models/ExportedDatasetMetadata' +import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' export class DatasetMockRepository implements DatasetRepository { getAllWithCount: ( @@ -171,6 +173,21 @@ export class DatasetMockRepository implements DatasetRepository { }) } + exportDatasetMetadata( + _datasetId: string | number, + _exporter: string, + _version?: DatasetNotNumberedVersion.LATEST_PUBLISHED | DatasetNotNumberedVersion.DRAFT + ): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve({ + content: 'Exported dataset metadata content', + contentType: 'text/plain' + }) + }, FakerHelper.loadingTimout()) + }) + } + updateTermsOfAccess(_datasetId: string | number, _termsOfAccess: TermsOfAccess): Promise { return new Promise((resolve) => { setTimeout(() => { diff --git a/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx b/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx index 0e884e8a6..312cf5d5c 100644 --- a/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx +++ b/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx @@ -22,7 +22,6 @@ export const Default: Story = { render: () => ( @@ -34,7 +33,6 @@ export const AnonymizedView: Story = { render: () => ( diff --git a/src/stories/file/file-metadata/FileMetadata.stories.tsx b/src/stories/file/file-metadata/FileMetadata.stories.tsx index 3a48322b9..24d19ffcd 100644 --- a/src/stories/file/file-metadata/FileMetadata.stories.tsx +++ b/src/stories/file/file-metadata/FileMetadata.stories.tsx @@ -5,6 +5,9 @@ import { FileMetadataMother } from '../../../../tests/component/files/domain/mod import { FilePermissionsMother } from '../../../../tests/component/files/domain/models/FilePermissionsMother' import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' import { DataverseInfoMockRepository } from '@/stories/shared-mock-repositories/info/DataverseInfoMockRepository' +import { RepositoriesProvider } from '@/shared/contexts/repositories/RepositoriesProvider' +import { DatasetMockRepository } from '@/stories/dataset/DatasetMockRepository' +import { CollectionMockRepository } from '@/stories/collection/CollectionMockRepository' const meta: Meta = { title: 'Sections/File Page/FileMetadata', @@ -17,13 +20,17 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + + + ) } diff --git a/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx b/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx index 7ab8d1e3a..a7ce549f6 100644 --- a/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx @@ -1,3 +1,4 @@ +import { ComponentProps } from 'react' import { DatasetMother } from '../../../dataset/domain/models/DatasetMother' import { DatasetMetadata } from '../../../../../src/sections/dataset/dataset-metadata/DatasetMetadata' import { @@ -9,6 +10,8 @@ import { MetadataBlockInfoRepository } from '../../../../../src/metadata-block-i import { MetadataBlockInfoMother } from '../../../metadata-block-info/domain/models/MetadataBlockInfoMother' import { DatasetMetadataExportFormatsMother } from '@tests/component/info/domain/models/DatasetMetadataExportFormatsMother' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' +import { WithRepositories } from '@tests/component/WithRepositories' +import { DatasetMockRepository } from '@/stories/dataset/DatasetMockRepository' const mockDataset = DatasetMother.create({ metadataBlocks: [ @@ -209,29 +212,28 @@ describe('DatasetMetadata', () => { .resolves(mockDatasetMetadataExportFormats) }) - it('renders the metadata blocks sections titles correctly', () => { + const mountDatasetMetadata = (props: Partial> = {}) => { cy.customMount( - + + + ) + } + + it('renders the metadata blocks sections titles correctly', () => { + mountDatasetMetadata() cy.findByRole('button', { name: 'Citation Metadata' }).should('exist') cy.findByRole('button', { name: 'Geospatial Metadata' }).should('exist') }) it('renders the metadata blocks fields correctly', () => { - cy.customMount( - - ) + mountDatasetMetadata() cy.get('.accordion > :nth-child(1)').within(() => { cy.findByText(/Citation Metadata/i).should('exist') @@ -255,14 +257,7 @@ describe('DatasetMetadata', () => { }) it('renders the metadata blocks fields values correctly', () => { - cy.customMount( - - ) + mountDatasetMetadata() cy.get('.accordion > :nth-child(1)').within(() => { cy.findByText(/Citation Metadata/i).should('exist') @@ -333,28 +328,22 @@ describe('DatasetMetadata', () => { metadataBlockInfoRepository.getByName = cy.stub().resolves(metadataBlockInfoMock) cy.customMount( - - - + + + + + ) cy.findAllByText(ANONYMIZED_FIELD_VALUE).should('exist') }) it('shows a tip for dataset contact field', () => { - cy.customMount( - - ) + mountDatasetMetadata() cy.findByText('Use email button above to contact.').should('exist') }) @@ -364,14 +353,7 @@ describe('DatasetMetadata', () => { .stub() .rejects(new Error('Error getting metadata block display info')) - cy.customMount( - - ) + mountDatasetMetadata() cy.findAllByTestId('ds-metadata-block-display-format-error').should('exist') cy.contains('Error getting metadata block display info').should('not.exist') @@ -415,14 +397,7 @@ describe('DatasetMetadata', () => { } ] }) - cy.customMount( - - ) + mountDatasetMetadata({ dataset: mockDataset }) cy.findByRole('button', { name: 'Citation Metadata' }).should('exist') cy.findByRole('button', { name: 'Geospatial Metadata' }).should('not.exist') @@ -475,14 +450,7 @@ describe('DatasetMetadata', () => { ] }) - cy.customMount( - - ) + mountDatasetMetadata({ dataset: mockDatasetWithExtraFields }) cy.get('.accordion > :nth-child(1)').within(() => { cy.findByText(/Citation Metadata/i).should('exist') diff --git a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx index 1038b757f..81c2856bc 100644 --- a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx @@ -1,12 +1,21 @@ import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' import { ExportMetadataDropdown } from '@/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown' -import { QueryParamKey } from '@/sections/Route.enum' import { DatasetMetadataExportFormatsMother } from '@tests/component/info/domain/models/DatasetMetadataExportFormatsMother' -import { requireAppConfig } from '@/config' - -const appConfig = requireAppConfig() +import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' +import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' +import { WithRepositories } from '@tests/component/WithRepositories' +import { type ComponentProps } from 'react' +import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' +import { DatasetNonNumericVersion } from '@/dataset/domain/models/Dataset' const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository +const datasetRepository: DatasetRepository = {} as DatasetRepository +let openedWindow: { + closed: boolean + document: { title: string } + location: { href: string } + close: Cypress.Agent +} const mockDatasetMetadataExportFormats = DatasetMetadataExportFormatsMother.create({ foo: { @@ -24,93 +33,190 @@ describe('ExportMetadataDropdown', () => { dataverseInfoRepository.getAvailableDatasetMetadataExportFormats = cy .stub() .resolves(mockDatasetMetadataExportFormats) + datasetRepository.exportDatasetMetadata = cy.stub().as('exportDatasetMetadata').resolves({ + content: 'exported metadata', + contentType: 'application/xml' + }) + datasetRepository.getDatasetVersionsSummaries = cy + .stub() + .as('getDatasetVersionsSummaries') + .resolves({ + summaries: [{ id: 1, versionNumber: '1.0', contributors: 'Admin, Dataverse' }], + totalCount: 1 + }) + + cy.window().then((win) => { + cy.stub(win.URL, 'createObjectURL').as('createObjectURL').returns('blob:exported-metadata') + cy.stub(win.URL, 'revokeObjectURL').as('revokeObjectURL') + openedWindow = { + closed: false, + document: { title: '' }, + location: { href: '' }, + close: cy.stub().as('openedWindowClose') + } + cy.stub(win, 'open') + .as('windowOpen') + .returns(openedWindow as unknown as Window) + }) }) - it('should render export format options', () => { + const mountExportMetadataDropdown = ( + props: Partial> = {} + ) => { cy.customMount( - + + + ) + } + + it('should render export format options', () => { + mountExportMetadataDropdown() cy.findByRole('button', { name: 'Export Metadata' }).click() - Object.entries(mockDatasetMetadataExportFormats).forEach(([key, value]) => { + Object.entries(mockDatasetMetadataExportFormats).forEach(([, value]) => { value.isVisibleInUserInterface ? cy.findByText(value.displayName).should('exist') : cy.findByText(value.displayName).should('not.exist') - const href = `${appConfig.backendUrl}/api/datasets/export?exporter=${key}&${ - QueryParamKey.PERSISTENT_ID - }=${encodeURIComponent(testDatasetPersistentId)}` - if (value.isVisibleInUserInterface) { - cy.findByRole('link', { name: value.displayName }).should('have.attr', 'href', href) + cy.findByRole('button', { name: value.displayName }).should('exist') } }) }) - it('should not render if dataset is not released', () => { - cy.customMount( - + it('should render latest published metadata export for a guest user', () => { + mountExportMetadataDropdown({ canUpdateDataset: false }) + + cy.findByRole('button', { name: 'Export Metadata' }).should('exist') + }) + + it('should render latest published metadata export for an admin or owner when a draft exists', () => { + datasetRepository.getDatasetVersionsSummaries = cy + .stub() + .as('getDatasetVersionsSummaries') + .resolves({ + summaries: [ + { + id: 2, + versionNumber: DatasetNonNumericVersion.DRAFT, + contributors: 'Admin, Dataverse' + }, + { id: 1, versionNumber: '1.0', contributors: 'Admin, Dataverse' } + ], + totalCount: 2 + }) + + mountExportMetadataDropdown({ + datasetVersion: DatasetVersionMother.createReleasedWithLatestVersionIsADraft(), + canUpdateDataset: true + }) + + cy.findByRole('button', { name: 'Export Metadata' }).should('exist') + }) + + it('should export metadata using the dataset repository', () => { + mountExportMetadataDropdown() + + cy.findByRole('button', { name: 'Export Metadata' }).click() + cy.findByRole('button', { name: 'OAI_ORE' }).click() + + cy.get('@exportDatasetMetadata').should( + 'have.been.calledWith', + testDatasetPersistentId, + 'OAI_ORE' ) + cy.then(() => { + expect(openedWindow.location.href).to.equal('blob:exported-metadata') + }) + cy.get('@windowOpen').should('have.been.calledWith', '', '_blank') + cy.get('@createObjectURL').should('have.been.called') + cy.get('@revokeObjectURL').should('have.been.calledWith', 'blob:exported-metadata') + }) - cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') + it('should show an error and close the new tab when exporting metadata fails', () => { + datasetRepository.exportDatasetMetadata = cy + .stub() + .as('exportDatasetMetadata') + .rejects(new Error('Export failed')) + + mountExportMetadataDropdown() + + cy.findByRole('button', { name: 'Export Metadata' }).click() + cy.findByRole('button', { name: 'OAI_ORE' }).click() + + cy.get('@exportDatasetMetadata').should( + 'have.been.calledWith', + testDatasetPersistentId, + 'OAI_ORE' + ) + cy.get('@windowOpen').should('have.been.calledWith', '', '_blank') + cy.get('@openedWindowClose').should('have.been.called') + cy.findByText('There was a problem exporting the dataset metadata. Please try again.').should( + 'exist' + ) }) - it('should not render if dataset is deaccessioned and user cannot update dataset', () => { - cy.customMount( - + it('should render and export draft metadata when dataset version is draft', () => { + mountExportMetadataDropdown({ + datasetVersion: DatasetVersionMother.createDraft(), + canUpdateDataset: true + }) + + cy.findByRole('button', { name: 'Export Metadata' }).click() + cy.findByRole('button', { name: 'OAI_ORE' }).click() + + cy.get('@exportDatasetMetadata').should( + 'have.been.calledWith', + testDatasetPersistentId, + 'OAI_ORE', + DatasetNotNumberedVersion.DRAFT ) + }) + + it('should not render if published dataset version is not latest', () => { + datasetRepository.getDatasetVersionsSummaries = cy + .stub() + .as('getDatasetVersionsSummaries') + .resolves({ + summaries: [{ id: 2, versionNumber: '2.0', contributors: 'Admin, Dataverse' }], + totalCount: 1 + }) + + mountExportMetadataDropdown() cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') }) - it('should render if dataset is deaccessioned and user can update dataset', () => { - cy.customMount( - - ) + it('should not render if dataset version is draft and user cannot update dataset', () => { + mountExportMetadataDropdown({ + datasetVersion: DatasetVersionMother.createDraft(), + canUpdateDataset: false + }) - cy.findByRole('button', { name: 'Export Metadata' }).should('exist') + cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') }) - it('should not render if anonymized view', () => { - cy.customMount( - - ) + it('should not render if dataset is deaccessioned and user cannot update dataset', () => { + mountExportMetadataDropdown({ + datasetVersion: DatasetVersionMother.createDeaccessioned() + }) + + cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') + }) + + it('should not render if dataset is deaccessioned and user can update dataset', () => { + mountExportMetadataDropdown({ + datasetVersion: DatasetVersionMother.createDeaccessioned(), + canUpdateDataset: true + }) cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') }) diff --git a/tests/component/sections/file/file-metadata/FileMetadata.spec.tsx b/tests/component/sections/file/file-metadata/FileMetadata.spec.tsx index c242af442..aee90b8a0 100644 --- a/tests/component/sections/file/file-metadata/FileMetadata.spec.tsx +++ b/tests/component/sections/file/file-metadata/FileMetadata.spec.tsx @@ -1,4 +1,4 @@ -import { FileMetadata } from '../../../../../src/sections/file/file-metadata/FileMetadata' +import { FileMetadata as FileMetadataComponent } from '../../../../../src/sections/file/file-metadata/FileMetadata' import { FileMother } from '../../../files/domain/models/FileMother' import { FileSizeUnit } from '../../../../../src/files/domain/models/FileMetadata' import { @@ -11,14 +11,21 @@ import { import { FilePermissionsMother } from '../../../files/domain/models/FilePermissionsMother' import { DataverseInfoMockRepository } from '@/stories/shared-mock-repositories/info/DataverseInfoMockRepository' import { requireAppConfig } from '@/config' -import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' -import { DatasetMetadataExportFormatsMother } from '@tests/component/info/domain/models/DatasetMetadataExportFormatsMother' +import { WithRepositories } from '@tests/component/WithRepositories' +import { DatasetMockRepository } from '@/stories/dataset/DatasetMockRepository' +import { type ComponentProps } from 'react' const appConfig = requireAppConfig() const file = FileMother.create() +const FileMetadata = (props: ComponentProps) => ( + + + +) + describe('FileMetadata', () => { - it('renders the File Metadata tab', () => { + const mountFileMetadata = (props: Partial> = {}) => { cy.customMount( { datasetPersistentId={file.datasetPersistentId} datasetVersion={file.datasetVersion} dataverseInfoRepository={new DataverseInfoMockRepository()} + {...props} /> ) + } + it('renders the file metadata', () => { + mountFileMetadata() cy.findByRole('button', { name: 'File Metadata' }).should('exist') }) - it('renders export metadata when the file is on the latest dataset version', () => { - const dataverseInfoRepository = new DataverseInfoMockRepository() - cy.stub(dataverseInfoRepository, 'getAvailableDatasetMetadataExportFormats').resolves( - DatasetMetadataExportFormatsMother.create() - ) - - cy.customMount( - - ) - - cy.findByRole('button', { name: 'Export Metadata' }).should('exist') - }) - - it('does not render export metadata when the file is not on the latest dataset version', () => { - const dataverseInfoRepository = new DataverseInfoMockRepository() - const getAvailableDatasetMetadataExportFormats = cy - .stub(dataverseInfoRepository, 'getAvailableDatasetMetadataExportFormats') - .resolves(DatasetMetadataExportFormatsMother.create()) - - cy.customMount( - - ) - - cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') - cy.wrap(getAvailableDatasetMetadataExportFormats).should('not.have.been.called') - }) - it('renders the file preview', () => { - cy.customMount( - - ) - + mountFileMetadata() cy.findByText('Preview').should('exist') cy.findByRole('img').should('exist') }) @@ -93,14 +53,16 @@ describe('FileMetadata', () => { it('renders the file labels', () => { const metadataWithLabels = FileMetadataMother.createWithLabelsRealistic() cy.customMount( - + + + ) cy.findByText('File Tags').should('exist') @@ -111,14 +73,16 @@ describe('FileMetadata', () => { it('does not render the file labels when there are no labels', () => { const metadataWithoutLabels = FileMetadataMother.createWithNoLabels() cy.customMount( - + + + ) cy.findByText('File Tags').should('not.exist') @@ -129,14 +93,16 @@ describe('FileMetadata', () => { persistentId: 'doi:10.5072/FK2/ABC123' }) cy.customMount( - + + + ) cy.findByText('File Persistent ID').should('exist') @@ -146,14 +112,16 @@ describe('FileMetadata', () => { it('does not render the file persistent id when there is no persistent id', () => { const metadataWithoutPersistentId = FileMetadataMother.createWithNoPersistentId() cy.customMount( - + + + ) cy.findByText('File Persistent ID').should('not.exist') diff --git a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx index 57db44c7d..019e13624 100644 --- a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx +++ b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx @@ -21,6 +21,24 @@ type Dataset = { } const DRAFT_PARAM = DatasetNonNumericVersionSearchParam.DRAFT +const visitDatasetMetadataTabAndAssertExportMetadata = ( + persistentId: string, + shouldExist: boolean, + version?: string +) => { + const searchParams = new URLSearchParams({ + persistentId, + tab: 'metadata' + }) + + if (version) { + searchParams.set('version', version) + } + + cy.visit(`${FRONTEND_BASE_PATH}/datasets?${searchParams.toString()}`) + cy.findByRole('button', { name: 'Export Metadata' }).should(shouldExist ? 'exist' : 'not.exist') +} + describe('Dataset', () => { beforeEach(() => { TestsUtils.login().then((token) => { @@ -185,6 +203,43 @@ describe('Dataset', () => { }) }) + it('shows export metadata on the dataset page for admin draft, admin latest published, and guest latest published views', () => { + cy.wrap(DatasetHelper.create()).then((draftDataset) => { + visitDatasetMetadataTabAndAssertExportMetadata(draftDataset.persistentId, true, DRAFT_PARAM) + }) + + cy.wrap(DatasetHelper.createAndPublish()) + .its('persistentId') + .then((persistentId: string) => { + visitDatasetMetadataTabAndAssertExportMetadata(persistentId, true) + + TestsUtils.logout() + visitDatasetMetadataTabAndAssertExportMetadata(persistentId, true) + }) + }) + + it('hides export metadata on the dataset page for older published versions', () => { + cy.wrap(DatasetHelper.createWithFileAndPublish(FileHelper.create()), { timeout: 6000 }) + .then((dataset) => { + if (!dataset.file) { + throw new Error('Expected created dataset to include a file') + } + + return cy.wrap( + FileHelper.addLabel(dataset.file.id, []).then(async () => { + await DatasetHelper.publish(dataset.persistentId) + return dataset.persistentId + }) + ) + }) + .then((persistentId) => { + visitDatasetMetadataTabAndAssertExportMetadata(persistentId, false, '1.0') + + TestsUtils.logout() + visitDatasetMetadataTabAndAssertExportMetadata(persistentId, false, '1.0') + }) + }) + it('shows dataset reviews for a dataset with a published local review dataset', () => { const timestamp = new Date().valueOf() const collectionAlias = `dataset-reviews-${timestamp}` diff --git a/tests/e2e-integration/e2e/sections/file/File.spec.tsx b/tests/e2e-integration/e2e/sections/file/File.spec.tsx index 1c965c7a4..4feb4044c 100644 --- a/tests/e2e-integration/e2e/sections/file/File.spec.tsx +++ b/tests/e2e-integration/e2e/sections/file/File.spec.tsx @@ -6,6 +6,23 @@ import { FileHelper } from '../../../shared/files/FileHelper' import { GuestbookHelper } from '../../../shared/guestbooks/GuestbookHelper' import { faker } from '@faker-js/faker' +const visitFileMetadataTabAndAssertExportMetadata = ( + fileId: number, + shouldExist: boolean, + datasetVersion?: string +) => { + const searchParams = new URLSearchParams({ + id: fileId.toString() + }) + + if (datasetVersion) { + searchParams.set('datasetVersion', datasetVersion) + } + + cy.visit(`${FRONTEND_BASE_PATH}/files?${searchParams.toString()}`) + cy.findByRole('button', { name: 'Export Metadata' }).should(shouldExist ? 'exist' : 'not.exist') +} + describe('File', () => { beforeEach(() => { TestsUtils.login().then((token) => { @@ -60,6 +77,55 @@ describe('File', () => { ) }) + it('shows export metadata on the file page for admin draft, admin latest published, and guest latest published views', () => { + cy.wrap(DatasetHelper.createWithFile(FileHelper.create())).then((draftDataset) => { + if (!draftDataset.file) { + throw new Error('Expected created dataset to include a file') + } + + visitFileMetadataTabAndAssertExportMetadata(draftDataset.file.id, true) + }) + + cy.wrap(DatasetHelper.createWithFileAndPublish(FileHelper.create()), { timeout: 6000 }).then( + (publishedDataset) => { + if (!publishedDataset.file) { + throw new Error('Expected created dataset to include a file') + } + + visitFileMetadataTabAndAssertExportMetadata(publishedDataset.file.id, true) + + TestsUtils.logout() + visitFileMetadataTabAndAssertExportMetadata(publishedDataset.file.id, true) + } + ) + }) + + it('hides export metadata on the file page for older published versions', () => { + cy.wrap(DatasetHelper.createWithFileAndPublish(FileHelper.create()), { timeout: 6000 }) + .then((dataset) => { + if (!dataset.file) { + throw new Error('Expected created dataset to include a file') + } + + return cy.wrap( + FileHelper.addLabel(dataset.file.id, []).then(async () => { + await DatasetHelper.publish(dataset.persistentId) + return dataset.file?.id + }) + ) + }) + .then((fileId) => { + if (!fileId) { + throw new Error('Expected created dataset to include a file') + } + + visitFileMetadataTabAndAssertExportMetadata(fileId, false, '1.0') + + TestsUtils.logout() + visitFileMetadataTabAndAssertExportMetadata(fileId, false, '1.0') + }) + }) + it('loads version summaries when clicking on the version tab', () => { cy.wrap( DatasetHelper.createWithFileAndPublish(FileHelper.create()).then( From e67eae9b2370c09613fe53a30cfe9d933498e9bf Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Fri, 10 Jul 2026 15:49:48 -0400 Subject: [PATCH 03/15] fix: error lint --- .../guestbook/GuestbookMockRepository.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/stories/shared-mock-repositories/guestbook/GuestbookMockRepository.ts b/src/stories/shared-mock-repositories/guestbook/GuestbookMockRepository.ts index 076a3155e..94dc82127 100644 --- a/src/stories/shared-mock-repositories/guestbook/GuestbookMockRepository.ts +++ b/src/stories/shared-mock-repositories/guestbook/GuestbookMockRepository.ts @@ -1,4 +1,3 @@ -import { type Guestbook as JSDataverseGuestbook } from '@iqss/dataverse-client-javascript' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { Guestbook } from '@/guestbooks/domain/models/Guestbook' @@ -23,28 +22,13 @@ export const storybookGuestbook: Guestbook = { dataverseId: 1 } -export const storybookClientGuestbooks: JSDataverseGuestbook[] = [ - { - id: storybookGuestbook.id, - name: storybookGuestbook.name, - enabled: storybookGuestbook.enabled, - nameRequired: storybookGuestbook.nameRequired, - emailRequired: storybookGuestbook.emailRequired, - institutionRequired: storybookGuestbook.institutionRequired, - positionRequired: storybookGuestbook.positionRequired, - createTime: storybookGuestbook.createTime, - dataverseId: storybookGuestbook.dataverseId, - customQuestions: storybookGuestbook.customQuestions - } -] - export class GuestbookMockRepository implements GuestbookRepository { getGuestbook(_guestbookId: number): Promise { return Promise.resolve(storybookGuestbook) } getGuestbooksByCollectionId(_collectionIdOrAlias: number | string): Promise { - return Promise.resolve(storybookClientGuestbooks as Guestbook[]) + return Promise.resolve([storybookGuestbook]) } assignDatasetGuestbook(_datasetId: number | string, _guestbookId: number): Promise { From a7868d4b50f26eca2a3218ba8e4ed49404f9a1fa Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Fri, 10 Jul 2026 17:42:42 -0400 Subject: [PATCH 04/15] fix: chromatic error --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 34a5bd68e..c1b0c9e25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@dnd-kit/sortable": "8.0.0", "@dnd-kit/utilities": "3.2.2", "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.2.0-alpha.10", + "@iqss/dataverse-client-javascript": "2.2.0-pr463.799c2e1", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", @@ -3285,9 +3285,9 @@ } }, "node_modules/@iqss/dataverse-client-javascript": { - "version": "2.2.0-alpha.10", - "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.2.0-alpha.10/1fcacf53d38b344ea8502ad68aaa69c9eeb66348", - "integrity": "sha512-qQx0dLZHEeR4FJh0J4eb7D5sXE+S+PpXB1u8AiySFN4FKXu0wSf50bGEJbmyTqmmvtwfrjuUB2YKl1mCB1rZrg==", + "version": "2.2.0-pr463.799c2e1", + "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.2.0-pr463.799c2e1/0adde52ebb8906bcb2e7ea4766a8bc3eb0c3a8eb", + "integrity": "sha512-8hYrvHyv8gO2DZiJIvtc5GTdTezwM8nStCfiG+Sa/M6Us5EswxesbylaF+w3LstQN1mPQL6sJafW5sHjx3Mg6A==", "license": "MIT", "dependencies": { "@types/node": "^18.15.11", From 0ad19688acfbbf49235058007ccac4ca468db28d Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Mon, 13 Jul 2026 09:41:21 -0400 Subject: [PATCH 05/15] feat: anoymizedview --- src/sections/dataset/Dataset.tsx | 3 +++ .../dataset-metadata/DatasetMetadata.tsx | 3 +++ .../ExportMetadataDropdown.tsx | 10 +++++++++- .../file/file-metadata/FileMetadata.tsx | 19 +++++++++++-------- .../DatasetMetadata.stories.tsx | 2 ++ .../dataset-metadata/DatasetMetadata.spec.tsx | 2 ++ .../ExportMetadataDropdown.spec.tsx | 8 ++++++++ 7 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/sections/dataset/Dataset.tsx b/src/sections/dataset/Dataset.tsx index dc8dbf3db..68d5dfaa4 100644 --- a/src/sections/dataset/Dataset.tsx +++ b/src/sections/dataset/Dataset.tsx @@ -30,6 +30,7 @@ import { ContactRepository } from '@/contact/domain/repositories/ContactReposito import { DatasetMetrics } from './dataset-metrics/DatasetMetrics' import { DatasetPublishingStatus } from '@/dataset/domain/models/Dataset' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' +import { useAnonymized } from './anonymized/AnonymizedContext' import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import { DatasetReviews } from './dataset-reviews/DatasetReviews' @@ -62,6 +63,7 @@ export function Dataset({ const publishCompleted = useCheckPublishCompleted(publishInProgress, dataset, datasetRepository) const [activeTab, setActiveTab] = useState(tab) const termsTabRef = useRef(null) + const { anonymizedView } = useAnonymized() useUpdateDatasetAlerts({ dataset, publishInProgress @@ -193,6 +195,7 @@ export function Dataset({
diff --git a/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx b/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx index e216c6683..2c016e65c 100644 --- a/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx +++ b/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx @@ -7,12 +7,14 @@ import { ExportMetadataDropdown } from './export-metadata-dropdown/ExportMetadat interface DatasetMetadataProps { dataset: Dataset + anonymizedView: boolean metadataBlockInfoRepository: MetadataBlockInfoRepository dataverseInfoRepository: DataverseInfoRepository } export function DatasetMetadata({ dataset, + anonymizedView, metadataBlockInfoRepository, dataverseInfoRepository }: DatasetMetadataProps) { @@ -23,6 +25,7 @@ export function DatasetMetadata({ datasetPersistentId={dataset.persistentId} datasetVersion={dataset.version} canUpdateDataset={dataset.permissions?.canUpdateDataset} + anonymizedView={anonymizedView} dataverseInfoRepository={dataverseInfoRepository} />
diff --git a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx index d1e576096..8aba465e0 100644 --- a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx +++ b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx @@ -15,6 +15,7 @@ interface ExportMetadataDropdownProps { datasetPersistentId: string datasetVersion: DatasetVersion canUpdateDataset: boolean + anonymizedView: boolean dataverseInfoRepository: DataverseInfoRepository } @@ -22,6 +23,7 @@ export const ExportMetadataDropdown = ({ datasetPersistentId, datasetVersion, canUpdateDataset, + anonymizedView, dataverseInfoRepository }: ExportMetadataDropdownProps) => { const { datasetRepository } = useDatasetRepositories() @@ -36,6 +38,12 @@ export const ExportMetadataDropdown = ({ let isMounted = true setShouldRender(false) + if (anonymizedView) { + return () => { + isMounted = false + } + } + void DatasetHelper.canExportMetadata( datasetRepository, datasetPersistentId, @@ -50,7 +58,7 @@ export const ExportMetadataDropdown = ({ return () => { isMounted = false } - }, [datasetRepository, datasetPersistentId, datasetVersion, canUpdateDataset]) + }, [datasetRepository, datasetPersistentId, datasetVersion, canUpdateDataset, anonymizedView]) if (!shouldRender) return null diff --git a/src/sections/file/file-metadata/FileMetadata.tsx b/src/sections/file/file-metadata/FileMetadata.tsx index d39157586..7e2c079f2 100644 --- a/src/sections/file/file-metadata/FileMetadata.tsx +++ b/src/sections/file/file-metadata/FileMetadata.tsx @@ -40,14 +40,17 @@ export function FileMetadata({ return ( <> -
- -
+ {datasetVersion.isLatest && ( +
+ +
+ )} {t('metadata.title')} diff --git a/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx b/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx index 312cf5d5c..b1cced0a2 100644 --- a/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx +++ b/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx @@ -22,6 +22,7 @@ export const Default: Story = { render: () => ( @@ -33,6 +34,7 @@ export const AnonymizedView: Story = { render: () => ( diff --git a/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx b/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx index a7ce549f6..cb3199ef9 100644 --- a/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/DatasetMetadata.spec.tsx @@ -217,6 +217,7 @@ describe('DatasetMetadata', () => { { diff --git a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx index 81c2856bc..abeb0e1cf 100644 --- a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx @@ -69,6 +69,7 @@ describe('ExportMetadataDropdown', () => { datasetPersistentId={testDatasetPersistentId} datasetVersion={DatasetVersionMother.createReleased()} canUpdateDataset={false} + anonymizedView={false} dataverseInfoRepository={dataverseInfoRepository} {...props} /> @@ -98,6 +99,13 @@ describe('ExportMetadataDropdown', () => { cy.findByRole('button', { name: 'Export Metadata' }).should('exist') }) + it('should not render in anonymized view', () => { + mountExportMetadataDropdown({ anonymizedView: true }) + + cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') + cy.get('@getDatasetVersionsSummaries').should('not.have.been.called') + }) + it('should render latest published metadata export for an admin or owner when a draft exists', () => { datasetRepository.getDatasetVersionsSummaries = cy .stub() From a34947037b36ba2b6daf6600e8b00efe1af56cad Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Mon, 13 Jul 2026 10:19:55 -0400 Subject: [PATCH 06/15] fix: use ExportMetadata --- .../ExportMetadataDropdown.tsx | 42 +---- .../useExportMetadata.ts | 62 +++++++ .../useExportMetadata.spec.tsx | 160 ++++++++++++++++++ 3 files changed, 229 insertions(+), 35 deletions(-) create mode 100644 src/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.ts create mode 100644 tests/component/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.spec.tsx diff --git a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx index 8aba465e0..6ea6b79d6 100644 --- a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx +++ b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx @@ -2,14 +2,12 @@ import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { BoxArrowUpRight } from 'react-bootstrap-icons' import { DropdownButton, DropdownButtonItem } from '@iqss/dataverse-design-system' -import { toast } from 'react-toastify' import { useGetAvailableDatasetMetadataExportFormats } from '@/info/domain/hooks/useGetAvailableDatasetMetadataExportFormats' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' -import { exportDatasetMetadata } from '@/dataset/domain/useCases/exportDatasetMetadata' -import { DatasetPublishingStatus, DatasetVersion } from '@/dataset/domain/models/Dataset' -import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' +import { DatasetVersion } from '@/dataset/domain/models/Dataset' import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import { DatasetHelper } from '@/sections/dataset/DatasetHelper' +import { useExportMetadata } from '@/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata' interface ExportMetadataDropdownProps { datasetPersistentId: string @@ -31,8 +29,11 @@ export const ExportMetadataDropdown = ({ const [shouldRender, setShouldRender] = useState(false) const { datasetMetadataExportFormats, isLoadingExportFormats, errorGetExportFormats } = useGetAvailableDatasetMetadataExportFormats({ dataverseInfoRepository }) - - const datasetIsDraft = datasetVersion.publishingStatus === DatasetPublishingStatus.DRAFT + const { handleExportMetadata } = useExportMetadata({ + datasetRepository, + datasetPersistentId, + datasetVersion + }) useEffect(() => { let isMounted = true @@ -71,35 +72,6 @@ export const ExportMetadataDropdown = ({ return null } - const handleExportMetadata = async (exporter: string) => { - const newWindow = window.open('', '_blank') - - if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') { - return - } - - try { - newWindow.document.title = t('exportMetadata') - - const version = datasetIsDraft ? DatasetNotNumberedVersion.DRAFT : undefined - const metadata = await exportDatasetMetadata( - datasetRepository, - datasetPersistentId, - exporter, - version - ) - - const blob = new Blob([metadata.content], { type: metadata.contentType }) - const url = URL.createObjectURL(blob) - newWindow.location.href = url - - setTimeout(() => URL.revokeObjectURL(url), 1000) - } catch { - if (!newWindow.closed) newWindow.close() - toast.error(t('exportMetadataError')) - } - } - return ( Promise +} + +export const useExportMetadata = ({ + datasetRepository, + datasetPersistentId, + datasetVersion +}: UseExportMetadataParams): UseExportMetadataReturn => { + const { t } = useTranslation('shared') + + const handleExportMetadata = useCallback( + async (exporter: string) => { + const newWindow = window.open('', '_blank') + + if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') { + return + } + + try { + newWindow.document.title = t('exportMetadata') + + const version = + datasetVersion.publishingStatus === DatasetPublishingStatus.DRAFT + ? DatasetNotNumberedVersion.DRAFT + : undefined + const metadata = await exportDatasetMetadata( + datasetRepository, + datasetPersistentId, + exporter, + version + ) + + const blob = new Blob([metadata.content], { type: metadata.contentType }) + const url = URL.createObjectURL(blob) + newWindow.location.href = url + + setTimeout(() => URL.revokeObjectURL(url), 1000) + } catch { + if (!newWindow.closed) newWindow.close() + toast.error(t('exportMetadataError')) + } + }, + [datasetRepository, datasetPersistentId, datasetVersion.publishingStatus, t] + ) + + return { handleExportMetadata } +} diff --git a/tests/component/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.spec.tsx b/tests/component/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.spec.tsx new file mode 100644 index 000000000..79c507b43 --- /dev/null +++ b/tests/component/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.spec.tsx @@ -0,0 +1,160 @@ +import { act, renderHook } from '@testing-library/react' +import { ReactNode } from 'react' +import { I18nextProvider } from 'react-i18next' +import { toast } from 'react-toastify' +import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' +import i18next from '@/i18n' +import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' +import { useExportMetadata } from '@/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata' +import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' + +const testDatasetPersistentId = 'doi:10.70122/FK2/XXXXXX' + +const wrapper = ({ children }: { children: ReactNode }) => ( + {children} +) + +describe('useExportMetadata', () => { + const datasetRepository: DatasetRepository = {} as DatasetRepository + let openedWindow: { + closed: boolean + document: { title: string } + location: { href: string } + close: Cypress.Agent + } + + beforeEach(() => { + datasetRepository.exportDatasetMetadata = cy.stub().as('exportDatasetMetadata').resolves({ + content: 'exported metadata', + contentType: 'application/xml' + }) + + cy.clock() + + cy.window().then((win) => { + cy.stub(win.URL, 'createObjectURL').as('createObjectURL').returns('blob:exported-metadata') + cy.stub(win.URL, 'revokeObjectURL').as('revokeObjectURL') + openedWindow = { + closed: false, + document: { title: '' }, + location: { href: '' }, + close: cy.stub().as('openedWindowClose') + } + cy.stub(win, 'open') + .as('windowOpen') + .returns(openedWindow as unknown as Window) + }) + }) + + it('should export latest published metadata in a new tab', () => { + const { result } = renderHook( + () => + useExportMetadata({ + datasetRepository, + datasetPersistentId: testDatasetPersistentId, + datasetVersion: DatasetVersionMother.createReleased() + }), + { wrapper } + ) + + cy.then(async () => { + await act(async () => { + await result.current.handleExportMetadata('OAI_ORE') + }) + }) + + cy.get('@windowOpen').should('have.been.calledWith', '', '_blank') + cy.get('@exportDatasetMetadata').should( + 'have.been.calledWith', + testDatasetPersistentId, + 'OAI_ORE' + ) + cy.then(() => { + expect(openedWindow.document.title).to.equal('Export Metadata') + expect(openedWindow.location.href).to.equal('blob:exported-metadata') + }) + cy.get('@createObjectURL').should('have.been.called') + cy.tick(1000) + cy.get('@revokeObjectURL').should('have.been.calledWith', 'blob:exported-metadata') + }) + + it('should export draft metadata when dataset version is draft', () => { + const { result } = renderHook( + () => + useExportMetadata({ + datasetRepository, + datasetPersistentId: testDatasetPersistentId, + datasetVersion: DatasetVersionMother.createDraft() + }), + { wrapper } + ) + + cy.then(async () => { + await act(async () => { + await result.current.handleExportMetadata('OAI_ORE') + }) + }) + + cy.get('@exportDatasetMetadata').should( + 'have.been.calledWith', + testDatasetPersistentId, + 'OAI_ORE', + DatasetNotNumberedVersion.DRAFT + ) + }) + + it('should close the new tab and show an error when export fails', () => { + datasetRepository.exportDatasetMetadata = cy + .stub() + .as('exportDatasetMetadata') + .rejects(new Error('Export failed')) + cy.stub(toast, 'error').as('toastError') + + const { result } = renderHook( + () => + useExportMetadata({ + datasetRepository, + datasetPersistentId: testDatasetPersistentId, + datasetVersion: DatasetVersionMother.createReleased() + }), + { wrapper } + ) + + cy.then(async () => { + await act(async () => { + await result.current.handleExportMetadata('OAI_ORE') + }) + }) + + cy.get('@openedWindowClose').should('have.been.called') + cy.get('@toastError').should( + 'have.been.calledWith', + 'There was a problem exporting the dataset metadata. Please try again.' + ) + }) + + it('should not export metadata when popup is blocked', () => { + cy.window().then((win) => { + ;(win.open as Cypress.Agent).returns(null) + }) + + const { result } = renderHook( + () => + useExportMetadata({ + datasetRepository, + datasetPersistentId: testDatasetPersistentId, + datasetVersion: DatasetVersionMother.createReleased() + }), + { wrapper } + ) + + cy.then(async () => { + await act(async () => { + await result.current.handleExportMetadata('OAI_ORE') + }) + }) + + cy.get('@exportDatasetMetadata').should('not.have.been.called') + cy.get('@createObjectURL').should('not.have.been.called') + }) +}) From 2859e1dbad41de18d517033604158f1a1fdebf17 Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Mon, 13 Jul 2026 11:03:59 -0400 Subject: [PATCH 07/15] fix: canExportMetadata --- src/sections/dataset/DatasetHelper.ts | 65 ------------------ .../ExportMetadataDropdown.tsx | 67 +++++++++++++++++-- 2 files changed, 62 insertions(+), 70 deletions(-) delete mode 100644 src/sections/dataset/DatasetHelper.ts diff --git a/src/sections/dataset/DatasetHelper.ts b/src/sections/dataset/DatasetHelper.ts deleted file mode 100644 index b676f6d31..000000000 --- a/src/sections/dataset/DatasetHelper.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - DatasetNonNumericVersion, - DatasetNonNumericVersionSearchParam, - DatasetPublishingStatus, - DatasetVersion -} from '@/dataset/domain/models/Dataset' -import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' -import { getDatasetVersionsSummaries } from '@/dataset/domain/useCases/getDatasetVersionsSummaries' -import { - DatasetVersionSummaryInfo, - DatasetVersionSummaryStringValues -} from '@/dataset/domain/models/DatasetVersionSummaryInfo' -import { DatasetVersionPaginationInfo } from '@/dataset/domain/models/DatasetVersionPaginationInfo' - -export class DatasetHelper { - static async canExportMetadata( - datasetRepository: DatasetRepository, - datasetId: number | string, - datasetVersion: DatasetVersion, - canUpdateDataset: boolean - ): Promise { - if (datasetVersion.publishingStatus === DatasetPublishingStatus.DRAFT) { - return canUpdateDataset - } - - if (datasetVersion.publishingStatus !== DatasetPublishingStatus.RELEASED) { - return false - } - - return this.isLatestPublishedVersion( - datasetRepository, - datasetId, - datasetVersion.number.toString() - ) - } - - private static async isLatestPublishedVersion( - datasetRepository: DatasetRepository, - datasetId: number | string, - datasetVersionNumber: string - ): Promise { - try { - const versionSummaries = await getDatasetVersionsSummaries( - datasetRepository, - datasetId, - new DatasetVersionPaginationInfo(1, 10) - ) - const latestPublishedVersion = versionSummaries.summaries.find((summary) => - this.isPublishedVersionSummary(summary) - ) - - return latestPublishedVersion?.versionNumber === datasetVersionNumber - } catch { - return false - } - } - - private static isPublishedVersionSummary(summary: DatasetVersionSummaryInfo): boolean { - return ( - summary.versionNumber !== DatasetNonNumericVersion.DRAFT && - summary.versionNumber !== DatasetNonNumericVersionSearchParam.DRAFT && - summary.summary !== DatasetVersionSummaryStringValues.versionDeaccessioned - ) - } -} diff --git a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx index 6ea6b79d6..0068955c4 100644 --- a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx +++ b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx @@ -4,10 +4,21 @@ import { BoxArrowUpRight } from 'react-bootstrap-icons' import { DropdownButton, DropdownButtonItem } from '@iqss/dataverse-design-system' import { useGetAvailableDatasetMetadataExportFormats } from '@/info/domain/hooks/useGetAvailableDatasetMetadataExportFormats' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' -import { DatasetVersion } from '@/dataset/domain/models/Dataset' +import { + DatasetNonNumericVersion, + DatasetNonNumericVersionSearchParam, + DatasetPublishingStatus, + DatasetVersion +} from '@/dataset/domain/models/Dataset' import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' -import { DatasetHelper } from '@/sections/dataset/DatasetHelper' import { useExportMetadata } from '@/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata' +import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' +import { getDatasetVersionsSummaries } from '@/dataset/domain/useCases/getDatasetVersionsSummaries' +import { DatasetVersionPaginationInfo } from '@/dataset/domain/models/DatasetVersionPaginationInfo' +import { + DatasetVersionSummaryInfo, + DatasetVersionSummaryStringValues +} from '@/dataset/domain/models/DatasetVersionSummaryInfo' interface ExportMetadataDropdownProps { datasetPersistentId: string @@ -45,14 +56,14 @@ export const ExportMetadataDropdown = ({ } } - void DatasetHelper.canExportMetadata( + void canExportMetadata( datasetRepository, datasetPersistentId, datasetVersion, canUpdateDataset - ).then((canExportMetadata) => { + ).then((canExportMetadataResult) => { if (isMounted) { - setShouldRender(canExportMetadata) + setShouldRender(canExportMetadataResult) } }) @@ -94,3 +105,49 @@ export const ExportMetadataDropdown = ({ ) } + +async function canExportMetadata( + datasetRepository: DatasetRepository, + datasetId: number | string, + datasetVersion: DatasetVersion, + canUpdateDataset: boolean +): Promise { + if (datasetVersion.publishingStatus === DatasetPublishingStatus.DRAFT) { + return canUpdateDataset + } + + if (datasetVersion.publishingStatus !== DatasetPublishingStatus.RELEASED) { + return false + } + + return isLatestPublishedVersion(datasetRepository, datasetId, datasetVersion.number.toString()) +} + +async function isLatestPublishedVersion( + datasetRepository: DatasetRepository, + datasetId: number | string, + datasetVersionNumber: string +): Promise { + try { + const versionSummaries = await getDatasetVersionsSummaries( + datasetRepository, + datasetId, + new DatasetVersionPaginationInfo(1, 10) + ) + const latestPublishedVersion = versionSummaries.summaries.find((summary) => + isPublishedVersionSummary(summary) + ) + + return latestPublishedVersion?.versionNumber === datasetVersionNumber + } catch { + return false + } +} + +function isPublishedVersionSummary(summary: DatasetVersionSummaryInfo): boolean { + return ( + summary.versionNumber !== DatasetNonNumericVersion.DRAFT && + summary.versionNumber !== DatasetNonNumericVersionSearchParam.DRAFT && + summary.summary !== DatasetVersionSummaryStringValues.versionDeaccessioned + ) +} From 089202e0b2c84a746da25a911845db162b2f3f6b Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Tue, 14 Jul 2026 09:45:04 -0400 Subject: [PATCH 08/15] package update --- package-lock.json | 2544 ++++----------------------------------------- package.json | 2 +- 2 files changed, 223 insertions(+), 2323 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1b0c9e25..ac1ffb201 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@dnd-kit/sortable": "8.0.0", "@dnd-kit/utilities": "3.2.2", "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.2.0-pr463.799c2e1", + "@iqss/dataverse-client-javascript": "2.2.0-alpha.14", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", @@ -230,93 +230,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", - "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "regexpu-core": "^6.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", - "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "debug": "^4.4.3", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.11" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", @@ -326,21 +239,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", @@ -371,20 +269,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-plugin-utils": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", @@ -394,59 +278,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -474,22 +305,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helpers": { "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", @@ -518,109 +333,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", - "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", - "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -676,23 +388,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", - "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", @@ -700,922 +395,7 @@ "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", - "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", - "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", - "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", - "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", - "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1624,48 +404,37 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", - "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-reserved-words": { + "node_modules/@babel/plugin-syntax-jsx": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -1676,119 +445,92 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", - "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" @@ -1797,16 +539,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" @@ -1815,102 +555,30 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", - "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.2.tgz", - "integrity": "sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw==", + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/compat-data": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.28.6", - "@babel/plugin-syntax-import-attributes": "^7.28.6", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.29.0", - "@babel/plugin-transform-async-to-generator": "^7.28.6", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.6", - "@babel/plugin-transform-class-properties": "^7.28.6", - "@babel/plugin-transform-class-static-block": "^7.28.6", - "@babel/plugin-transform-classes": "^7.28.6", - "@babel/plugin-transform-computed-properties": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-dotall-regex": "^7.28.6", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.6", - "@babel/plugin-transform-exponentiation-operator": "^7.28.6", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.28.6", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.28.6", - "@babel/plugin-transform-modules-systemjs": "^7.29.0", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", - "@babel/plugin-transform-numeric-separator": "^7.28.6", - "@babel/plugin-transform-object-rest-spread": "^7.28.6", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.28.6", - "@babel/plugin-transform-optional-chaining": "^7.28.6", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.28.6", - "@babel/plugin-transform-private-property-in-object": "^7.28.6", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.29.0", - "@babel/plugin-transform-regexp-modifiers": "^7.28.6", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.28.6", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.28.6", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.15", - "babel-plugin-polyfill-corejs3": "^0.14.0", - "babel-plugin-polyfill-regenerator": "^0.6.6", - "core-js-compat": "^3.48.0", - "semver": "^6.3.1" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1919,20 +587,20 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/runtime": { @@ -3285,9 +1953,9 @@ } }, "node_modules/@iqss/dataverse-client-javascript": { - "version": "2.2.0-pr463.799c2e1", - "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.2.0-pr463.799c2e1/0adde52ebb8906bcb2e7ea4766a8bc3eb0c3a8eb", - "integrity": "sha512-8hYrvHyv8gO2DZiJIvtc5GTdTezwM8nStCfiG+Sa/M6Us5EswxesbylaF+w3LstQN1mPQL6sJafW5sHjx3Mg6A==", + "version": "2.2.0-alpha.14", + "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.2.0-alpha.14/a89f8485fb6d2f6267be0a42c5db1711357eed95", + "integrity": "sha512-zXYIXoh074Wr31DCyNprUMssxrI1ySa/8XqoknPI/Ye5lf1/XjdoJirT/iJ+mr9p8y8IdLdBTp2eT0ICV74JuQ==", "license": "MIT", "dependencies": { "@types/node": "^18.15.11", @@ -4792,18 +3460,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "devOptional": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -7019,6 +5675,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7032,6 +5689,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7045,6 +5703,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7058,6 +5717,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7071,6 +5731,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7084,6 +5745,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7097,6 +5759,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7110,6 +5773,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7123,6 +5787,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7136,6 +5801,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7149,6 +5815,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7162,6 +5829,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7175,6 +5843,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7188,6 +5857,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7201,6 +5871,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7214,6 +5885,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7227,6 +5899,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7240,6 +5913,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7253,6 +5927,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7266,6 +5941,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7279,6 +5955,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -7292,6 +5969,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8794,6 +7472,7 @@ "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", @@ -9574,34 +8253,11 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, "license": "MIT" }, "node_modules/@types/graceful-fs": { @@ -10408,198 +9064,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0", - "peer": true - }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -10680,7 +9144,7 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -10700,20 +9164,6 @@ "acorn-walk": "^8.0.2" } }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -10761,6 +9211,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", @@ -10818,20 +9269,6 @@ } } }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -10937,6 +9374,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", + "dev": true, "license": "MIT", "dependencies": { "default-require-extensions": "^3.0.0" @@ -10977,6 +9415,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -11349,154 +9788,6 @@ "@babel/core": "^7.8.0" } }, - "node_modules/babel-loader": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", - "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-loader/node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/babel-loader/node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/yocto-queue": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", - "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -11564,51 +9855,6 @@ "dev": true, "license": "MIT" }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", - "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.8", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", - "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8", - "core-js-compat": "^3.48.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", - "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/babel-plugin-styled-components": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", @@ -12004,7 +10250,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/byte-size": { @@ -12112,6 +10358,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", + "dev": true, "license": "MIT", "dependencies": { "hasha": "^5.0.0", @@ -12127,6 +10374,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, "license": "MIT", "dependencies": { "semver": "^6.0.0" @@ -12142,6 +10390,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", @@ -12436,17 +10685,6 @@ } } }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.0" - } - }, "node_modules/ci-info": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", @@ -12479,6 +10717,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -12815,14 +11054,6 @@ "dev": true, "license": "ISC" }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true, - "license": "ISC", - "peer": true - }, "node_modules/common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", @@ -12837,6 +11068,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, "license": "MIT" }, "node_modules/compare-func": { @@ -13065,21 +11297,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "license": "MIT" }, - "node_modules/core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "browserslist": "^4.28.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -13202,6 +11419,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -13593,6 +11811,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13726,6 +11945,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.1.tgz", "integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==", + "dev": true, "license": "MIT", "dependencies": { "strip-bom": "^4.0.0" @@ -14248,12 +12468,14 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -14264,6 +12486,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -14283,21 +12506,6 @@ "once": "^1.4.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", - "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", @@ -14471,14 +12679,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -14541,6 +12741,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, "license": "MIT" }, "node_modules/esbuild": { @@ -15226,17 +13427,6 @@ "dev": true, "license": "MIT" }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/execa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", @@ -15570,6 +13760,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, "license": "MIT", "dependencies": { "commondir": "^1.0.1", @@ -15587,6 +13778,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, "license": "MIT", "dependencies": { "semver": "^6.0.0" @@ -15865,6 +14057,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "dev": true, "funding": [ { "type": "github", @@ -16047,6 +14240,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -16517,14 +14711,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true - }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -16832,6 +15018,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "dev": true, "license": "MIT", "dependencies": { "is-stream": "^2.0.0", @@ -16848,6 +15035,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" @@ -16950,6 +15138,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, "license": "MIT" }, "node_modules/html-parse-stringify": { @@ -17259,6 +15448,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" @@ -17684,6 +15874,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -17936,6 +16127,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -18010,6 +16202,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, "license": "MIT" }, "node_modules/is-unc-path": { @@ -18132,6 +16325,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/isstream": { @@ -18145,6 +16339,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=8" @@ -18154,6 +16349,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "append-transform": "^2.0.0" @@ -18206,6 +16402,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz", "integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==", + "dev": true, "license": "ISC", "dependencies": { "archy": "^1.0.0", @@ -18223,6 +16420,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=8" @@ -18232,6 +16430,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" @@ -18244,6 +16443,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" @@ -18253,6 +16453,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", @@ -18267,6 +16468,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", @@ -18281,6 +16483,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", @@ -21932,21 +20135,6 @@ "node": ">=8" } }, - "node_modules/loader-runner": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", - "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -21969,18 +20157,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", + "dev": true, "license": "MIT" }, "node_modules/lodash.get": { @@ -22161,6 +20342,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, "license": "MIT", "dependencies": { "semver": "^7.5.3" @@ -22176,6 +20358,7 @@ "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -23504,6 +21687,7 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, "funding": [ { "type": "github", @@ -23819,6 +22003,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "dev": true, "license": "MIT", "dependencies": { "process-on-spawn": "^1.0.0" @@ -24268,6 +22453,7 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", "integrity": "sha512-jMW04n9SxKdKi1ZMGhvUTHBN0EICCRkHemEoE5jm6mTYcqcdas0ATzgUgejlQUHMvpnOZqGB5Xxsv9KxJW1j8A==", + "dev": true, "license": "ISC", "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", @@ -24309,6 +22495,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -24320,12 +22507,14 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, "license": "MIT" }, "node_modules/nyc/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -24339,6 +22528,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -24353,6 +22543,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -24373,6 +22564,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.7.5", @@ -24388,6 +22580,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -24400,6 +22593,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, "license": "MIT", "dependencies": { "semver": "^6.0.0" @@ -24415,6 +22609,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -24430,6 +22625,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.2.0" @@ -24442,6 +22638,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" @@ -24454,6 +22651,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -24463,6 +22661,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -24477,12 +22676,14 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, "license": "ISC" }, "node_modules/nyc/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^6.0.0", @@ -24505,6 +22706,7 @@ "version": "18.1.3", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, "license": "ISC", "dependencies": { "camelcase": "^5.0.0", @@ -24977,6 +23179,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, "license": "ISC", "dependencies": { "graceful-fs": "^4.1.15", @@ -25514,6 +23717,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -25633,6 +23837,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, "license": "MIT", "dependencies": { "find-up": "^4.0.0" @@ -25645,6 +23850,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -25658,6 +23864,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -25670,6 +23877,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -25685,6 +23893,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.2.0" @@ -25766,6 +23975,7 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -25987,6 +24197,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.1.0.tgz", "integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==", + "dev": true, "license": "MIT", "dependencies": { "fromentries": "^1.2.0" @@ -26461,6 +24672,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" @@ -26562,6 +24774,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", @@ -27152,28 +25365,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", - "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -27207,51 +25398,11 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/regexpu-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", - "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.2", - "regjsgen": "^0.8.0", - "regjsparser": "^0.13.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.2.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/regjsparser": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", - "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "jsesc": "~3.1.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, "node_modules/release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "dev": true, "license": "ISC", "dependencies": { "es6-error": "^4.0.1" @@ -27358,6 +25509,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -27376,6 +25528,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, "license": "ISC" }, "node_modules/requireindex": { @@ -27534,6 +25687,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -27550,6 +25704,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -27570,6 +25725,7 @@ "version": "4.52.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz", "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -27748,7 +25904,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/sass": { @@ -27785,49 +25941,10 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "loose-envify": "^1.1.0" } }, "node_modules/semver": { @@ -27843,6 +25960,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, "license": "ISC" }, "node_modules/set-function-length": { @@ -27902,6 +26020,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -27914,6 +26033,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -28008,6 +26128,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, "license": "ISC" }, "node_modules/sigstore": { @@ -28157,6 +26278,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -28203,6 +26325,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^2.0.0", @@ -28220,6 +26343,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -28233,6 +26357,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -28242,6 +26367,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, "license": "MIT", "dependencies": { "semver": "^6.0.0" @@ -28483,6 +26609,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -28600,6 +26727,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -28626,6 +26754,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -29381,21 +27510,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -29511,114 +27625,6 @@ "node": ">=4" } }, - "node_modules/terser": { - "version": "5.46.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.2.tgz", - "integrity": "sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==", - "devOptional": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-UYhptBwhWvfIjKd/UuFo6D8uq9xpGLDK+z8EDsj/zWhrTaH34cKEbrkMKfV5YWqGBvAYA3tlzZbs2R+qYrbQJA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "devOptional": true, - "license": "MIT", - "peer": true - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "devOptional": true, - "license": "MIT", - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -30328,6 +28334,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, "license": "MIT", "dependencies": { "is-typedarray": "^1.0.0" @@ -30416,54 +28423,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", - "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", - "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/unified": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", @@ -31034,6 +28993,7 @@ "version": "5.4.20", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", + "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", @@ -31209,6 +29169,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31225,6 +29186,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31241,6 +29203,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31257,6 +29220,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31273,6 +29237,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31289,6 +29254,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31305,6 +29271,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31321,6 +29288,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31337,6 +29305,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31353,6 +29322,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31369,6 +29339,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31385,6 +29356,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31401,6 +29373,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31417,6 +29390,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31433,6 +29407,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31449,6 +29424,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31465,6 +29441,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31481,6 +29458,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31497,6 +29475,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31513,6 +29492,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31529,6 +29509,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31545,6 +29526,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31561,6 +29543,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -31574,6 +29557,7 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -31788,21 +29772,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -31829,66 +29798,6 @@ "node": ">=12" } }, - "node_modules/webpack": { - "version": "5.106.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", - "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.16.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.20.0", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "loader-runner": "^4.3.1", - "mime-db": "^1.54.0", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.17", - "watchpack": "^2.5.1", - "webpack-sources": "^3.3.4" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-sources": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.0.tgz", - "integrity": "sha512-gHwIe1cgBvvfLeu1Yz/dcFpmHfKDVxxyqI+kzqmuxZED81z2ChxpyqPaWcNqigPywhaEke7AjSGga+kxY55gjQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", @@ -31896,17 +29805,6 @@ "dev": true, "license": "MIT" }, - "node_modules/webpack/node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -31961,6 +29859,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -32041,6 +29940,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, "license": "ISC" }, "node_modules/which-typed-array": { diff --git a/package.json b/package.json index 50ad1c755..8c6ad439e 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "@dnd-kit/sortable": "8.0.0", "@dnd-kit/utilities": "3.2.2", "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.2.0-pr463.799c2e1", + "@iqss/dataverse-client-javascript": "2.2.0-alpha.14", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", From e1e4d9bbdcf9fd93a534d2d4cf3dc0885b0c3dba Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Thu, 16 Jul 2026 12:23:04 -0400 Subject: [PATCH 09/15] Revert "Merge with dev branch" This reverts commit 27ecb482fed5c62c65bb03470ed142ba0d27bb81, reversing changes made to 089202e0b2c84a746da25a911845db162b2f3f6b. --- .github/workflows/deploy-beta-testing.yml | 2 +- .github/workflows/deploy.yml | 6 +- .storybook/preview.tsx | 10 +- CHANGELOG.md | 4 +- packages/design-system/CHANGELOG.md | 6 - src/App.tsx | 22 +- src/router/routes.tsx | 5 +- src/sections/account/Account.tsx | 5 +- src/sections/account/AccountFactory.tsx | 3 + .../api-token-section/ApiTokenSection.tsx | 15 +- src/sections/collection/Collection.tsx | 4 +- src/sections/dataset/Dataset.tsx | 6 + src/sections/dataset/DatasetFactory.tsx | 33 +- .../DatasetToolsOptions.tsx | 14 +- .../dataset/dataset-files/DatasetFiles.tsx | 13 +- .../dataset-files/DatasetFilesScrollable.tsx | 12 +- .../dataset-files/files-table/FilesTable.tsx | 9 +- .../FilesTableColumnsDefinition.tsx | 7 +- .../files-table/FilesTableScrollable.tsx | 13 +- .../file-actions/FileActionsHeader.tsx | 10 +- .../DatasetDeleteFileButton.tsx | 10 +- .../DatasetEditFileTagsButton.tsx | 5 +- .../DatasetRestrictFileButton.tsx | 5 +- .../edit-files-menu/EditFilesMenu.tsx | 11 +- .../edit-files-menu/EditFilesOptions.tsx | 12 +- .../file-actions-cell/FileActionsCell.tsx | 6 +- .../file-action-buttons/FileActionButtons.tsx | 6 +- .../DownloadWithTermsAndGuestbookModal.tsx | 4 +- .../file-options-menu/FileOptionsMenu.tsx | 11 +- .../files-table/useFilesTable.tsx | 16 +- .../files-table/useFilesTableScrollable.tsx | 14 +- .../dataset-guestbook/DatasetGuestbook.tsx | 4 +- .../dataset/dataset-terms/DatasetTerms.tsx | 7 +- .../edit-dataset-terms/EditDatasetTerms.tsx | 15 +- .../EditDatasetTermsFactory.tsx | 3 + .../edit-guestbook/EditGuestbook.tsx | 10 +- src/sections/file/File.tsx | 3 +- src/sections/file/FileFactory.tsx | 11 +- .../access-file-menu/FileToolOptions.tsx | 15 +- .../FileEmbeddedExternalTool.tsx | 7 +- .../guestbooks/GuestbookRepositoryContext.ts | 9 + .../GuestbookRepositoryProvider.tsx | 18 + src/sections/session/SessionProvider.tsx | 13 +- .../shared/pagination/PaginationControls.tsx | 34 +- src/sections/sign-up/SignUp.tsx | 8 +- src/sections/sign-up/SignUpFactory.tsx | 3 + .../FormFields.tsx | 6 +- .../ValidTokenNotLinkedAccountForm.tsx | 11 +- .../external-tools/ExternalToolsProvider.tsx | 16 +- .../repositories/RepositoriesProvider.tsx | 57 +-- src/stories/WithRepositories.tsx | 32 +- src/stories/account/Account.stories.tsx | 5 +- .../AccountInfoSection.stories.tsx | 5 +- .../ApiTokenSection.stories.tsx | 20 +- .../NotificationSection.stories.tsx | 10 +- .../CollectionCard.stories.tsx | 48 +-- src/stories/dataset/Dataset.stories.tsx | 58 +-- .../AccessDatasetMenu.stories.tsx | 29 +- .../dataset-files/DatasetFiles.stories.tsx | 60 ++- .../DatasetFilesScrollable.stories.tsx | 60 ++- .../edit-files-menu/EditFilesMenu.stories.tsx | 13 +- .../FileOptionsMenu.stories.tsx | 47 ++- .../DatasetGuestbook.stories.tsx | 6 +- .../dataset-terms/DatasetTerms.stories.tsx | 60 ++- .../EditDatasetTerms.stories.tsx | 10 +- src/stories/file/File.stories.tsx | 62 ++- .../AccessFileMenu.stories.tsx | 23 +- .../EditFileDropdown.stories.tsx | 37 +- .../FileEmbeddedExternalTool.stories.tsx | 37 +- ...loadWithTermsAndGuestbookModal.stories.tsx | 6 +- src/stories/sign-up/SignUp.stories.tsx | 34 +- tests/component/WithRepositories.tsx | 20 +- .../sections/account/Account.spec.tsx | 3 + .../sections/account/ApiTokenSection.spec.tsx | 13 +- .../sections/dataset/Dataset.spec.tsx | 100 +---- .../DatasetToolOptions.spec.tsx | 97 ++--- .../AccessDatasetMenu.spec.tsx | 354 ++++++++++-------- .../dataset-files/DatasetFiles.spec.tsx | 156 ++++++-- .../DatasetFilesScrollable.spec.tsx | 117 ++++-- .../files-table/FilesTable.spec.tsx | 30 +- .../file-actions/FileActionsHeader.spec.tsx | 4 +- .../DownloadFilesButton.spec.tsx | 136 +++---- .../edit-files-menu/EditFilesMenu.spec.tsx | 34 +- .../edit-files-menu/EditFilesOptions.spec.tsx | 192 +++++++--- .../FileActionButtons.spec.tsx | 8 +- .../file-action-buttons/FileTools.spec.tsx | 25 +- .../FileOptionsMenu.spec.tsx | 53 ++- ...ownloadWithTermsAndGuestbookModal.spec.tsx | 6 +- .../DatasetGuestbook.spec.tsx | 6 +- .../useGetDatasetReviews.spec.tsx | 104 ----- .../dataset-terms/DatasetTerms.spec.tsx | 107 +++--- .../EditDatasetTerms.spec.tsx | 38 +- .../edit-dataset-terms/EditGuestbook.spec.tsx | 118 ++++-- .../EditTermsOfAccess.spec.tsx | 4 - tests/component/sections/file/File.spec.tsx | 30 +- .../access-file-menu/AccessFileMenu.spec.tsx | 10 +- .../access-file-menu/FileToolOptions.spec.tsx | 97 ++--- .../FileEmbeddedExternalTool.spec.tsx | 25 +- .../sections/session/SessionProvider.spec.tsx | 34 +- .../citation/CitationDownloadButton.spec.tsx | 41 +- .../sections/sign-up/SignUp.spec.tsx | 75 ++-- .../ValidTokenNotLinkedAccountForm.spec.tsx | 34 +- .../e2e/sections/dataset/Dataset.spec.tsx | 3 +- tests/e2e-integration/shared/TestsUtils.ts | 3 +- tests/support/commands.tsx | 9 +- vite.config.ts | 3 - 106 files changed, 1730 insertions(+), 1570 deletions(-) create mode 100644 src/sections/guestbooks/GuestbookRepositoryContext.ts create mode 100644 src/sections/guestbooks/GuestbookRepositoryProvider.tsx delete mode 100644 tests/component/sections/dataset/dataset-reviews/useGetDatasetReviews.spec.tsx diff --git a/.github/workflows/deploy-beta-testing.yml b/.github/workflows/deploy-beta-testing.yml index 56a60b5ef..238d0fb93 100644 --- a/.github/workflows/deploy-beta-testing.yml +++ b/.github/workflows/deploy-beta-testing.yml @@ -97,7 +97,7 @@ jobs: script: | APPLICATION_NAME=dataverse-frontend APPLICATION_WAR_PATH=deployment/payara/target/$APPLICATION_NAME.war - ASADMIN='/usr/local/payara7/bin/asadmin --user admin' + ASADMIN='/usr/local/payara6/bin/asadmin --user admin' DATAVERSE_FRONTEND=`$ASADMIN list-applications |grep $APPLICATION_NAME |awk '{print $1}'` $ASADMIN undeploy $DATAVERSE_FRONTEND $ASADMIN deploy --name $APPLICATION_NAME --contextroot ${{ env.FRONTEND_BASE_PATH }} $APPLICATION_WAR_PATH diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0fa39dd5d..3a17e535b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -155,7 +155,7 @@ jobs: script: | APPLICATION_NAME=dataverse-frontend APPLICATION_WAR_PATH=deployment/payara/target/$APPLICATION_NAME.war - ASADMIN='/usr/local/payara7/bin/asadmin --user admin' + ASADMIN='/usr/local/payara6/bin/asadmin --user admin' DATAVERSE_FRONTEND=`$ASADMIN list-applications |grep $APPLICATION_NAME |awk '{print $1}'` $ASADMIN undeploy $DATAVERSE_FRONTEND $ASADMIN deploy --name $APPLICATION_NAME --contextroot /${{ github.event.inputs.basepath }} $APPLICATION_WAR_PATH @@ -171,7 +171,7 @@ jobs: script: | APPLICATION_NAME=dataverse-frontend APPLICATION_WAR_PATH=/tmp/deployment/payara/target/$APPLICATION_NAME.war - ASADMIN='/usr/local/payara7/bin/asadmin --user admin' + ASADMIN='/usr/local/payara6/bin/asadmin --user admin' DATAVERSE_FRONTEND=`$ASADMIN list-applications |grep $APPLICATION_NAME |awk '{print $1}'` $ASADMIN undeploy $DATAVERSE_FRONTEND $ASADMIN deploy --name $APPLICATION_NAME --contextroot /${{ github.event.inputs.basepath }} $APPLICATION_WAR_PATH @@ -187,7 +187,7 @@ jobs: script: | APPLICATION_NAME=dataverse-frontend APPLICATION_WAR_PATH=/tmp/deployment/payara/target/$APPLICATION_NAME.war - ASADMIN='/usr/local/payara7/bin/asadmin --user admin' + ASADMIN='/usr/local/payara6/bin/asadmin --user admin' DATAVERSE_FRONTEND=`$ASADMIN list-applications |grep $APPLICATION_NAME |awk '{print $1}'` $ASADMIN undeploy $DATAVERSE_FRONTEND $ASADMIN deploy --name $APPLICATION_NAME --contextroot /${{ github.event.inputs.basepath }} $APPLICATION_WAR_PATH diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 5b68e2be5..9c9223e68 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -5,7 +5,6 @@ import { createBrowserRouter, RouteObject, RouterProvider } from 'react-router-d import { FakerHelper } from '../tests/component/shared/FakerHelper' import { ExternalToolsProvider } from '../src/shared/contexts/external-tools/ExternalToolsProvider' import { ExternalToolsEmptyMockRepository } from '../src/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' -import { RepositoriesStoryProvider } from '../src/stories/WithRepositories' import 'react-loading-skeleton/dist/skeleton.css' import '../src/assets/global.scss' import '../src/assets/swal-custom.scss' @@ -41,12 +40,9 @@ const preview: Preview = { return ( - - - - - + + + ) } diff --git a/CHANGELOG.md b/CHANGELOG.md index 5167090ee..221028b82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel ### Changed -- Hide "Export Metadata" on dataset and file pages that are not for the latest released dataset version. -- Show "Export Metadata" on dataset and file pages for draft version. -- Avoided prop-drilling for file, guestbook, user and external tool repository, so used context to share repository instances. +- Hide "Export Metadata" on file pages that are not for the latest released dataset version. ### Fixed diff --git a/packages/design-system/CHANGELOG.md b/packages/design-system/CHANGELOG.md index 2237524f7..18ec5fc99 100644 --- a/packages/design-system/CHANGELOG.md +++ b/packages/design-system/CHANGELOG.md @@ -5,12 +5,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # Non Published Changes -### Added - -- **SelectAdvanced:** - - Add support for grouping options via an optional `group` field on each option; renders a header above each group in the unfiltered menu (hidden while a search is active). - - Add `hidePlaceholderOption` prop to omit the "Select..." placeholder row, for selects that must always resolve to a real option. - # [2.2.0](https://github.com/IQSS/dataverse-frontend/compare/@iqss/dataverse-design-system@2.1.0...@iqss/dataverse-design-system@2.2.0) (2026-04-24) ### Fixed diff --git a/src/App.tsx b/src/App.tsx index 11c402b4f..07ef2e934 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,9 +8,6 @@ import { ExternalToolsJSDataverseRepository } from './externalTools/infrastructu import { RepositoriesProvider } from './shared/contexts/repositories/RepositoriesProvider' import { CollectionJSDataverseRepository } from './collection/infrastructure/repositories/CollectionJSDataverseRepository' import { DatasetJSDataverseRepository } from './dataset/infrastructure/repositories/DatasetJSDataverseRepository' -import { FileJSDataverseRepository } from './files/infrastructure/FileJSDataverseRepository' -import { GuestbookJSDataverseRepository } from './guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository' -import { UserJSDataverseRepository } from './users/infrastructure/repositories/UserJSDataverseRepository' import 'react-loading-skeleton/dist/skeleton.css' import './assets/global.scss' import './assets/react-toastify-custom.scss' @@ -19,9 +16,6 @@ import './assets/swal-custom.scss' const externalToolsRepository = new ExternalToolsJSDataverseRepository() const collectionRepository = new CollectionJSDataverseRepository() const datasetRepository = new DatasetJSDataverseRepository() -const fileRepository = new FileJSDataverseRepository() -const guestbookRepository = new GuestbookJSDataverseRepository() -const userRepository = new UserJSDataverseRepository() function App() { const appConfig = requireAppConfig() @@ -44,17 +38,13 @@ function App() { return ( <> - - + + - - + + diff --git a/src/router/routes.tsx b/src/router/routes.tsx index 976ca5e97..c664b40fa 100644 --- a/src/router/routes.tsx +++ b/src/router/routes.tsx @@ -1,5 +1,6 @@ import { lazy, Suspense } from 'react' import { Navigate, RouteObject } from 'react-router-dom' +import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' import { Route } from '@/sections/Route.enum' import { Layout } from '@/sections/layout/Layout' import { ErrorPage } from '@/sections/error-page/ErrorPage' @@ -8,6 +9,8 @@ import { AuthCallback } from '@/sections/auth-callback/AuthCallback' import { SessionProvider } from '@/sections/session/SessionProvider' import { ProtectedRoute } from './ProtectedRoute' +const userRepository = new UserJSDataverseRepository() + const Homepage = lazy(() => import('../sections/homepage/HomepageFactory').then(({ HomepageFactory }) => ({ default: () => HomepageFactory.create() @@ -152,7 +155,7 @@ const AdvancedSearchPage = lazy(() => export const routes: RouteObject[] = [ { - element: , + element: , children: [ { path: '/', diff --git a/src/sections/account/Account.tsx b/src/sections/account/Account.tsx index a53bbd1d9..bd29f9185 100644 --- a/src/sections/account/Account.tsx +++ b/src/sections/account/Account.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next' import { useSearchParams } from 'react-router-dom' import { Tabs } from '@iqss/dataverse-design-system' import { AccountHelper, AccountPanelTabKey } from './AccountHelper' +import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' import { NotificationRepository } from '@/notifications/domain/repositories/NotificationRepository' import { ApiTokenSection } from './api-token-section/ApiTokenSection' import { AccountInfoSection } from './account-info-section/AccountInfoSection' @@ -16,12 +17,14 @@ const tabsKeys = AccountHelper.ACCOUNT_PANEL_TABS_KEYS interface AccountProps { defaultActiveTabKey: AccountPanelTabKey + userRepository: UserJSDataverseRepository roleRepository: RoleJSDataverseRepository notificationRepository: NotificationRepository } export const Account = ({ defaultActiveTabKey, + userRepository, roleRepository, notificationRepository }: AccountProps) => { @@ -63,7 +66,7 @@ export const Account = ({
- +
diff --git a/src/sections/account/AccountFactory.tsx b/src/sections/account/AccountFactory.tsx index a127ec977..a0fa99c97 100644 --- a/src/sections/account/AccountFactory.tsx +++ b/src/sections/account/AccountFactory.tsx @@ -2,9 +2,11 @@ import { ReactElement } from 'react' import { useSearchParams } from 'react-router-dom' import { AccountHelper } from './AccountHelper' import { Account } from './Account' +import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' import { RoleJSDataverseRepository } from '@/roles/infrastructure/repositories/RoleJSDataverseRepository' import { NotificationJSDataverseRepository } from '@/notifications/infrastructure/repositories/NotificationJSDataverseRepository' +const userRepository = new UserJSDataverseRepository() const roleRepository = new RoleJSDataverseRepository() const notificationRepository = new NotificationJSDataverseRepository() @@ -21,6 +23,7 @@ function AccountWithSearchParams() { return ( diff --git a/src/sections/account/api-token-section/ApiTokenSection.tsx b/src/sections/account/api-token-section/ApiTokenSection.tsx index 212d7eea7..a9efd7374 100644 --- a/src/sections/account/api-token-section/ApiTokenSection.tsx +++ b/src/sections/account/api-token-section/ApiTokenSection.tsx @@ -7,18 +7,21 @@ import { useRevokeApiToken } from './useRevokeApiToken' import { ApiTokenSectionSkeleton } from './ApiTokenSectionSkeleton' import { TokenInfo } from '@/users/domain/models/TokenInfo' import { DateHelper } from '@/shared/helpers/DateHelper' +import { UserRepository } from '@/users/domain/repositories/UserRepository' import { Button } from '@iqss/dataverse-design-system' import { Alert } from '@iqss/dataverse-design-system' -import { useUserRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import accountStyles from '../Account.module.scss' import styles from './ApiTokenSection.module.scss' -export const ApiTokenSection = () => { - const { userRepository } = useUserRepositories() +interface ApiTokenSectionProps { + repository: UserRepository +} + +export const ApiTokenSection = ({ repository }: ApiTokenSectionProps) => { const { t } = useTranslation('account', { keyPrefix: 'apiToken' }) const [currentApiTokenInfo, setCurrentApiTokenInfo] = useState() - const { error: getError, apiTokenInfo, isLoading } = useGetApiToken(userRepository) + const { error: getError, apiTokenInfo, isLoading } = useGetApiToken(repository) useEffect(() => { setCurrentApiTokenInfo(apiTokenInfo) @@ -29,7 +32,7 @@ export const ApiTokenSection = () => { isRecreating, error: recreatingError, apiTokenInfo: updatedTokenInfo - } = useRecreateApiToken(userRepository) + } = useRecreateApiToken(repository) useEffect(() => { if (updatedTokenInfo) { @@ -41,7 +44,7 @@ export const ApiTokenSection = () => { void recreateToken() } - const { revokeToken, isRevoking, error: revokingError } = useRevokeApiToken(userRepository) + const { revokeToken, isRevoking, error: revokingError } = useRevokeApiToken(repository) const handleRevokeToken = async () => { await revokeToken() diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index bc3fdb137..212eafcbc 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -22,7 +22,7 @@ import { ContactRepository } from '@/contact/domain/repositories/ContactReposito import { NotFoundPage } from '../not-found-page/NotFoundPage' import { LinkCollectionDropdown } from './link-collection-dropdown/LinkCollectionDropdown' import { useSession } from '../session/SessionContext' -import { useRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' +import { useCollectionRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from './Collection.module.scss' interface CollectionProps { @@ -41,7 +41,7 @@ export function Collection({ contactRepository, accountCreated }: CollectionProps) { - const { collectionRepository } = useRepositories() + const { collectionRepository } = useCollectionRepositories() useScrollTop() const { previousPath } = useHistoryTracker() const previousPathIsHomepage = previousPath === Route.HOME diff --git a/src/sections/dataset/Dataset.tsx b/src/sections/dataset/Dataset.tsx index 4bac21004..68d5dfaa4 100644 --- a/src/sections/dataset/Dataset.tsx +++ b/src/sections/dataset/Dataset.tsx @@ -11,6 +11,7 @@ import { DatasetMetadata } from './dataset-metadata/DatasetMetadata' import { DatasetSummary } from './dataset-summary/DatasetSummary' import { DatasetCitation } from './dataset-citation/DatasetCitation' import { DatasetFiles } from './dataset-files/DatasetFiles' +import { FileRepository } from '../../files/domain/repositories/FileRepository' import { DatasetActionButtons } from './dataset-action-buttons/DatasetActionButtons' import { useDataset } from './DatasetContext' import { useNotImplementedModal } from '../not-implemented/NotImplementedModalContext' @@ -34,6 +35,7 @@ import { useDatasetRepositories } from '@/shared/contexts/repositories/Repositor import { DatasetReviews } from './dataset-reviews/DatasetReviews' interface DatasetProps { + fileRepository: FileRepository metadataBlockInfoRepository: MetadataBlockInfoRepository contactRepository: ContactRepository dataverseInfoRepository: DataverseInfoRepository @@ -43,6 +45,7 @@ interface DatasetProps { } export function Dataset({ + fileRepository, metadataBlockInfoRepository, contactRepository, dataverseInfoRepository, @@ -172,6 +175,7 @@ export function Dataset({
{filesTabInfiniteScrollEnabled ? ( ) : ( @@ -202,6 +207,7 @@ export function Dataset({ - - - - - - - - - - - - + + + + + + + + + + + + + + + ) } } @@ -77,6 +82,7 @@ function DatasetWithSearchParams() { searchParams={{ privateUrlToken: privateUrlToken }} isPublishing={publishInProgress}> { const { t } = useTranslation('shared') - const { datasetExploreTools, datasetConfigureTools } = useExternalTools() + const { datasetExploreTools, datasetConfigureTools, externalToolsRepository } = useExternalTools() const tools = kind === 'explore' ? datasetExploreTools : datasetConfigureTools if (!tools || tools.length === 0) return null @@ -35,6 +35,7 @@ const DatasetToolOptions = ({ persistentId, kind }: DatasetToolOptionsProps) => @@ -47,10 +48,15 @@ interface ToolOptionProps { toolId: number toolDisplayName: string persistentId: string + externalToolsRepository: ExternalToolsRepository } -const ToolOption = ({ toolId, toolDisplayName, persistentId }: ToolOptionProps) => { - const { externalToolsRepository } = useExternalToolsRepositories() +const ToolOption = ({ + toolId, + toolDisplayName, + persistentId, + externalToolsRepository +}: ToolOptionProps) => { const [isOpening, setIsOpening] = useState(false) const { t, i18n } = useTranslation('shared') const openingRef = useRef(false) diff --git a/src/sections/dataset/dataset-files/DatasetFiles.tsx b/src/sections/dataset/dataset-files/DatasetFiles.tsx index d321e6298..df6a4820f 100644 --- a/src/sections/dataset/dataset-files/DatasetFiles.tsx +++ b/src/sections/dataset/dataset-files/DatasetFiles.tsx @@ -1,3 +1,4 @@ +import { FileRepository } from '../../../files/domain/repositories/FileRepository' import { useState } from 'react' import { FilesTable } from './files-table/FilesTable' import { FileCriteriaForm } from './file-criteria-form/FileCriteriaForm' @@ -6,19 +7,22 @@ import { useFiles } from './useFiles' import { PaginationControls } from '../../shared/pagination/PaginationControls' import { DatasetVersion } from '../../../dataset/domain/models/Dataset' import { FilePaginationInfo } from '../../../files/domain/models/FilePaginationInfo' -import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface DatasetFilesProps { + filesRepository: FileRepository datasetPersistentId: string datasetVersion: DatasetVersion } -export function DatasetFiles({ datasetPersistentId, datasetVersion }: DatasetFilesProps) { - const { fileRepository } = useDatasetRepositories() +export function DatasetFiles({ + filesRepository, + datasetPersistentId, + datasetVersion +}: DatasetFilesProps) { const [paginationInfo, setPaginationInfo] = useState(new FilePaginationInfo()) const [criteria, setCriteria] = useState(new FileCriteria()) const { files, isLoading, filesCountInfo, filesTotalDownloadSize } = useFiles( - fileRepository, + filesRepository, datasetPersistentId, datasetVersion, setPaginationInfo, @@ -35,6 +39,7 @@ export function DatasetFiles({ datasetPersistentId, datasetVersion }: DatasetFil /> (null) const criteriaContainerRef = useRef(null) const criteriaContainerSize = useObserveElementSize(criteriaContainerRef) @@ -43,7 +44,7 @@ export function DatasetFilesScrollable({ isLoading: _isLoadingFilesCountInfo, error: errorFilesCountInfo } = useGetFilesCountInfo({ - filesRepository: fileRepository, + filesRepository, datasetPersistentId, datasetVersion, criteria, @@ -55,7 +56,7 @@ export function DatasetFilesScrollable({ isLoading: _isLoadingFilesTotalDownloadSize, error: errorFilesTotalDownloadSize } = useGetFilesTotalDownloadSize({ - filesRepository: fileRepository, + filesRepository, datasetPersistentId, datasetVersion, criteria, @@ -74,7 +75,7 @@ export function DatasetFilesScrollable({ isEmptyFiles, refreshFiles } = useGetAccumulatedFiles({ - filesRepository: fileRepository, + filesRepository, datasetPersistentId, datasetVersion }) @@ -182,6 +183,7 @@ export function DatasetFilesScrollable({ showSentryRef={showSentryRef} isEmptyFiles={isEmptyFiles} accumulatedCount={accumulatedCount} + fileRepository={filesRepository} />
diff --git a/src/sections/dataset/dataset-files/files-table/FilesTable.tsx b/src/sections/dataset/dataset-files/files-table/FilesTable.tsx index 75afed124..319bd4ada 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTable.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTable.tsx @@ -10,9 +10,12 @@ import { useEffect, useState } from 'react' import { FileSelection } from './row-selection/useFileSelection' import { FileCriteria } from '../../../../files/domain/models/FileCriteria' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' +import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface FilesTableProps { files: FilePreview[] + fileRepository: FileRepository isLoading: boolean paginationInfo: FilePaginationInfo filesTotalDownloadSize: number @@ -24,11 +27,15 @@ export function FilesTable({ isLoading, paginationInfo, filesTotalDownloadSize, + fileRepository, criteria }: FilesTableProps) { + const { datasetRepository } = useDatasetRepositories() const { table, fileSelection, selectAllFiles, clearFileSelection } = useFilesTable( files, - paginationInfo + paginationInfo, + fileRepository, + datasetRepository ) const [visitedPagination, setVisitedPagination] = useState(paginationInfo) diff --git a/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx b/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx index 3cfba53f8..66c8d8083 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx @@ -7,10 +7,14 @@ import { FileActionsCell } from './file-actions/file-actions-cell/FileActionsCel import { FileSelection } from './row-selection/useFileSelection' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' import { RowSelectionCheckbox } from '@/sections/shared/form/row-selection-checkbox/RowSelectionCheckbox' +import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' export const createColumnsDefinition = ( paginationInfo: FilePaginationInfo, fileSelection: FileSelection, + fileRepository: FileRepository, + datasetRepository: DatasetRepository, accumulatedFilesCount?: number ): ColumnDef[] => [ { @@ -52,9 +56,10 @@ export const createColumnsDefinition = ( row.original)} fileSelection={fileSelection} + fileRepository={fileRepository} /> ), accessorKey: 'status', - cell: (props) => + cell: (props) => } ] diff --git a/src/sections/dataset/dataset-files/files-table/FilesTableScrollable.tsx b/src/sections/dataset/dataset-files/files-table/FilesTableScrollable.tsx index 878aa45f9..72e9739bf 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTableScrollable.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTableScrollable.tsx @@ -11,6 +11,8 @@ import { ZipDownloadLimitMessage } from './zip-download-limit-message/ZipDownloa import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' import { type SentryRef } from '../DatasetFilesScrollable' import styles from './FilesTable.module.scss' +import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface FilesTableScrollableProps { files: FilePreview[] @@ -21,6 +23,7 @@ interface FilesTableScrollableProps { sentryRef: SentryRef showSentryRef: boolean isEmptyFiles: boolean + fileRepository: FileRepository accumulatedCount: number } @@ -33,10 +36,18 @@ export const FilesTableScrollable = ({ sentryRef, showSentryRef, isEmptyFiles, + fileRepository, accumulatedCount }: FilesTableScrollableProps) => { + const { datasetRepository } = useDatasetRepositories() const { table, fileSelection, selectAllPossibleRows, clearRowsSelection } = - useFilesTableScrollable(files, paginationInfo, accumulatedCount) + useFilesTableScrollable( + files, + paginationInfo, + accumulatedCount, + fileRepository, + datasetRepository + ) const [previousCriteria, setPreviousCriteria] = useState(criteria) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx index 2162ae7e7..e7eac554b 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx @@ -4,17 +4,23 @@ import styles from './FileActionsHeader.module.scss' import { useTranslation } from 'react-i18next' import { DownloadFilesButton } from './download-files/DownloadFilesButton' import { FileSelection } from '../row-selection/useFileSelection' +import { FileRepository } from '@/files/domain/repositories/FileRepository' interface FileActionsHeaderProps { files: FilePreview[] fileSelection: FileSelection + fileRepository: FileRepository } -export function FileActionsHeader({ files, fileSelection }: FileActionsHeaderProps) { +export function FileActionsHeader({ + files, + fileSelection, + fileRepository +}: FileActionsHeaderProps) { const { t } = useTranslation('files') return (
- +
) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton.tsx index e324b7e1a..7359f5f4e 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton.tsx @@ -3,21 +3,25 @@ import { useTranslation } from 'react-i18next' import { useNavigate, useSearchParams } from 'react-router-dom' import { toast } from 'react-toastify' import { DropdownButtonItem } from '@iqss/dataverse-design-system' +import { FileRepository } from '@/files/domain/repositories/FileRepository' import { QueryParamKey, Route } from '@/sections/Route.enum' import { DatasetNonNumericVersionSearchParam } from '@/dataset/domain/models/Dataset' import { ConfirmDeleteFileModal } from '@/sections/file/file-action-buttons/edit-file-menu/delete-file-button/confirm-delete-file-modal/ConfirmDeleteFileModal' import { useDeleteFile } from '@/sections/file/file-action-buttons/edit-file-menu/delete-file-button/useDeleteFile' import { useFilesContext } from '@/sections/file/FilesContext' import { EditFilesMenuDatasetInfo } from './EditFilesOptions' -import { useFileRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface DatasetDeleteFileButtonProps { fileId: number + fileRepository: FileRepository datasetInfo: EditFilesMenuDatasetInfo } -export const DatasetDeleteFileButton = ({ fileId, datasetInfo }: DatasetDeleteFileButtonProps) => { - const { fileRepository } = useFileRepositories() +export const DatasetDeleteFileButton = ({ + fileId, + fileRepository, + datasetInfo +}: DatasetDeleteFileButtonProps) => { const [showConfirmationModal, setShowConfirmationModal] = useState(false) const navigate = useNavigate() const { t } = useTranslation('file') diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetEditFileTagsButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetEditFileTagsButton.tsx index 3e03f4cd8..27a308276 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetEditFileTagsButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetEditFileTagsButton.tsx @@ -4,16 +4,17 @@ import { DropdownButtonItem } from '@iqss/dataverse-design-system' import { EditFileTagsModal } from '@/sections/file/file-action-buttons/edit-file-menu/edit-file-tags/edit-file-tags-modal/EditFileTagsModal' import { useUpdateFileCategories } from '@/sections/file/file-action-buttons/edit-file-menu/edit-file-tags/useUpdateFileCategories' import { useUpdateFileTabularTags } from '@/sections/file/file-action-buttons/edit-file-menu/edit-file-tags/useUpdateFileTabularTags' +import { FileRepository } from '@/files/domain/repositories/FileRepository' import { toast } from 'react-toastify' import { FileLabel } from '@/files/domain/models/FileMetadata' import { useFilesContext } from '@/sections/file/FilesContext' import { QueryParamKey, Route } from '@/sections/Route.enum' import { useNavigate, useSearchParams } from 'react-router-dom' import { DatasetNonNumericVersionSearchParam } from '@/dataset/domain/models/Dataset' -import { useFileRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface EditFileTagsButtonProps { fileId: number + fileRepository: FileRepository existingLabels?: FileLabel[] datasetPersistentId: string isTabularFile: boolean @@ -21,11 +22,11 @@ interface EditFileTagsButtonProps { export const DatasetEditFileTagsButton = ({ fileId, + fileRepository, existingLabels, datasetPersistentId, isTabularFile }: EditFileTagsButtonProps) => { - const { fileRepository } = useFileRepositories() const [isModalOpen, setIsModalOpen] = useState(false) const { t } = useTranslation('file') const { refreshFiles } = useFilesContext() diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton.tsx index 5716e4659..9b91daaba 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton.tsx @@ -3,26 +3,27 @@ import { useTranslation } from 'react-i18next' import { useNavigate, useSearchParams } from 'react-router-dom' import { toast } from 'react-toastify' import { DropdownButtonItem } from '@iqss/dataverse-design-system' +import { FileRepository } from '@/files/domain/repositories/FileRepository' import { QueryParamKey, Route } from '@/sections/Route.enum' import { DatasetNonNumericVersionSearchParam } from '@/dataset/domain/models/Dataset' import { ConfirmRestrictFileModal } from '@/sections/file/file-action-buttons/edit-file-menu/restrict-file-button/confirm-restrict-file-modal/ConfirmRestrictFileModal' import { useRestrictFile } from '@/sections/file/file-action-buttons/edit-file-menu/restrict-file-button/useRestrictFile' import { useFilesContext } from '@/sections/file/FilesContext' import { EditFilesMenuDatasetInfo } from './EditFilesOptions' -import { useFileRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface DatasetRestrictFileButtonProps { fileId: number isRestricted: boolean + fileRepository: FileRepository datasetInfo: EditFilesMenuDatasetInfo } export const DatasetRestrictFileButton = ({ fileId, isRestricted, + fileRepository, datasetInfo }: DatasetRestrictFileButtonProps) => { - const { fileRepository } = useFileRepositories() const [showConfirmationModal, setShowConfirmationModal] = useState(false) const navigate = useNavigate() const { t } = useTranslation('file') diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx index e03335c43..df49cb577 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx @@ -7,14 +7,16 @@ import { useTranslation } from 'react-i18next' import { useDataset } from '../../../../DatasetContext' import { FileSelection } from '../../row-selection/useFileSelection' import { useMediaQuery } from '../../../../../../shared/hooks/useMediaQuery' +import { FileRepository } from '@/files/domain/repositories/FileRepository' import styles from './EditFilesMenu.module.scss' interface EditFilesMenuProps { files: FilePreview[] fileSelection: FileSelection + fileRepository: FileRepository } const MINIMUM_FILES_COUNT_TO_SHOW_EDIT_FILES_BUTTON = 1 -export function EditFilesMenu({ files, fileSelection }: EditFilesMenuProps) { +export function EditFilesMenu({ files, fileSelection, fileRepository }: EditFilesMenuProps) { const { t } = useTranslation('files') const { user } = useSession() const { dataset } = useDataset() @@ -38,7 +40,12 @@ export function EditFilesMenu({ files, fileSelection }: EditFilesMenuProps) { disabled={ dataset.checkIsLockedFromEdits(user.persistentId) || !dataset.hasValidTermsOfAccess }> - +
) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx index 7c9d6bcb0..62b46755b 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx @@ -6,6 +6,7 @@ import { useState } from 'react' import { FileSelection } from '../../row-selection/useFileSelection' import { NoSelectedFilesModal } from '../no-selected-files-modal/NoSelectedFilesModal' import { useNotImplementedModal } from '../../../../../not-implemented/NotImplementedModalContext' +import { FileRepository } from '@/files/domain/repositories/FileRepository' import { DatasetRestrictFileButton } from '@/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton' import { DatasetDeleteFileButton } from '@/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton' import { RouteWithParams } from '@/sections/Route.enum' @@ -20,6 +21,7 @@ type EditFilesOptionsProps = files: FilePreview[] file?: never fileSelection: FileSelection + fileRepository: FileRepository datasetInfo?: never isHeader: true } @@ -27,6 +29,7 @@ type EditFilesOptionsProps = files?: never file: FilePreview fileSelection?: never + fileRepository: FileRepository datasetInfo: EditFilesMenuDatasetInfo isHeader: false } @@ -44,6 +47,7 @@ export function EditFilesOptions({ file, files, fileSelection, + fileRepository, datasetInfo, isHeader }: EditFilesOptionsProps) { @@ -74,6 +78,7 @@ export function EditFilesOptions({ {/* TODO: remove this when we can handle non-S3 files */} @@ -92,12 +97,17 @@ export function EditFilesOptions({ - + ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.tsx index 111ca468f..525cb407b 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.tsx @@ -1,3 +1,4 @@ +import { FileRepository } from '@/files/domain/repositories/FileRepository' import { FilePreview } from '../../../../../../files/domain/models/FilePreview' import { FileActionButtons } from './file-action-buttons/FileActionButtons' import { FileInfoMessages } from './file-info-messages/FileInfoMessages' @@ -5,12 +6,13 @@ import styles from './FileActionsCell.module.scss' interface FileActionsCellProps { file: FilePreview + fileRepository: FileRepository } -export function FileActionsCell({ file }: FileActionsCellProps) { +export function FileActionsCell({ file, fileRepository }: FileActionsCellProps) { return (
- +
) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.tsx index 5b2bcd055..8f1c891f6 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.tsx @@ -1,5 +1,6 @@ import { useTranslation } from 'react-i18next' import { ButtonGroup } from '@iqss/dataverse-design-system' +import { FileRepository } from '@/files/domain/repositories/FileRepository' import { FilePreview } from '@/files/domain/models/FilePreview' import { DatasetPublishingStatus } from '@/dataset/domain/models/Dataset' import { AccessFileMenu } from '@/sections/file/file-action-buttons/access-file-menu/AccessFileMenu' @@ -9,8 +10,9 @@ import { FileTools } from './FileTools' interface FileActionButtonsProps { file: FilePreview + fileRepository: FileRepository } -export function FileActionButtons({ file }: FileActionButtonsProps) { +export function FileActionButtons({ file, fileRepository }: FileActionButtonsProps) { const { t } = useTranslation('files') const isBelow768px = useMediaQuery('(max-width: 768px)') @@ -28,7 +30,7 @@ export function FileActionButtons({ file }: FileActionButtonsProps) { ingestInProgress={file.ingest.isInProgress} asIcon /> - + ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx index 7edb8b112..1e873c074 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx @@ -12,7 +12,7 @@ import { Guestbook, GuestbookCustomQuestion } from '@/guestbooks/domain/models/G import { useGuestbookCollectSubmission } from './useGuestbookCollectSubmission' import { CustomTerms as CustomTermsModel, DatasetLicense } from '@/dataset/domain/models/Dataset' import { useAccessRepository } from '@/sections/access/AccessRepositoryContext' -import { useGuestbookRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' +import { useGuestbookRepository } from '@/sections/guestbooks/GuestbookRepositoryContext' import { GuestbookAnswerDTO, GuestbookResponseDTO @@ -50,7 +50,7 @@ export function DownloadWithTermsAndGuestbookModal({ const { t: tDataset } = useTranslation('dataset') const { user } = useSession() const accessRepository = useAccessRepository() - const { guestbookRepository } = useGuestbookRepositories() + const guestbookRepository = useGuestbookRepository() const hasGuestbook = guestbookId !== undefined const [formValues, setFormValues] = useState({}) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.tsx index 1cf941f25..325e3adaa 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.tsx @@ -7,14 +7,16 @@ import { useTranslation } from 'react-i18next' import { useState } from 'react' import { FileAlreadyDeletedModal } from './FileAlreadyDeletedModal' import { useDataset } from '../../../../../../DatasetContext' +import { FileRepository } from '@/files/domain/repositories/FileRepository' import { EditFilesMenuDatasetInfo } from '../../../edit-files-menu/EditFilesOptions' import { FileConfigureToolsOptions } from '@/sections/file/file-action-buttons/access-file-menu/FileToolOptions' interface FileOptionsMenuProps { file: FilePreview + fileRepository: FileRepository } -export function FileOptionsMenu({ file }: FileOptionsMenuProps) { +export function FileOptionsMenu({ file, fileRepository }: FileOptionsMenuProps) { const { t } = useTranslation('files') const { user } = useSession() const { dataset } = useDataset() @@ -69,7 +71,12 @@ export function FileOptionsMenu({ file }: FileOptionsMenuProps) { {t('actions.optionsMenu.headers.editOptions')} - + diff --git a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx index 02e7be148..09202cda5 100644 --- a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx +++ b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx @@ -4,12 +4,19 @@ import { getCoreRowModel, Row, useReactTable } from '@tanstack/react-table' import { createColumnsDefinition } from './FilesTableColumnsDefinition' import { useFileSelection } from './row-selection/useFileSelection' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' +import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' export type RowSelection = { [key: string]: boolean } -export function useFilesTable(files: FilePreview[], paginationInfo: FilePaginationInfo) { +export function useFilesTable( + files: FilePreview[], + paginationInfo: FilePaginationInfo, + fileRepository: FileRepository, + datasetRepository: DatasetRepository +) { const [currentPageRowSelection, setCurrentPageRowSelection] = useState({}) const [currentPageSelectedRowModel, setCurrentPageSelectedRowModel] = useState< Record> @@ -21,7 +28,12 @@ export function useFilesTable(files: FilePreview[], paginationInfo: FilePaginati ) const table = useReactTable({ data: files, - columns: createColumnsDefinition(paginationInfo, fileSelection), + columns: createColumnsDefinition( + paginationInfo, + fileSelection, + fileRepository, + datasetRepository + ), state: { rowSelection: currentPageRowSelection }, diff --git a/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx b/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx index 3210028f1..7eefb8dac 100644 --- a/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx +++ b/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx @@ -4,6 +4,8 @@ import { FilePreview } from '../../../../files/domain/models/FilePreview' import { getCoreRowModel, Row, useReactTable } from '@tanstack/react-table' import { createColumnsDefinition } from './FilesTableColumnsDefinition' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' +import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' export type RowSelection = { [key: string]: boolean @@ -16,7 +18,9 @@ export type FileSelection = { export function useFilesTableScrollable( files: FilePreview[], paginationInfo: FilePaginationInfo, - accumulatedFilesCount: number + accumulatedFilesCount: number, + fileRepository: FileRepository, + datasetRepository: DatasetRepository ) { const [rowSelection, setRowSelection] = useState({}) const [selectedRowsModels, setSelectedRowsModels] = useState>>({}) @@ -38,7 +42,13 @@ export function useFilesTableScrollable( const table = useReactTable({ data: files, - columns: createColumnsDefinition(paginationInfo, fileSelection, accumulatedFilesCount), + columns: createColumnsDefinition( + paginationInfo, + fileSelection, + fileRepository, + datasetRepository, + accumulatedFilesCount + ), state: { rowSelection: rowSelection }, diff --git a/src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx b/src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx index 1b4da518a..338d37c18 100644 --- a/src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx +++ b/src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx @@ -4,13 +4,13 @@ import { Button, Col, QuestionMarkTooltip, Row, Spinner } from '@iqss/dataverse- import { useGetGuestbookById } from './useGetGuestbookById' import { PreviewGuestbookModal } from '@/sections/guestbooks/preview-modal/PreviewGuestbookModal' import { useDataset } from '@/sections/dataset/DatasetContext' -import { useGuestbookRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' +import { useGuestbookRepository } from '@/sections/guestbooks/GuestbookRepositoryContext' import styles from '@/sections/dataset/dataset-terms/DatasetTerms.module.scss' export const DatasetGuestbook = () => { const { t } = useTranslation('dataset') const { dataset } = useDataset() - const { guestbookRepository } = useGuestbookRepositories() + const guestbookRepository = useGuestbookRepository() const [showPreview, setShowPreview] = useState(false) const { guestbook, isLoadingGuestbook } = useGetGuestbookById({ guestbookRepository, diff --git a/src/sections/dataset/dataset-terms/DatasetTerms.tsx b/src/sections/dataset/dataset-terms/DatasetTerms.tsx index bbb957d5d..f5d823130 100644 --- a/src/sections/dataset/dataset-terms/DatasetTerms.tsx +++ b/src/sections/dataset/dataset-terms/DatasetTerms.tsx @@ -7,6 +7,7 @@ import { import { EditDatasetTermsButton } from '@/sections/dataset/dataset-terms/EditDatasetTermsButton' import { useTranslation } from 'react-i18next' import { useGetFilesCountInfo } from '@/sections/dataset/dataset-files/useGetFilesCountInfo' +import { FileRepository } from '@/files/domain/repositories/FileRepository' import { FileAccessCount } from '@/files/domain/models/FilesCountInfo' import { FileAccessOption } from '@/files/domain/models/FileCriteria' import { SpinnerSymbol } from '@/sections/dataset/dataset-files/files-table/spinner-symbol/SpinnerSymbol' @@ -15,11 +16,11 @@ import { TermsOfAccess } from '@/sections/dataset/dataset-terms/TermsOfAccess' import { License } from '@/sections/dataset/dataset-terms/License' import { DatasetGuestbook } from '@/sections/dataset/dataset-guestbook/DatasetGuestbook' import { useSearchParams } from 'react-router-dom' -import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface DatasetTermsProps { license: DatasetLicense | undefined termsOfUse: DatasetTermsOfUse + filesRepository: FileRepository datasetPersistentId: string datasetVersion: DatasetVersion canUpdateDataset?: boolean @@ -28,15 +29,15 @@ interface DatasetTermsProps { export function DatasetTerms({ license, termsOfUse, + filesRepository, datasetPersistentId, datasetVersion, canUpdateDataset }: DatasetTermsProps) { - const { fileRepository } = useDatasetRepositories() const { t } = useTranslation('dataset') const [searchParams] = useSearchParams() const { filesCountInfo, isLoading } = useGetFilesCountInfo({ - filesRepository: fileRepository, + filesRepository, datasetPersistentId, datasetVersion, includeDeaccessioned: canUpdateDataset diff --git a/src/sections/edit-dataset-terms/EditDatasetTerms.tsx b/src/sections/edit-dataset-terms/EditDatasetTerms.tsx index 65597bbbf..55c39a851 100644 --- a/src/sections/edit-dataset-terms/EditDatasetTerms.tsx +++ b/src/sections/edit-dataset-terms/EditDatasetTerms.tsx @@ -7,6 +7,7 @@ import { EditLicenseAndTerms } from './edit-license-and-terms/EditLicenseAndTerm import { EditTermsOfAccess } from './edit-terms-of-access/EditTermsOfAccess' import { LicenseRepository } from '../../licenses/domain/repositories/LicenseRepository' import { EditGuestbook } from './edit-guestbook/EditGuestbook' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { useDataset } from '../dataset/DatasetContext' import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator' import { NotFoundPage } from '../not-found-page/NotFoundPage' @@ -20,11 +21,13 @@ const tabsKeys = EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS interface EditDatasetTermsProps { defaultActiveTabKey: EditDatasetTermsTabKey licenseRepository: LicenseRepository + guestbookRepository: GuestbookRepository } export const EditDatasetTerms = ({ defaultActiveTabKey, - licenseRepository + licenseRepository, + guestbookRepository }: EditDatasetTermsProps) => { const { t } = useTranslation('dataset') const [activeKey, setActiveKey] = useState(defaultActiveTabKey) @@ -128,7 +131,10 @@ export const EditDatasetTerms = ({ {t('editTerms.tabs.guestbook')}
- +
@@ -154,7 +160,10 @@ export const EditDatasetTerms = ({
- +
diff --git a/src/sections/edit-dataset-terms/EditDatasetTermsFactory.tsx b/src/sections/edit-dataset-terms/EditDatasetTermsFactory.tsx index 8b7197ce6..4ce635b97 100644 --- a/src/sections/edit-dataset-terms/EditDatasetTermsFactory.tsx +++ b/src/sections/edit-dataset-terms/EditDatasetTermsFactory.tsx @@ -6,9 +6,11 @@ import { DatasetJSDataverseRepository } from '@/dataset/infrastructure/repositor import { DatasetProvider } from '../dataset/DatasetProvider' import { ReactElement } from 'react' import { DatasetNonNumericVersion } from '@/dataset/domain/models/Dataset' +import { GuestbookJSDataverseRepository } from '@/guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository' const licenseRepository = new LicenseJSDataverseRepository() const datasetRepository = new DatasetJSDataverseRepository() +const guestbookRepository = new GuestbookJSDataverseRepository() export class EditDatasetTermsFactory { static create(): ReactElement { @@ -30,6 +32,7 @@ function EditDatasetTermsWithSearchParams() { ) diff --git a/src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx b/src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx index 8f87175c5..d91e7185b 100644 --- a/src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx +++ b/src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx @@ -4,6 +4,7 @@ import { Trans, useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import { toast } from 'react-toastify' import { Guestbook } from '@/guestbooks/domain/models/Guestbook' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { DatasetNonNumericVersionSearchParam, DatasetPublishingStatus @@ -14,16 +15,19 @@ import { useAssignDatasetGuestbook } from './useAssignDatasetGuestbook' import { useRemoveDatasetGuestbook } from './useRemoveDatasetGuestbook' import { useDataset } from '../../dataset/DatasetContext' import { PreviewGuestbookModal } from '@/sections/guestbooks/preview-modal/PreviewGuestbookModal' -import { useGuestbookRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from './EditGuestbook.module.scss' interface EditGuestbookProps { + guestbookRepository: GuestbookRepository onPreview?: () => void onFormStateChange?: (isDirty: boolean) => void } -export function EditGuestbook({ onPreview, onFormStateChange }: EditGuestbookProps) { - const { guestbookRepository } = useGuestbookRepositories() +export function EditGuestbook({ + guestbookRepository, + onPreview, + onFormStateChange +}: EditGuestbookProps) { const { t } = useTranslation('dataset') const { t: tShared } = useTranslation('shared') const [selectedGuestbookId, setSelectedGuestbookId] = useState(undefined) diff --git a/src/sections/file/File.tsx b/src/sections/file/File.tsx index 74d8b954b..a19dfcc80 100644 --- a/src/sections/file/File.tsx +++ b/src/sections/file/File.tsx @@ -48,7 +48,7 @@ export function File({ const { setIsLoading } = useLoading() const { t } = useTranslation('file') const { file, isLoading } = useFile(repository, id, datasetVersionNumber) - const { externalTools } = useExternalTools() + const { externalTools, externalToolsRepository } = useExternalTools() const [activeTab, setActiveTab] = useState( toolTypeSelectedQueryParam && file?.permissions.canDownloadFile ? FilePageHelper.EXT_TOOL_TAB_KEY @@ -198,6 +198,7 @@ export function File({ applicableTools={fileApplicablePreviewOrQueryTools} toolTypeSelectedQueryParam={toolTypeSelectedQueryParam} isInView={activeTab === FilePageHelper.EXT_TOOL_TAB_KEY} + externalToolsRepository={externalToolsRepository} />
diff --git a/src/sections/file/FileFactory.tsx b/src/sections/file/FileFactory.tsx index f144fca2a..ff960825c 100644 --- a/src/sections/file/FileFactory.tsx +++ b/src/sections/file/FileFactory.tsx @@ -9,18 +9,23 @@ import { DataverseInfoJSDataverseRepository } from '@/info/infrastructure/reposi import { ContactJSDataverseRepository } from '@/contact/infrastructure/ContactJSDataverseRepository' import { AccessJSDataverseRepository } from '@/access/infrastructure/repositories/AccessJSDataverseRepository' import { AccessRepositoryProvider } from '../access/AccessRepositoryProvider' +import { GuestbookJSDataverseRepository } from '@/guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository' +import { GuestbookRepositoryProvider } from '../guestbooks/GuestbookRepositoryProvider' const repository = new FileJSDataverseRepository() const dataverseInfoRepository = new DataverseInfoJSDataverseRepository() const contactRepository = new ContactJSDataverseRepository() const accessRepository = new AccessJSDataverseRepository() +const guestbookRepository = new GuestbookJSDataverseRepository() export class FileFactory { static create(): ReactElement { return ( - - - + + + + + ) } } diff --git a/src/sections/file/file-action-buttons/access-file-menu/FileToolOptions.tsx b/src/sections/file/file-action-buttons/access-file-menu/FileToolOptions.tsx index 875021727..1b3adf353 100644 --- a/src/sections/file/file-action-buttons/access-file-menu/FileToolOptions.tsx +++ b/src/sections/file/file-action-buttons/access-file-menu/FileToolOptions.tsx @@ -9,9 +9,9 @@ import { import { DropdownButtonItem, DropdownHeader } from '@iqss/dataverse-design-system' import { useExternalTools } from '@/shared/contexts/external-tools/ExternalToolsProvider' import { getFileExternalToolResolved } from '@/externalTools/domain/useCases/GetFileExternalToolResolved' +import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' import { FilePageHelper } from '../../FilePageHelper' import { ExternalTool } from '@/externalTools/domain/models/ExternalTool' -import { useExternalToolsRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' type ToolKind = 'explore' | 'query' | 'configure' @@ -23,7 +23,8 @@ interface FileToolOptionsProps { const FileToolOptions = ({ fileId, fileType, kind }: FileToolOptionsProps) => { const { t } = useTranslation('shared') - const { fileExploreTools, fileQueryTools, fileConfigureTools } = useExternalTools() + const { fileExploreTools, fileQueryTools, fileConfigureTools, externalToolsRepository } = + useExternalTools() /** Per-kind config (single source of truth) */ const configByKind: Record< @@ -60,6 +61,7 @@ const FileToolOptions = ({ fileId, fileType, kind }: FileToolOptionsProps) => { toolId={tool.id} toolDisplayName={tool.displayName} fileId={fileId} + externalToolsRepository={externalToolsRepository} /> ))} @@ -70,10 +72,15 @@ interface ToolOptionProps { toolId: number toolDisplayName: string fileId: number + externalToolsRepository: ExternalToolsRepository } -const ToolOption = ({ toolId, toolDisplayName, fileId }: ToolOptionProps) => { - const { externalToolsRepository } = useExternalToolsRepositories() +const ToolOption = ({ + toolId, + toolDisplayName, + fileId, + externalToolsRepository +}: ToolOptionProps) => { const [isOpening, setIsOpening] = useState(false) const { t, i18n } = useTranslation('shared') const openingRef = useRef(false) diff --git a/src/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.tsx b/src/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.tsx index ddaced12f..c9c384660 100644 --- a/src/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.tsx +++ b/src/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.tsx @@ -5,12 +5,12 @@ import { WriteError } from '@iqss/dataverse-client-javascript' import { Alert, DropdownButton, DropdownButtonItem, Spinner } from '@iqss/dataverse-design-system' import { BoxArrowUpRight } from 'react-bootstrap-icons' import { ExternalTool } from '@/externalTools/domain/models/ExternalTool' +import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' import { FileExternalToolResolved } from '@/externalTools/domain/models/FileExternalToolResolved' import { getFileExternalToolResolved } from '@/externalTools/domain/useCases/GetFileExternalToolResolved' import { JSDataverseWriteErrorHandler } from '@/shared/helpers/JSDataverseWriteErrorHandler' import { File } from '@/files/domain/models/File' import { FilePageHelper } from '../FilePageHelper' -import { useExternalToolsRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from './FileEmbeddedExternalTool.module.scss' interface FileEmbeddedExternalToolProps { @@ -18,15 +18,16 @@ interface FileEmbeddedExternalToolProps { isInView: boolean applicableTools: ExternalTool[] toolTypeSelectedQueryParam: string | undefined + externalToolsRepository: ExternalToolsRepository } export const FileEmbeddedExternalTool = ({ file, isInView, applicableTools, - toolTypeSelectedQueryParam + toolTypeSelectedQueryParam, + externalToolsRepository }: FileEmbeddedExternalToolProps) => { - const { externalToolsRepository } = useExternalToolsRepositories() const { t, i18n } = useTranslation('file', { keyPrefix: 'previewTab' }) const [toolIdSelected, setToolIdSelected] = useState( FilePageHelper.getDefaultSelectedToolId(toolTypeSelectedQueryParam, applicableTools) diff --git a/src/sections/guestbooks/GuestbookRepositoryContext.ts b/src/sections/guestbooks/GuestbookRepositoryContext.ts new file mode 100644 index 000000000..09fae761f --- /dev/null +++ b/src/sections/guestbooks/GuestbookRepositoryContext.ts @@ -0,0 +1,9 @@ +import { createContext, useContext } from 'react' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' +import { GuestbookJSDataverseRepository } from '@/guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository' + +const guestbookRepository = new GuestbookJSDataverseRepository() + +export const GuestbookRepositoryContext = createContext(guestbookRepository) + +export const useGuestbookRepository = () => useContext(GuestbookRepositoryContext) diff --git a/src/sections/guestbooks/GuestbookRepositoryProvider.tsx b/src/sections/guestbooks/GuestbookRepositoryProvider.tsx new file mode 100644 index 000000000..f4b026d5f --- /dev/null +++ b/src/sections/guestbooks/GuestbookRepositoryProvider.tsx @@ -0,0 +1,18 @@ +import { PropsWithChildren } from 'react' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' +import { GuestbookRepositoryContext } from './GuestbookRepositoryContext' + +interface GuestbookRepositoryProviderProps { + repository: GuestbookRepository +} + +export function GuestbookRepositoryProvider({ + repository, + children +}: PropsWithChildren) { + return ( + + {children} + + ) +} diff --git a/src/sections/session/SessionProvider.tsx b/src/sections/session/SessionProvider.tsx index b1a776a9f..8e14aef36 100644 --- a/src/sections/session/SessionProvider.tsx +++ b/src/sections/session/SessionProvider.tsx @@ -5,15 +5,18 @@ import { ReadError } from '@iqss/dataverse-client-javascript' import { User } from '../../users/domain/models/User' import { SessionContext, SessionError } from './SessionContext' import { getUser } from '../../users/domain/useCases/getUser' +import { UserRepository } from '../../users/domain/repositories/UserRepository' import { JSDataverseReadErrorHandler } from '@/shared/helpers/JSDataverseReadErrorHandler' import { QueryParamKey, Route } from '../Route.enum' -import { useUserRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' export const BEARER_TOKEN_IS_VALID_BUT_NOT_LINKED_MESSAGE = 'Bearer token is validated, but there is no linked user account.' -export function SessionProvider() { - const { userRepository } = useUserRepositories() +interface SessionProviderProps { + repository: UserRepository +} + +export function SessionProvider({ repository }: SessionProviderProps) { const navigate = useNavigate() const { token, loginInProgress } = useContext(AuthContext) const [user, setUser] = useState(null) @@ -60,14 +63,14 @@ export function SessionProvider() { setIsLoadingUser(true) try { - const user = await getUser(userRepository) + const user = await getUser(repository) setUser(user) } catch (err) { handleFetchError(err) } finally { setIsLoadingUser(false) } - }, [userRepository, handleFetchError]) + }, [repository, handleFetchError]) const refetchUserSession = async () => { await fetchUser() diff --git a/src/sections/shared/pagination/PaginationControls.tsx b/src/sections/shared/pagination/PaginationControls.tsx index 26ec6e83f..52a79e564 100644 --- a/src/sections/shared/pagination/PaginationControls.tsx +++ b/src/sections/shared/pagination/PaginationControls.tsx @@ -18,30 +18,34 @@ export function PaginationControls>({ }: PaginationProps) { const [paginationInfo, setPaginationInfo] = useState(initialPaginationInfo) const goToPage = (newPage: number) => { - const updatedPaginationInfo = paginationInfo.goToPage(newPage) - setPaginationInfo(updatedPaginationInfo) - onPaginationInfoChange(updatedPaginationInfo) + setPaginationInfo(paginationInfo.goToPage(newPage)) } const goToPreviousPage = () => { - const updatedPaginationInfo = paginationInfo.goToPreviousPage() - setPaginationInfo(updatedPaginationInfo) - onPaginationInfoChange(updatedPaginationInfo) + setPaginationInfo(paginationInfo.goToPreviousPage()) } const goToNextPage = () => { - const updatedPaginationInfo = paginationInfo.goToNextPage() - setPaginationInfo(updatedPaginationInfo) - onPaginationInfoChange(updatedPaginationInfo) + setPaginationInfo(paginationInfo.goToNextPage()) } const setPageSize = (newPageSize: number) => { - const updatedPaginationInfo = paginationInfo.withPageSize(newPageSize) - setPaginationInfo(updatedPaginationInfo) - onPaginationInfoChange(updatedPaginationInfo) + setPaginationInfo(paginationInfo.withPageSize(newPageSize)) } useEffect(() => { - setPaginationInfo((currentPaginationInfo) => - currentPaginationInfo.withTotal(initialPaginationInfo.totalItems) - ) + onPaginationInfoChange(paginationInfo) + // TODO: Not a priority as not used for inifinite scroll is used but the eslint disable should be removed and the dependency should be added + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [paginationInfo.pageSize]) + + useEffect(() => { + onPaginationInfoChange(paginationInfo) + // TODO: Not a priority as not used for inifinite scroll is used but the eslint disable should be removed and the dependency should be added + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [paginationInfo.page]) + + useEffect(() => { + setPaginationInfo(paginationInfo.withTotal(initialPaginationInfo.totalItems)) + // TODO: Not a priority as not used for inifinite scroll is used but the eslint disable should be removed and the dependency should be added + // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialPaginationInfo.totalItems]) if (paginationInfo.totalPages < MINIMUM_NUMBER_OF_PAGES_TO_DISPLAY_PAGINATION) { diff --git a/src/sections/sign-up/SignUp.tsx b/src/sections/sign-up/SignUp.tsx index 7ad02a282..4d7e60f46 100644 --- a/src/sections/sign-up/SignUp.tsx +++ b/src/sections/sign-up/SignUp.tsx @@ -1,17 +1,20 @@ import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { Alert, Tabs } from '@iqss/dataverse-design-system' +import { UserRepository } from '@/users/domain/repositories/UserRepository' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' import { useLoading } from '../../shared/contexts/loading/LoadingContext' import { ValidTokenNotLinkedAccountForm } from './valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm' import styles from './SignUp.module.scss' interface SignUpProps { + userRepository: UserRepository dataverseInfoRepository: DataverseInfoRepository hasValidTokenButNotLinkedAccount: boolean } export const SignUp = ({ + userRepository, dataverseInfoRepository, hasValidTokenButNotLinkedAccount }: SignUpProps) => { @@ -61,7 +64,10 @@ export const SignUp = ({
{hasValidTokenButNotLinkedAccount && ( - + )}
diff --git a/src/sections/sign-up/SignUpFactory.tsx b/src/sections/sign-up/SignUpFactory.tsx index 3444d7fba..9fa9879c3 100644 --- a/src/sections/sign-up/SignUpFactory.tsx +++ b/src/sections/sign-up/SignUpFactory.tsx @@ -2,8 +2,10 @@ import { ReactElement } from 'react' import { useSearchParams } from 'react-router-dom' import { SignUp } from './SignUp' import { QueryParamKey } from '../Route.enum' +import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' import { DataverseInfoJSDataverseRepository } from '@/info/infrastructure/repositories/DataverseInfoJSDataverseRepository' +const userRepository = new UserJSDataverseRepository() const dataverseInfoRepository = new DataverseInfoJSDataverseRepository() export class SignUpFactory { @@ -20,6 +22,7 @@ function SignUpWithSearchParams() { return ( diff --git a/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx b/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx index 53a00bf88..84a3b22f7 100644 --- a/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx +++ b/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx @@ -3,20 +3,20 @@ import { AuthContext } from 'react-oauth2-code-pkce' import { Controller, FormProvider, useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { Alert, Button, Col, Form, Stack } from '@iqss/dataverse-design-system' +import { UserRepository } from '@/users/domain/repositories/UserRepository' import { Validator } from '@/shared/helpers/Validator' import { type ValidTokenNotLinkedAccountFormData } from './types' import { TermsOfUse } from '@/info/domain/models/TermsOfUse' import { SubmissionStatus, useSubmitUser } from './useSubmitUser' -import { useUserRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from './FormFields.module.scss' interface FormFieldsProps { + userRepository: UserRepository formDefaultValues: ValidTokenNotLinkedAccountFormData termsOfUse: TermsOfUse } -export const FormFields = ({ formDefaultValues, termsOfUse }: FormFieldsProps) => { - const { userRepository } = useUserRepositories() +export const FormFields = ({ userRepository, formDefaultValues, termsOfUse }: FormFieldsProps) => { const { tokenData, logOut } = useContext(AuthContext) const { t } = useTranslation('signUp') const { t: tShared } = useTranslation('shared') diff --git a/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx b/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx index 31dc27888..960047ada 100644 --- a/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx +++ b/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx @@ -1,6 +1,7 @@ import { useContext } from 'react' import { AuthContext } from 'react-oauth2-code-pkce' import { Alert } from '@iqss/dataverse-design-system' +import { UserRepository } from '@/users/domain/repositories/UserRepository' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' import { useGetTermsOfUse } from '@/shared/hooks/useGetTermsOfUse' import { OIDC_STANDARD_CLAIMS, type ValidTokenNotLinkedAccountFormData } from './types' @@ -9,10 +10,12 @@ import { FormFields } from './FormFields' import { FormFieldsSkeleton } from './FormFieldsSkeleton' interface ValidTokenNotLinkedAccountFormProps { + userRepository: UserRepository dataverseInfoRepository: DataverseInfoRepository } export const ValidTokenNotLinkedAccountForm = ({ + userRepository, dataverseInfoRepository }: ValidTokenNotLinkedAccountFormProps) => { const { tokenData } = useContext(AuthContext) @@ -69,5 +72,11 @@ export const ValidTokenNotLinkedAccountForm = ({ return {errorTermsOfUse} } - return + return ( + + ) } diff --git a/src/shared/contexts/external-tools/ExternalToolsProvider.tsx b/src/shared/contexts/external-tools/ExternalToolsProvider.tsx index e3337a120..88ce8ccda 100644 --- a/src/shared/contexts/external-tools/ExternalToolsProvider.tsx +++ b/src/shared/contexts/external-tools/ExternalToolsProvider.tsx @@ -1,9 +1,9 @@ import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react' import { ExternalTool, ToolScope, ToolType } from '@/externalTools/domain/models/ExternalTool' +import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' import { getExternalTools } from '@/externalTools/domain/useCases/GetExternalTools' import { ReadError } from '@iqss/dataverse-client-javascript' import { JSDataverseReadErrorHandler } from '@/shared/helpers/JSDataverseReadErrorHandler' -import { useExternalToolsRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' type ExternalToolsContextValue = { externalTools: ExternalTool[] @@ -16,16 +16,20 @@ type ExternalToolsContextValue = { filePreviewTools: ExternalTool[] fileQueryTools: ExternalTool[] fileConfigureTools: ExternalTool[] + externalToolsRepository: ExternalToolsRepository } const ExternalToolsContext = createContext(undefined) type ExternalToolsProviderProps = { + externalToolsRepository: ExternalToolsRepository children: React.ReactNode } -export function ExternalToolsProvider({ children }: ExternalToolsProviderProps) { - const { externalToolsRepository } = useExternalToolsRepositories() +export function ExternalToolsProvider({ + externalToolsRepository, + children +}: ExternalToolsProviderProps) { const [externalTools, setExternalTools] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) @@ -107,7 +111,8 @@ export function ExternalToolsProvider({ children }: ExternalToolsProviderProps) filePreviewTools, fileQueryTools, fileConfigureTools, - refreshExternalTools: fetchExternalTools + refreshExternalTools: fetchExternalTools, + externalToolsRepository }), [ externalTools, @@ -119,7 +124,8 @@ export function ExternalToolsProvider({ children }: ExternalToolsProviderProps) filePreviewTools, fileQueryTools, fileConfigureTools, - fetchExternalTools + fetchExternalTools, + externalToolsRepository ] ) diff --git a/src/shared/contexts/repositories/RepositoriesProvider.tsx b/src/shared/contexts/repositories/RepositoriesProvider.tsx index 6b827e426..0141e274f 100644 --- a/src/shared/contexts/repositories/RepositoriesProvider.tsx +++ b/src/shared/contexts/repositories/RepositoriesProvider.tsx @@ -1,18 +1,10 @@ import React, { createContext, useContext, useMemo } from 'react' import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' -import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' -import { FileRepository } from '@/files/domain/repositories/FileRepository' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { UserRepository } from '@/users/domain/repositories/UserRepository' export interface RepositoriesContextValue { collectionRepository: CollectionRepository datasetRepository: DatasetRepository - externalToolsRepository: ExternalToolsRepository - fileRepository: FileRepository - guestbookRepository: GuestbookRepository - userRepository: UserRepository } const RepositoriesContext = createContext(undefined) @@ -24,29 +16,14 @@ interface RepositoriesProviderProps extends RepositoriesContextValue { export function RepositoriesProvider({ children, collectionRepository, - datasetRepository, - externalToolsRepository, - fileRepository, - guestbookRepository, - userRepository + datasetRepository }: RepositoriesProviderProps) { const value = useMemo( () => ({ collectionRepository, - datasetRepository, - externalToolsRepository, - fileRepository, - guestbookRepository, - userRepository + datasetRepository }), - [ - collectionRepository, - datasetRepository, - externalToolsRepository, - fileRepository, - guestbookRepository, - userRepository - ] + [collectionRepository, datasetRepository] ) return {children} @@ -69,31 +46,7 @@ export function useCollectionRepositories() { } export function useDatasetRepositories() { - const { datasetRepository, fileRepository } = useRepositories() - - return { datasetRepository, fileRepository } -} - -export function useExternalToolsRepositories() { - const { externalToolsRepository } = useRepositories() - - return { externalToolsRepository } -} - -export function useFileRepositories() { - const { fileRepository } = useRepositories() - - return { fileRepository } -} - -export function useUserRepositories() { - const { userRepository } = useRepositories() - - return { userRepository } -} - -export function useGuestbookRepositories() { - const { guestbookRepository } = useRepositories() + const { datasetRepository } = useRepositories() - return { guestbookRepository } + return { datasetRepository } } diff --git a/src/stories/WithRepositories.tsx b/src/stories/WithRepositories.tsx index bb08ac7a5..a12ba61fa 100644 --- a/src/stories/WithRepositories.tsx +++ b/src/stories/WithRepositories.tsx @@ -2,10 +2,6 @@ import { StoryFn } from '@storybook/react' import { ReactNode } from 'react' import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' -import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' -import { FileRepository } from '@/files/domain/repositories/FileRepository' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { UserRepository } from '@/users/domain/repositories/UserRepository' import { RepositoriesProvider } from '@/shared/contexts/repositories/RepositoriesProvider' function failFastRepository(name: string): T { @@ -25,29 +21,17 @@ function failFastRepository(name: string): T { interface WithRepositoriesProps { collectionRepository?: CollectionRepository datasetRepository?: DatasetRepository - externalToolsRepository?: ExternalToolsRepository - fileRepository?: FileRepository - guestbookRepository?: GuestbookRepository - userRepository?: UserRepository } export function WithRepositories({ collectionRepository = failFastRepository('CollectionRepository'), - datasetRepository = failFastRepository('DatasetRepository'), - externalToolsRepository = failFastRepository('ExternalToolsRepository'), - fileRepository = failFastRepository('FileRepository'), - guestbookRepository = failFastRepository('GuestbookRepository'), - userRepository = failFastRepository('UserRepository') + datasetRepository = failFastRepository('DatasetRepository') }: WithRepositoriesProps) { function WithRepositoriesDecorator(Story: StoryFn) { return ( + datasetRepository={datasetRepository}> ) @@ -65,20 +49,12 @@ interface RepositoriesStoryProviderProps extends WithRepositoriesProps { export function RepositoriesStoryProvider({ children, collectionRepository = failFastRepository('CollectionRepository'), - datasetRepository = failFastRepository('DatasetRepository'), - externalToolsRepository = failFastRepository('ExternalToolsRepository'), - fileRepository = failFastRepository('FileRepository'), - guestbookRepository = failFastRepository('GuestbookRepository'), - userRepository = failFastRepository('UserRepository') + datasetRepository = failFastRepository('DatasetRepository') }: RepositoriesStoryProviderProps) { return ( + datasetRepository={datasetRepository}> {children} ) diff --git a/src/stories/account/Account.stories.tsx b/src/stories/account/Account.stories.tsx index 0cfb3c4a0..566a0c416 100644 --- a/src/stories/account/Account.stories.tsx +++ b/src/stories/account/Account.stories.tsx @@ -25,11 +25,10 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + diff --git a/src/stories/account/account-info-section/AccountInfoSection.stories.tsx b/src/stories/account/account-info-section/AccountInfoSection.stories.tsx index 79d071ce4..3e3499c2f 100644 --- a/src/stories/account/account-info-section/AccountInfoSection.stories.tsx +++ b/src/stories/account/account-info-section/AccountInfoSection.stories.tsx @@ -25,11 +25,10 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + diff --git a/src/stories/account/api-token-section/ApiTokenSection.stories.tsx b/src/stories/account/api-token-section/ApiTokenSection.stories.tsx index 764e470d8..755710008 100644 --- a/src/stories/account/api-token-section/ApiTokenSection.stories.tsx +++ b/src/stories/account/api-token-section/ApiTokenSection.stories.tsx @@ -27,11 +27,10 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + @@ -41,11 +40,10 @@ export const Default: Story = { export const Loading: Story = { render: () => ( - + @@ -55,11 +53,10 @@ export const Loading: Story = { export const Error: Story = { render: () => ( - + @@ -82,11 +79,10 @@ export const NoToken: Story = { } return ( - + diff --git a/src/stories/account/notification-section/NotificationSection.stories.tsx b/src/stories/account/notification-section/NotificationSection.stories.tsx index eeac2f82f..b0810be73 100644 --- a/src/stories/account/notification-section/NotificationSection.stories.tsx +++ b/src/stories/account/notification-section/NotificationSection.stories.tsx @@ -27,11 +27,10 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + @@ -40,11 +39,10 @@ export const Default: Story = { } export const Error: Story = { render: () => ( - + diff --git a/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx b/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx index fb0def6ee..b39a6c1ab 100644 --- a/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx +++ b/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx @@ -2,27 +2,7 @@ import { Meta, StoryObj } from '@storybook/react' import { WithI18next } from '../../WithI18next' import { CollectionCard } from '@/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard' import { CollectionItemTypePreviewMother } from '../../../../tests/component/collection/domain/models/CollectionItemTypePreviewMother' - -const collectionPreview = CollectionItemTypePreviewMother.createRealistic() -const longDescriptionCollectionPreview = CollectionItemTypePreviewMother.create({ - ...collectionPreview, - description: 'Scientific research collection with a detailed public description. ' - .repeat(20) - .trim() -}) -const unpublishedCollectionPreview = CollectionItemTypePreviewMother.create({ - ...collectionPreview, - isReleased: false -}) -const thumbnailCollectionPreview = CollectionItemTypePreviewMother.create({ - ...collectionPreview, - thumbnail: - 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2264%22 height=%2248%22 viewBox=%220 0 64 48%22%3E%3Crect width=%2264%22 height=%2248%22 fill=%22%23f1f5f9%22/%3E%3Cpath d=%22M10 34l12-12 9 9 8-8 15 15H10z%22 fill=%22%235b728a%22/%3E%3Ccircle cx=%2246%22 cy=%2214%22 r=%226%22 fill=%22%23f9c74f%22/%3E%3C/svg%3E' -}) -const linkedCollectionPreview = CollectionItemTypePreviewMother.create({ - ...collectionPreview, - isLinked: true -}) +import { FakerHelper } from '../../../../tests/component/shared/FakerHelper' const meta: Meta = { title: 'Sections/Collection Page/CollectionCard', @@ -34,25 +14,31 @@ export default meta type Story = StoryObj export const Default: Story = { - render: () => ( - - ) -} - -export const WithLongDescription: Story = { render: () => ( ) } +export const WithLongDescription: Story = { + render: () => { + const collectionPreview = CollectionItemTypePreviewMother.create({ + name: 'Scientific Research Collection', + description: FakerHelper.paragraph(20) + }) + return ( + + ) + } +} + export const Unpublished: Story = { render: () => ( ) } @@ -61,7 +47,7 @@ export const WithThumbnail: Story = { render: () => ( ) } @@ -70,7 +56,7 @@ export const Linked: Story = { render: () => ( ) } diff --git a/src/stories/dataset/Dataset.stories.tsx b/src/stories/dataset/Dataset.stories.tsx index 75aa80eaa..d121e9c12 100644 --- a/src/stories/dataset/Dataset.stories.tsx +++ b/src/stories/dataset/Dataset.stories.tsx @@ -23,6 +23,7 @@ import { CollectionMockRepository } from '@/stories/collection/CollectionMockRep import { ContactMockRepository } from '../shared-mock-repositories/contact/ContactMockRepository' import { DataverseInfoMockRepository } from '../shared-mock-repositories/info/DataverseInfoMockRepository' import { GuestbookMockRepository } from '../shared-mock-repositories/guestbook/GuestbookMockRepository' +import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { DatasetProvider } from '@/sections/dataset/DatasetProvider' import { RepositoriesStoryProvider, WithRepositories } from '@/stories/WithRepositories' @@ -54,11 +55,13 @@ const WithDatasetGuestbook = (Story: () => JSX.Element) => { const datasetRepository = new DatasetWithGuestbookMockRepository() return ( - - - + + + + + ) } @@ -69,10 +72,9 @@ export const Default: Story = { render: () => ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetWithReviewsMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> ( + datasetRepository={new DatasetMockRepository()}> = { title: 'Sections/Dataset Page/DatasetActionButtons/AccessDatasetMenu', @@ -66,20 +65,18 @@ export const WithTabularFiles: Story = { export const WithExploreOptionsTools: Story = { render: () => ( - - - - - + + + ) } diff --git a/src/stories/dataset/dataset-files/DatasetFiles.stories.tsx b/src/stories/dataset/dataset-files/DatasetFiles.stories.tsx index 0b083b805..be915cd6e 100644 --- a/src/stories/dataset/dataset-files/DatasetFiles.stories.tsx +++ b/src/stories/dataset/dataset-files/DatasetFiles.stories.tsx @@ -8,12 +8,16 @@ import { WithSettings } from '../../WithSettings' import { FileMockNoFiltersRepository } from '../../file/FileMockNoFiltersRepository' import { DatasetMother } from '../../../../tests/component/dataset/domain/models/DatasetMother' import { DatasetMockRepository } from '../../dataset/DatasetMockRepository' -import { RepositoriesStoryProvider } from '../../WithRepositories' +import { WithRepositories } from '../../WithRepositories' const meta: Meta = { title: 'Sections/Dataset Page/DatasetFiles', component: DatasetFiles, - decorators: [WithI18next, WithSettings] + decorators: [ + WithI18next, + WithSettings, + WithRepositories({ datasetRepository: new DatasetMockRepository() }) + ] } export default meta @@ -23,52 +27,40 @@ const testDataset = DatasetMother.createRealistic() export const Default: Story = { render: () => ( - - - + ) } export const Loading: Story = { render: () => ( - - - + ) } export const NoFiles: Story = { render: () => ( - - - + ) } export const NoFilters: Story = { render: () => ( - - - + ) } diff --git a/src/stories/dataset/dataset-files/DatasetFilesScrollable.stories.tsx b/src/stories/dataset/dataset-files/DatasetFilesScrollable.stories.tsx index cad78c7f8..564277756 100644 --- a/src/stories/dataset/dataset-files/DatasetFilesScrollable.stories.tsx +++ b/src/stories/dataset/dataset-files/DatasetFilesScrollable.stories.tsx @@ -8,12 +8,16 @@ import { WithSettings } from '../../WithSettings' import { FileMockNoFiltersRepository } from '../../file/FileMockNoFiltersRepository' import { DatasetMother } from '../../../../tests/component/dataset/domain/models/DatasetMother' import { DatasetMockRepository } from '../../dataset/DatasetMockRepository' -import { RepositoriesStoryProvider } from '../../WithRepositories' +import { WithRepositories } from '../../WithRepositories' const meta: Meta = { title: 'Sections/Dataset Page/DatasetFilesScrollable', component: DatasetFilesScrollable, - decorators: [WithI18next, WithSettings], + decorators: [ + WithI18next, + WithSettings, + WithRepositories({ datasetRepository: new DatasetMockRepository() }) + ], parameters: { // Sets the delay for all stories. chromatic: { delay: 15000, pauseAnimationAtEnd: true } @@ -27,52 +31,40 @@ const testDataset = DatasetMother.createRealistic() export const Default: Story = { render: () => ( - - - + ) } export const Loading: Story = { render: () => ( - - - + ) } export const NoFiles: Story = { render: () => ( - - - + ) } export const NoFilters: Story = { render: () => ( - - - + ) } diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx index 054a7783f..27d8ad7e6 100644 --- a/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx +++ b/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx @@ -17,10 +17,7 @@ const meta: Meta = { WithSettings, WithLoggedInUser, WithDatasetAllPermissionsGranted, - WithRepositories({ - datasetRepository: new DatasetMockRepository(), - fileRepository: new FileMockRepository() - }) + WithRepositories({ datasetRepository: new DatasetMockRepository() }) ] } @@ -28,5 +25,11 @@ export default meta type Story = StoryObj export const Default: Story = { - render: () => + render: () => ( + + ) } diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/file-options-menu/FileOptionsMenu.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/file-options-menu/FileOptionsMenu.stories.tsx index 7fcd85395..723ccef8b 100644 --- a/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/file-options-menu/FileOptionsMenu.stories.tsx +++ b/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/file-options-menu/FileOptionsMenu.stories.tsx @@ -12,7 +12,7 @@ import { ExternalToolsProvider } from '@/shared/contexts/external-tools/External import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' import { FakerHelper } from '@tests/component/shared/FakerHelper' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' -import { RepositoriesStoryProvider, WithRepositories } from '@/stories/WithRepositories' +import { WithRepositories } from '@/stories/WithRepositories' const meta: Meta = { title: @@ -22,10 +22,7 @@ const meta: Meta = { WithI18next, WithSettings, WithLoggedInUser, - WithRepositories({ - datasetRepository: new DatasetMockRepository(), - fileRepository: new FileMockRepository() - }) + WithRepositories({ datasetRepository: new DatasetMockRepository() }) ] } @@ -34,22 +31,42 @@ type Story = StoryObj export const DefaultWithLoggedInUser: Story = { decorators: [WithDatasetAllPermissionsGranted], - render: () => + render: () => ( + + ) } export const Restricted: Story = { decorators: [WithDatasetAllPermissionsGranted], - render: () => + render: () => ( + + ) } export const WithDatasetLocked: Story = { decorators: [WithDatasetLockedFromEdits], - render: () => + render: () => ( + + ) } export const WithFileAlreadyDeleted: Story = { decorators: [WithDatasetAllPermissionsGranted], - render: () => + render: () => ( + + ) } const externalToolsRepositoryWithFileConfigureTool = new ExternalToolsMockRepository() @@ -64,12 +81,12 @@ externalToolsRepositoryWithFileConfigureTool.getExternalTools = () => { export const WithConfigureTool: Story = { decorators: [WithDatasetAllPermissionsGranted], render: () => ( - - - - - + + + ) } diff --git a/src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx b/src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx index be0f67f8a..560252471 100644 --- a/src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx +++ b/src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx @@ -3,11 +3,11 @@ import { DatasetGuestbook } from '@/sections/dataset/dataset-guestbook/DatasetGu import { WithI18next } from '@/stories/WithI18next' import { DatasetContext } from '@/sections/dataset/DatasetContext' import { DatasetMother } from '@tests/component/dataset/domain/models/DatasetMother' +import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' import { GuestbookMockRepository, storybookGuestbook } from '@/stories/shared-mock-repositories/guestbook/GuestbookMockRepository' -import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Sections/Dataset Page/DatasetTerms/DatasetGuestbook', @@ -22,7 +22,7 @@ const guestbookRepository = new GuestbookMockRepository() const withDatasetContext = (datasetWithGuestbook: boolean) => { const DatasetGuestbookStoryDecorator = (StoryComponent: () => JSX.Element) => ( - + { }}> - + ) DatasetGuestbookStoryDecorator.displayName = `DatasetGuestbookStoryDecorator-${String( diff --git a/src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx b/src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx index 857ffe4cc..8cfa7409f 100644 --- a/src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx +++ b/src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx @@ -13,8 +13,7 @@ import { storybookGuestbook } from '@/stories/shared-mock-repositories/guestbook/GuestbookMockRepository' import { DatasetContext } from '@/sections/dataset/DatasetContext' -import { RepositoriesStoryProvider } from '@/stories/WithRepositories' -import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' const meta: Meta = { title: 'Sections/Dataset Page/DatasetTerms', @@ -36,39 +35,29 @@ const guestbookRepository = new GuestbookMockRepository() const withDatasetContext = (dataset = testDatasetWithGuestbook) => { const DatasetTermsStoryDecorator = (Story: () => JSX.Element) => ( - {} - }}> - - + + {} + }}> + + + ) DatasetTermsStoryDecorator.displayName = 'DatasetTermsStoryDecorator' return DatasetTermsStoryDecorator } -const withFileRepository = (fileRepository: FileRepository) => { - const DatasetTermsFileRepositoryStoryDecorator = (Story: () => JSX.Element) => ( - - - - ) - - DatasetTermsFileRepositoryStoryDecorator.displayName = 'DatasetTermsFileRepositoryStoryDecorator' - return DatasetTermsFileRepositoryStoryDecorator -} - export const Default: Story = { - decorators: [withDatasetContext(), withFileRepository(new FileMockRepository())], + decorators: [withDatasetContext()], render: () => ( @@ -76,11 +65,11 @@ export const Default: Story = { } export const Loading: Story = { - decorators: [withFileRepository(new FileMockLoadingRepository())], render: () => ( @@ -88,11 +77,12 @@ export const Loading: Story = { } export const RestrictedFiles: Story = { - decorators: [withDatasetContext(), withFileRepository(new FileMockRestrictedFilesRepository())], + decorators: [withDatasetContext()], render: () => ( @@ -100,22 +90,24 @@ export const RestrictedFiles: Story = { } export const NoRestrictedFiles: Story = { - decorators: [withDatasetContext(), withFileRepository(new FileMockNoRestrictedFilesRepository())], + decorators: [withDatasetContext()], render: () => ( ) } export const CustomTerms: Story = { - decorators: [withDatasetContext(), withFileRepository(new FileMockNoRestrictedFilesRepository())], + decorators: [withDatasetContext()], render: () => ( @@ -123,14 +115,12 @@ export const CustomTerms: Story = { } export const WithoutAssignedGuestbook: Story = { - decorators: [ - withDatasetContext(DatasetMother.createRealistic({ guestbookId: undefined })), - withFileRepository(new FileMockNoRestrictedFilesRepository()) - ], + decorators: [withDatasetContext(DatasetMother.createRealistic({ guestbookId: undefined }))], render: () => ( @@ -138,14 +128,12 @@ export const WithoutAssignedGuestbook: Story = { } export const GuestbookEmptyState: Story = { - decorators: [ - withDatasetContext(DatasetMother.createRealistic({ guestbookId: undefined })), - withFileRepository(new FileMockNoRestrictedFilesRepository()) - ], + decorators: [withDatasetContext(DatasetMother.createRealistic({ guestbookId: undefined }))], render: () => ( diff --git a/src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx b/src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx index 4382b0468..04ae10a96 100644 --- a/src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx +++ b/src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx @@ -20,12 +20,7 @@ const guestbookRepository: GuestbookRepository = const meta: Meta = { title: 'Pages/EditDatasetTerms', component: EditDatasetTerms, - decorators: [ - WithI18next, - WithLayout, - WithDataset, - WithRepositories({ datasetRepository, guestbookRepository }) - ], + decorators: [WithI18next, WithLayout, WithDataset, WithRepositories({ datasetRepository })], parameters: { chromatic: { delay: 15000, pauseAnimationAtEnd: true } } @@ -39,6 +34,7 @@ export const EditLicenseAndTermsTab: Story = { ) } @@ -48,6 +44,7 @@ export const EditTermsOfAccessTab: Story = { ) } @@ -57,6 +54,7 @@ export const EditGuestbookTab: Story = { ) } diff --git a/src/stories/file/File.stories.tsx b/src/stories/file/File.stories.tsx index 0ef7514cf..1d3b0eda0 100644 --- a/src/stories/file/File.stories.tsx +++ b/src/stories/file/File.stories.tsx @@ -13,7 +13,7 @@ import { FakerHelper } from '@tests/component/shared/FakerHelper' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { DataverseInfoMockRepository } from '../shared-mock-repositories/info/DataverseInfoMockRepository' import { ContactMockRepository } from '../shared-mock-repositories/contact/ContactMockRepository' -import { RepositoriesStoryProvider, WithRepositories } from '../WithRepositories' +import { WithRepositories } from '../WithRepositories' const meta: Meta = { title: 'Pages/File', @@ -89,17 +89,15 @@ export const FileNotFound: Story = { export const WithMultipleExternalTools: Story = { render: () => ( - - - - - + + + ) } @@ -114,17 +112,15 @@ externalToolsRepositoryOnlyPreviewTool.getExternalTools = () => { export const WithOnlyOnePreviewExternalTool: Story = { render: () => ( - - - - - + + + ) } @@ -139,16 +135,14 @@ externalToolsRepositoryOnlyQueryTool.getExternalTools = () => { export const WithOnlyOneQueryExternalTool: Story = { render: () => ( - - - - - + + + ) } diff --git a/src/stories/file/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx b/src/stories/file/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx index da98c451d..f55904ea0 100644 --- a/src/stories/file/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx +++ b/src/stories/file/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx @@ -6,7 +6,6 @@ import { FileAccessMother } from '../../../../../tests/component/files/domain/mo import { FileMetadataMother } from '../../../../../tests/component/files/domain/models/FileMetadataMother' import { ExternalToolsProvider } from '@/shared/contexts/external-tools/ExternalToolsProvider' import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' -import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Sections/File Page/Action Buttons/AccessFileMenu', @@ -163,17 +162,15 @@ export const WithEmbargoAndRestrictedWithAccessGranted: Story = { export const WithExploreAndQueryOptionsTools: Story = { render: () => ( - - - - - + + + ) } diff --git a/src/stories/file/file-action-buttons/edit-file-dropdown/EditFileDropdown.stories.tsx b/src/stories/file/file-action-buttons/edit-file-dropdown/EditFileDropdown.stories.tsx index f80878c5d..cd19ea437 100644 --- a/src/stories/file/file-action-buttons/edit-file-dropdown/EditFileDropdown.stories.tsx +++ b/src/stories/file/file-action-buttons/edit-file-dropdown/EditFileDropdown.stories.tsx @@ -9,7 +9,7 @@ import { ExternalToolsProvider } from '@/shared/contexts/external-tools/External import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { FakerHelper } from '@tests/component/shared/FakerHelper' -import { RepositoriesStoryProvider, WithRepositories } from '@/stories/WithRepositories' +import { WithRepositories } from '@/stories/WithRepositories' const storyFile = FileMother.createRealistic() @@ -56,24 +56,21 @@ externalToolsRepositoryWithFileConfigureTool.getExternalTools = () => { export const WithConfigureToolOption: Story = { render: () => ( - - - - - + + + ) } diff --git a/src/stories/file/file-embedded-external-tool/FileEmbeddedExternalTool.stories.tsx b/src/stories/file/file-embedded-external-tool/FileEmbeddedExternalTool.stories.tsx index 47bda11fd..dcc339520 100644 --- a/src/stories/file/file-embedded-external-tool/FileEmbeddedExternalTool.stories.tsx +++ b/src/stories/file/file-embedded-external-tool/FileEmbeddedExternalTool.stories.tsx @@ -4,7 +4,6 @@ import { FileEmbeddedExternalTool } from '@/sections/file/file-embedded-external import { FileMother } from '@tests/component/files/domain/models/FileMother' import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' -import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Sections/File Page/File External Tools Tab', @@ -20,29 +19,27 @@ const externalToolsRepository = new ExternalToolsMockRepository() export const WithOneToolOnly: Story = { render: () => ( - - - + ) } export const WithMoreThanOneTool: Story = { render: () => ( - - - + ) } diff --git a/src/stories/guestbooks/guestbook-applied-modal/DownloadWithTermsAndGuestbookModal.stories.tsx b/src/stories/guestbooks/guestbook-applied-modal/DownloadWithTermsAndGuestbookModal.stories.tsx index c7bbf54cb..1d1dfb810 100644 --- a/src/stories/guestbooks/guestbook-applied-modal/DownloadWithTermsAndGuestbookModal.stories.tsx +++ b/src/stories/guestbooks/guestbook-applied-modal/DownloadWithTermsAndGuestbookModal.stories.tsx @@ -10,7 +10,7 @@ import { GuestbookResponseDTO } from '@/access/domain/repositories/AccessRepository' import { AccessRepositoryProvider } from '@/sections/access/AccessRepositoryProvider' -import { RepositoriesStoryProvider } from '@/stories/WithRepositories' +import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' const accessRepository: AccessRepository = { submitGuestbookForDatasetDownload: ( @@ -32,7 +32,7 @@ const meta: Meta = { WithI18next, WithLoggedInUser, (Story) => ( - + = { - + ) ] } diff --git a/src/stories/sign-up/SignUp.stories.tsx b/src/stories/sign-up/SignUp.stories.tsx index fd2253efc..b81684631 100644 --- a/src/stories/sign-up/SignUp.stories.tsx +++ b/src/stories/sign-up/SignUp.stories.tsx @@ -7,7 +7,6 @@ import { UserMockRepository } from '../shared-mock-repositories/user/UserMockRep import { DataverseInfoMockRepository } from '../shared-mock-repositories/info/DataverseInfoMockRepository' import { DataverseInfoMockLoadingRepository } from '../shared-mock-repositories/info/DataverseInfoMockLoadingkRepository' import { DataverseInfoMockErrorRepository } from '../shared-mock-repositories/info/DataverseInfoMockErrorRepository' -import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Pages/Sign Up', @@ -23,33 +22,30 @@ type Story = StoryObj export const ValidTokenWithNotLinkedAccount: Story = { render: () => ( - - - + ) } export const LoadingTermsOfUse: Story = { render: () => ( - - - + ) } export const FailedToFetchTermsOfUse: Story = { render: () => ( - - - + ) } diff --git a/tests/component/WithRepositories.tsx b/tests/component/WithRepositories.tsx index a8fa94e62..ceeb129c5 100644 --- a/tests/component/WithRepositories.tsx +++ b/tests/component/WithRepositories.tsx @@ -1,10 +1,6 @@ import { ReactNode } from 'react' import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' -import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' -import { FileRepository } from '@/files/domain/repositories/FileRepository' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { UserRepository } from '@/users/domain/repositories/UserRepository' import { RepositoriesProvider } from '@/shared/contexts/repositories/RepositoriesProvider' function failFastRepository(name: string): T { @@ -25,29 +21,17 @@ interface WithRepositoriesProps { children: ReactNode collectionRepository?: CollectionRepository datasetRepository?: DatasetRepository - externalToolsRepository?: ExternalToolsRepository - fileRepository?: FileRepository - guestbookRepository?: GuestbookRepository - userRepository?: UserRepository } export function WithRepositories({ children, collectionRepository = failFastRepository('CollectionRepository'), - datasetRepository = failFastRepository('DatasetRepository'), - externalToolsRepository = failFastRepository('ExternalToolsRepository'), - fileRepository = failFastRepository('FileRepository'), - guestbookRepository = failFastRepository('GuestbookRepository'), - userRepository = failFastRepository('UserRepository') + datasetRepository = failFastRepository('DatasetRepository') }: WithRepositoriesProps) { return ( + datasetRepository={datasetRepository}> {children} ) diff --git a/tests/component/sections/account/Account.spec.tsx b/tests/component/sections/account/Account.spec.tsx index 03b104c36..3661ca81a 100644 --- a/tests/component/sections/account/Account.spec.tsx +++ b/tests/component/sections/account/Account.spec.tsx @@ -1,5 +1,6 @@ import { Account } from '../../../../src/sections/account/Account' import { AccountHelper } from '../../../../src/sections/account/AccountHelper' +import { UserJSDataverseRepository } from '../../../../src/users/infrastructure/repositories/UserJSDataverseRepository' import { CollectionMockRepository } from '@/stories/collection/CollectionMockRepository' import { RoleMockRepository } from '@/stories/account/RoleMockRepository' import { NotificationType } from '@/notifications/domain/models/Notification' @@ -54,6 +55,7 @@ describe('Account', () => { @@ -72,6 +74,7 @@ describe('Account', () => { diff --git a/tests/component/sections/account/ApiTokenSection.spec.tsx b/tests/component/sections/account/ApiTokenSection.spec.tsx index c82923355..e6c9418d0 100644 --- a/tests/component/sections/account/ApiTokenSection.spec.tsx +++ b/tests/component/sections/account/ApiTokenSection.spec.tsx @@ -1,7 +1,6 @@ import { ApiTokenSection } from '../../../../src/sections/account/api-token-section/ApiTokenSection' import { DateHelper } from '@/shared/helpers/DateHelper' import { UserRepository } from '@/users/domain/repositories/UserRepository' -import { WithRepositories } from '@tests/component/WithRepositories' describe('ApiTokenSection', () => { const mockApiTokenInfo = { @@ -15,12 +14,6 @@ describe('ApiTokenSection', () => { let userRepository: UserRepository - const ApiTokenSectionWithRepositories = () => ( - - - - ) - beforeEach(() => { userRepository = { getCurrentApiToken: cy.stub().resolves(mockApiTokenInfo), @@ -30,7 +23,7 @@ describe('ApiTokenSection', () => { register: cy.stub().resolves() } - cy.mountAuthenticated() + cy.mountAuthenticated() }) it('should show the loading skeleton while fetching the token', () => { @@ -38,7 +31,7 @@ describe('ApiTokenSection', () => { return Cypress.Promise.delay(500).then(() => mockApiTokenInfo) }) - cy.mount() + cy.mount() cy.get('[data-testid="loadingSkeleton"]').should('exist') cy.wait(500) @@ -88,7 +81,7 @@ describe('ApiTokenSection', () => { it('should show error message when failing to fetch the current API token', () => { userRepository.getCurrentApiToken = cy.stub().rejects(new Error('Failed to fetch API token')) - cy.mountAuthenticated() + cy.mountAuthenticated() cy.findByText(/Failed to fetch API token/).should('exist') }) diff --git a/tests/component/sections/dataset/Dataset.spec.tsx b/tests/component/sections/dataset/Dataset.spec.tsx index 9e2c5b6ec..152671a2a 100644 --- a/tests/component/sections/dataset/Dataset.spec.tsx +++ b/tests/component/sections/dataset/Dataset.spec.tsx @@ -1,6 +1,5 @@ import { DatasetRepository } from '../../../../src/dataset/domain/repositories/DatasetRepository' import { Dataset } from '../../../../src/sections/dataset/Dataset' -import { TabsSkeleton } from '../../../../src/sections/dataset/DatasetSkeleton' import { DatasetMother, DatasetPermissionsMother } from '../../dataset/domain/models/DatasetMother' import { LoadingProvider } from '../../../../src/shared/contexts/loading/LoadingProvider' import { @@ -30,8 +29,6 @@ import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInf import { DatasetMetadataExportFormatsMother } from '@tests/component/info/domain/models/DatasetMetadataExportFormatsMother' import { WithRepositories } from '@tests/component/WithRepositories' import { DatasetReview } from '@/dataset/domain/models/DatasetReview' -import { useLocation } from 'react-router-dom' -import { TermsOfUseMother } from '@tests/component/dataset/domain/models/TermsOfUseMother' const setAnonymizedView = () => {} const fileRepository: FileRepository = {} as FileRepository @@ -156,11 +153,6 @@ const versionSummaryInfo: DatasetVersionSummaryInfo[] = [ const testDatasetMetadataExportFormats = DatasetMetadataExportFormatsMother.create() const termsTabLabelRegex = /^Terms(?: and Guestbook)?$/ -function LocationDisplay() { - const location = useLocation() - return
{`${location.pathname}${location.search}`}
-} - describe('Dataset', () => { const mountWithDataset = ( component: ReactNode, @@ -322,8 +314,7 @@ describe('Dataset', () => { + datasetRepository={datasetRepository}> @@ -339,6 +330,7 @@ describe('Dataset', () => { it('renders skeleton while loading', () => { mountWithDataset( { mountWithDataset( { mountWithDataset( { cy.findAllByRole('tab', { name: 'Versions' }).should('have.length', 1) }) - it('redirects to the dataset page when publish completes', () => { - const publishedDataset = DatasetMother.create({ - persistentId: 'doi:10.5072/FK2/PUBLISHDONE' - }) - cy.clock() - - mountWithDataset( - <> - - - , - publishedDataset - ) - - cy.findByText('Publish in Progress').should('exist') - cy.tick(2_000) - cy.findByTestId('location-display').should( - 'have.text', - '/datasets?persistentId=doi:10.5072/FK2/PUBLISHDONE' - ) - }) - it('renders the breadcrumbs', () => { mountWithDataset( { it('renders the Dataset page title and labels', () => { mountWithDataset( { it('renders the Dataset Metadata tab', () => { mountWithDataset( { it('renders the Dataset Terms tab', () => { mountWithDataset( { cy.findByTestId('dataset-guestbook-section').should('exist') }) - it('opens the terms tab from the custom terms summary link', () => { - const customTermsDataset = DatasetMother.create({ - license: undefined, - termsOfUse: TermsOfUseMother.create({ - customTerms: { - termsOfUse: 'Custom terms summary text', - confidentialityDeclaration: '', - specialPermissions: '', - restrictions: '', - citationRequirements: '', - depositorRequirements: '', - conditions: '', - disclaimer: '' - } - }) - }) - - mountWithDataset( - <> - - - , - customTermsDataset - ) - - cy.findByRole('button', { name: 'Custom Dataset Terms' }).click() - cy.findByTestId('location-display').should('contain', '?tab=terms') - }) - it('renders the read-only terms tab title when user cannot edit dataset', () => { const readOnlyDataset = DatasetMother.create({ permissions: DatasetPermissionsMother.createWithUpdateDatasetNotAllowed() @@ -525,6 +461,7 @@ describe('Dataset', () => { mountWithDataset( { it('renders the Dataset Files tab', () => { mountWithDataset( { mountWithDataset( { mountWithDataset( { ) }) - it('renders the dataset tabs skeleton', () => { - cy.customMount() - - cy.findByRole('tab', { name: 'Files' }).should('exist') - cy.findByRole('tab', { name: 'Metadata' }).should('exist') - cy.findByRole('tab', { name: 'Terms' }).should('exist') - cy.findByRole('tab', { name: 'Versions' }).should('exist') - }) - it('should render all tabs if the dataset is in deaccessioned version, and user has edit permission', () => { const testDataset = DatasetMother.createDeaccessionedwithEditPermission() mountWithDataset( { mountWithDataset( { it('renders the Dataset Action Buttons', () => { mountWithDataset( { it('renders the Dataset Files list table with infinite scrolling enabled', () => { mountWithDataset( { it('shows the toast when the information was sent to contact successfully', () => { mountWithDataset( { it('does not show the tooltip for contact owner button', () => { mountWithDataset( { mountWithDataset( { it('renders the dataset configure tools options if they are available', () => { cy.customMount( - - - - - + + + ) cy.findByText('Explore Options').should('not.exist') @@ -37,22 +34,18 @@ describe('DatasetToolOptions', () => { it('renders nothing if there are no dataset configure tools', () => { testExternalToolsRepository.getExternalTools = cy.stub().resolves([]) cy.customMount( - - - - - + + + ) cy.findByText('Configure Options').should('not.exist') }) it('renders the dataset explore tools options if they are available', () => { cy.customMount( - - - - - + + + ) cy.findByText('Configure Options').should('not.exist') @@ -63,11 +56,9 @@ describe('DatasetToolOptions', () => { it('renders nothing if there are no dataset explore tools', () => { testExternalToolsRepository.getExternalTools = cy.stub().resolves([]) cy.customMount( - - - - - + + + ) cy.findByText('Explore Options').should('not.exist') }) @@ -93,11 +84,9 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() @@ -133,11 +122,9 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('Dataset Explore Tool').click() @@ -170,11 +157,9 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() @@ -207,11 +192,9 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('Dataset Explore Tool').click() @@ -252,11 +235,9 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('Dataset Explore Tool').should('exist').as('toolButton') @@ -279,11 +260,9 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() @@ -309,11 +288,9 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() @@ -339,11 +316,9 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() diff --git a/tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx b/tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx index 3d720e64d..639811886 100644 --- a/tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx +++ b/tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx @@ -5,47 +5,12 @@ import { DatasetVersionMother } from '../../../../dataset/domain/models/DatasetMother' import { FileSizeUnit } from '../../../../../../src/files/domain/models/FileMetadata' -import { submitGuestbookForDatasetDownload } from '@iqss/dataverse-client-javascript' +import { getGuestbook, submitGuestbookForDatasetDownload } from '@iqss/dataverse-client-javascript' import { ReactNode, Suspense } from 'react' import { useTranslation } from 'react-i18next' import { AccessRepository } from '@/access/domain/repositories/AccessRepository' import { DatasetPermissions } from '@/dataset/domain/models/Dataset' import { AccessRepositoryProvider } from '@/sections/access/AccessRepositoryProvider' -import { Guestbook } from '@/guestbooks/domain/models/Guestbook' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { WithRepositories } from '@tests/component/WithRepositories' - -const guestbook: Guestbook = { - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 -} - -function createGuestbookRepository( - repositoryOverrides: Partial = {} -): GuestbookRepository { - return { - getGuestbook: cy.stub().resolves(guestbook), - getGuestbooksByCollectionId: cy.stub().resolves([]), - assignDatasetGuestbook: cy.stub().resolves(), - removeDatasetGuestbook: cy.stub().resolves(), - ...repositoryOverrides - } -} - -function withGuestbookRepository( - component: React.ReactNode, - guestbookRepository: GuestbookRepository -) { - return {component} -} function TranslationPreloader({ children }: { children: ReactNode }) { useTranslation('dataset') @@ -330,7 +295,18 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const guestbookRepository = createGuestbookRepository() + cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) const submitGuestbookForDatasetDownloadExecute = cy .stub(submitGuestbookForDatasetDownload, 'execute') .resolves('/api/v1/access/dataset/test-token') @@ -339,23 +315,20 @@ describe('AccessDatasetMenu', () => { }) cy.customMount( - withGuestbookRepository( - - - - - , - guestbookRepository - ) + + + + + ) cy.findByRole('button', { name: 'Access Dataset' }).click() @@ -378,34 +351,42 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const guestbookRepository = createGuestbookRepository() + const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.customMount( - withGuestbookRepository( - - - - - , - guestbookRepository - ) + + + + + ) - cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') + cy.wrap(getGuestbookExecute).should('not.have.been.called') cy.findByRole('button', { name: 'Access Dataset' }).click() cy.findByRole('button', { name: /Download ZIP/ }).click() - cy.wrap(guestbookRepository.getGuestbook).should('have.been.calledOnceWith', 10) + cy.wrap(getGuestbookExecute).should('have.been.calledOnceWith', 10) }) it('renders download option as a button and opens guestbook modal when guestbook exists', () => { @@ -416,25 +397,34 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const guestbookRepository = createGuestbookRepository() + + cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.customMount( - withGuestbookRepository( - - - - - , - guestbookRepository - ) + + + + + ) cy.findByRole('button', { name: 'Access Dataset' }).click() @@ -451,25 +441,34 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const guestbookRepository = createGuestbookRepository() + + cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.customMount( - withGuestbookRepository( - - - - - , - guestbookRepository - ) + + + + + ) cy.findByRole('button', { name: 'Access Dataset' }).click() @@ -487,25 +486,34 @@ describe('AccessDatasetMenu', () => { DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }), DatasetFileDownloadSizeMother.createArchival({ value: 4000, unit: FileSizeUnit.BYTES }) ] - const guestbookRepository = createGuestbookRepository() + + cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.customMount( - withGuestbookRepository( - - - - - , - guestbookRepository - ) + + + + + ) cy.findByRole('button', { name: 'Access Dataset' }).click() @@ -575,38 +583,46 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const guestbookRepository = createGuestbookRepository() + const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.window().then((window) => { cy.stub(window.HTMLAnchorElement.prototype, 'click').as('anchorClick') }) cy.customMount( - withGuestbookRepository( - withAccessRepository( - - - - - - ), - guestbookRepository + withAccessRepository( + + + + + ) ) cy.findByRole('button', { name: 'Access Dataset' }).click() cy.findByRole('button', { name: /Download ZIP/ }).click() - cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') + cy.wrap(getGuestbookExecute).should('not.have.been.called') cy.get('@anchorClick').should('have.been.calledOnce') cy.findByRole('dialog').should('not.exist') cy.findByText('Your download has started.').should('exist') @@ -621,38 +637,46 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const guestbookRepository = createGuestbookRepository() + const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.window().then((window) => { cy.stub(window.HTMLAnchorElement.prototype, 'click').as('anchorClick') }) cy.customMount( - withGuestbookRepository( - withAccessRepository( - - - - - - ), - guestbookRepository + withAccessRepository( + + + + + ) ) cy.findByRole('button', { name: 'Access Dataset' }).click() cy.findByRole('button', { name: /Download ZIP/ }).click() - cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') + cy.wrap(getGuestbookExecute).should('not.have.been.called') cy.get('@anchorClick').should('have.been.calledOnce') cy.findByRole('dialog').should('not.exist') cy.findByText('Your download has started.').should('exist') diff --git a/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx b/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx index 6d35de84c..42e943b75 100644 --- a/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx @@ -72,8 +72,12 @@ describe('DatasetFiles', () => { it('renders the files table', () => { cy.customMount( - - + + ) @@ -84,8 +88,12 @@ describe('DatasetFiles', () => { describe('Pagination navigation', () => { it('renders the files table with the correct header on a page different than the first one ', () => { cy.customMount( - - + + ) @@ -96,8 +104,12 @@ describe('DatasetFiles', () => { it('renders the files table with the correct page selected after updating the pageSize', () => { cy.customMount( - - + + ) @@ -112,8 +124,12 @@ describe('DatasetFiles', () => { it('renders the files table with the correct header with a different page size ', () => { cy.customMount( - - + + ) @@ -132,8 +148,12 @@ describe('DatasetFiles', () => { fileRepository.getFilesTotalDownloadSizeByDatasetPersistentId = cy.stub().resolves(19900) cy.customMount( - - + + ) @@ -164,8 +184,12 @@ describe('DatasetFiles', () => { fileRepository.getFilesTotalDownloadSizeByDatasetPersistentId = cy.stub().resolves(19900) cy.customMount( - - + + ) @@ -193,8 +217,12 @@ describe('DatasetFiles', () => { it('maintains the selection when the page changes', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -224,8 +252,12 @@ describe('DatasetFiles', () => { it('maintains the selection when the page size changes', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -246,8 +278,12 @@ describe('DatasetFiles', () => { it('removes the selection when the filters change', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -264,8 +300,12 @@ describe('DatasetFiles', () => { it('removes the selection when the Sort by changes', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -282,8 +322,12 @@ describe('DatasetFiles', () => { it('removes the selection when the Search bar is used', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -309,9 +353,10 @@ describe('DatasetFiles', () => { }) cy.customMount( - + @@ -336,9 +381,10 @@ describe('DatasetFiles', () => { it('renders the zip download limit message when selecting all rows', () => { cy.customMount( - + @@ -355,9 +401,10 @@ describe('DatasetFiles', () => { it('renders the zip download limit message when selecting all rows and then navigating to other page', () => { cy.customMount( - + @@ -377,8 +424,12 @@ describe('DatasetFiles', () => { describe('Calling use cases', () => { it('calls the useFiles hook with the correct parameters', () => { cy.customMount( - - + + ) @@ -397,8 +448,12 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when sortBy criteria changes', () => { cy.customMount( - - + + ) @@ -415,8 +470,12 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when filterByType criteria changes', () => { cy.customMount( - - + + ) @@ -433,8 +492,12 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when filterByAccess criteria changes', () => { cy.customMount( - - + + ) @@ -451,8 +514,12 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when filterByTag criteria changes', () => { cy.customMount( - - + + ) @@ -469,8 +536,12 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when searchText criteria changes', () => { cy.customMount( - - + + ) @@ -486,8 +557,12 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when paginationInfo changes', () => { cy.customMount( - - + + ) @@ -502,9 +577,10 @@ describe('DatasetFiles', () => { it('calls getFilesTotalDownloadSizeByDatasetPersistentId with the correct parameters when applying search file criteria', () => { cy.customMount( - + diff --git a/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx b/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx index 80e265339..006189e31 100644 --- a/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx @@ -93,8 +93,9 @@ describe('DatasetFilesScrollable', () => { it('renders the scrollable files table', () => { cy.customMount( - + @@ -108,8 +109,9 @@ describe('DatasetFilesScrollable', () => { it('check that the files sections are rendered even without edit permissions', () => { cy.customMount( - + { it('renders the first 10 files', () => { cy.customMount( - + @@ -162,8 +165,9 @@ describe('DatasetFilesScrollable', () => { totalFilesCount: 0 }) cy.customMount( - + @@ -178,8 +182,9 @@ describe('DatasetFilesScrollable', () => { fileRepository.getAllByDatasetPersistentIdWithCount = cy.stub().resolves(only4Files) cy.customMount( - + @@ -197,8 +202,9 @@ describe('DatasetFilesScrollable', () => { it('loads more files when scrolling to the bottom ', () => { cy.customMount( - + @@ -219,8 +225,9 @@ describe('DatasetFilesScrollable', () => { it('scrolls to the top when criteria changes', () => { cy.customMount( - + @@ -244,8 +251,9 @@ describe('DatasetFilesScrollable', () => { describe('Sticky elements', () => { it('should stick the header table when scrolling down', () => { cy.customMount( - + @@ -270,9 +278,10 @@ describe('DatasetFilesScrollable', () => { it('should stick the table top messages on top of the table header when scrolling down with selected files', () => { cy.customMount( - + @@ -304,9 +313,10 @@ describe('DatasetFilesScrollable', () => { it('table header should have css top value according to criteria container height', () => { cy.customMount( - + @@ -341,9 +351,10 @@ describe('DatasetFilesScrollable', () => { it('table header should have css top value according to criteria container height + top messages container height when selected files ,top messages container should have top value only according to criteria container height', () => { cy.customMount( - + @@ -408,8 +419,9 @@ describe('DatasetFilesScrollable', () => { describe('File selection', () => { it('selects first 10 files when clicking the top header checkbox', () => { cy.customMount( - + @@ -427,8 +439,9 @@ describe('DatasetFilesScrollable', () => { it('selects all files when clicking the select all button', () => { cy.customMount( - + @@ -450,8 +463,9 @@ describe('DatasetFilesScrollable', () => { it('selects all files when clicking the select all button and mantains selection when loading more on scroll to bottom', () => { cy.customMount( - + @@ -490,8 +504,9 @@ describe('DatasetFilesScrollable', () => { it('maintains the selection when scrolling to bottom and loading more files', () => { cy.customMount( - + @@ -535,8 +550,9 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the header checkbox is clicked again', () => { cy.customMount( - + @@ -558,8 +574,9 @@ describe('DatasetFilesScrollable', () => { it('selects all loaded by scroll files when clicking the header checkbox', () => { cy.customMount( - + @@ -586,8 +603,9 @@ describe('DatasetFilesScrollable', () => { it('all new loaded files should be checked if selecting all files when only displayed 10 and then scrolling to bottom to load 10 more files', () => { cy.customMount( - + @@ -622,8 +640,9 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the filters change', () => { cy.customMount( - + @@ -639,8 +658,9 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the Sort by changes', () => { cy.customMount( - + @@ -656,8 +676,9 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the Search bar is used', () => { cy.customMount( - + @@ -672,8 +693,9 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the clear all button is clicked', () => { cy.customMount( - + @@ -696,9 +718,10 @@ describe('DatasetFilesScrollable', () => { metadata: FileMetadataMother.create({ size: new FileSize(2, FileSizeUnit.BYTES) }) }) cy.customMount( - + @@ -722,9 +745,10 @@ describe('DatasetFilesScrollable', () => { it('renders the zip download limit message when selecting all rows', () => { cy.customMount( - + @@ -742,9 +766,10 @@ describe('DatasetFilesScrollable', () => { it('renders the zip download limit message when selecting all rows and then scrolling to bottom to load more files', () => { cy.customMount( - + @@ -770,8 +795,9 @@ describe('DatasetFilesScrollable', () => { describe('Calling use cases', () => { it('calls the useGetAccumulatedFiles hook with the correct parameters', () => { cy.customMount( - + @@ -790,8 +816,9 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when sortBy criteria changes', () => { cy.customMount( - + @@ -809,8 +836,9 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when filterByType criteria changes', () => { cy.customMount( - + @@ -828,8 +856,9 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when filterByAccess criteria changes', () => { cy.customMount( - + @@ -847,8 +876,9 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when filterByTag criteria changes', () => { cy.customMount( - + @@ -866,8 +896,9 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when searchText criteria changes', () => { cy.customMount( - + @@ -884,8 +915,9 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when scrolling to bottom', () => { cy.customMount( - + @@ -915,9 +947,10 @@ describe('DatasetFilesScrollable', () => { }) it('calls getFilesTotalDownloadSizeByDatasetPersistentId with the correct parameters when applying search file criteria', () => { cy.customMount( - + @@ -950,8 +983,9 @@ describe('DatasetFilesScrollable', () => { .rejects(new Error('Some error on getAllByDatasetPersistentIdWithCount')) cy.customMount( - + @@ -964,8 +998,9 @@ describe('DatasetFilesScrollable', () => { fileRepository.getAllByDatasetPersistentIdWithCount = cy.stub().rejects(new Error()) cy.customMount( - + @@ -980,8 +1015,9 @@ describe('DatasetFilesScrollable', () => { .rejects(new Error('Some error on getFilesTotalDownloadSizeByDatasetPersistentId')) cy.customMount( - + @@ -994,8 +1030,9 @@ describe('DatasetFilesScrollable', () => { fileRepository.getFilesTotalDownloadSizeByDatasetPersistentId = cy.stub().rejects(new Error()) cy.customMount( - + @@ -1010,8 +1047,9 @@ describe('DatasetFilesScrollable', () => { .rejects(new Error('Some error on getFilesCountInfoByDatasetPersistentId')) cy.customMount( - + @@ -1024,8 +1062,9 @@ describe('DatasetFilesScrollable', () => { fileRepository.getFilesCountInfoByDatasetPersistentId = cy.stub().rejects(new Error()) cy.customMount( - + diff --git a/tests/component/sections/dataset/dataset-files/files-table/FilesTable.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/FilesTable.spec.tsx index ae3a9262e..eaf2b0529 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/FilesTable.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/FilesTable.spec.tsx @@ -25,13 +25,14 @@ const defaultCriteria = new FileCriteria() describe('FilesTable', () => { it('renders the files table', () => { cy.customMount( - + ) @@ -47,13 +48,14 @@ describe('FilesTable', () => { it('renders the spinner when the data isLoading', () => { cy.customMount( - + ) @@ -63,13 +65,14 @@ describe('FilesTable', () => { it('renders the no files message when there are no files', () => { cy.customMount( - + ) @@ -80,13 +83,14 @@ describe('FilesTable', () => { describe('Row selection', () => { it('selects all rows in the current page when the header checkbox is clicked', () => { cy.customMount( - + ) @@ -102,13 +106,14 @@ describe('FilesTable', () => { it('clears row selection for the current page when the header checkbox is clicked', () => { cy.customMount( - + ) @@ -128,13 +133,14 @@ describe('FilesTable', () => { it("selects all rows when the 'Select all' button is clicked", () => { cy.customMount( - + ) @@ -150,13 +156,14 @@ describe('FilesTable', () => { it('clears the selection when the clear selection button is clicked', () => { cy.customMount( - + ) @@ -174,13 +181,14 @@ describe('FilesTable', () => { it('highlights the selected rows', () => { cy.customMount( - + ) @@ -206,7 +214,7 @@ describe('FilesTable', () => { .resolves(SettingMother.createZipDownloadLimit(new ZipDownloadLimit(500, FileSizeUnit.BYTES))) cy.customMount( - + { isLoading={false} filesTotalDownloadSize={testFilesTotalDownloadSize} criteria={defaultCriteria} + fileRepository={fileRepository} /> @@ -232,13 +241,14 @@ describe('FilesTable', () => { it('renders the file actions column', () => { cy.customMount( - + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx index 61085b02d..c443dd856 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx @@ -20,11 +20,11 @@ describe('FileActionsHeader', () => { datasetRepository.getByPersistentId = cy.stub().resolves(datasetWithUpdatePermissions) const files = FilePreviewMother.createMany(2) cy.mountAuthenticated( - + - + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx index e177e71e4..077c3ae3c 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx @@ -1,5 +1,6 @@ import { ReactNode, Suspense } from 'react' import { + getGuestbook, submitGuestbookForDatasetDownload, submitGuestbookForDatafilesDownload } from '@iqss/dataverse-client-javascript' @@ -23,26 +24,10 @@ import { CustomTermsMother, TermsOfUseMother } from '../../../../../../dataset/domain/models/TermsOfUseMother' -import { Guestbook } from '@/guestbooks/domain/models/Guestbook' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { WithRepositories } from '@tests/component/WithRepositories' const datasetRepository: DatasetRepository = {} as DatasetRepository const fileRepository = {} as FileRepository describe('DownloadFilesButton', () => { - const guestbook: Guestbook = { - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - } - const TranslationPreloader = ({ children }: { children: ReactNode }) => { useTranslation('files') useTranslation('dataset') @@ -82,21 +67,6 @@ describe('DownloadFilesButton', () => { ) } - const createGuestbookRepository = ( - repositoryOverrides: Partial = {} - ): GuestbookRepository => ({ - getGuestbook: cy.stub().resolves(guestbook), - getGuestbooksByCollectionId: cy.stub().resolves([]), - assignDatasetGuestbook: cy.stub().resolves(), - removeDatasetGuestbook: cy.stub().resolves(), - ...repositoryOverrides - }) - - const withGuestbookRepository = ( - component: ReactNode, - guestbookRepository: GuestbookRepository - ) => {component} - beforeEach(() => { fileRepository.getMultipleFileDownloadUrl = cy .stub() @@ -493,24 +463,32 @@ describe('DownloadFilesButton', () => { const fileSelection = { 'some-file-id': files[0] } - const guestbookRepository = createGuestbookRepository() + const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.mountAuthenticated( - withGuestbookRepository( - withDataset( - , - datasetWithGuestbook - ), - guestbookRepository + withDataset( + , + datasetWithGuestbook ) ) - cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') + cy.wrap(getGuestbookExecute).should('not.have.been.called') cy.get('#download-files').click() cy.findByRole('button', { name: 'Original Format' }).click() - cy.wrap(guestbookRepository.getGuestbook).should('have.been.calledOnceWith', 10) + cy.wrap(getGuestbookExecute).should('have.been.calledOnceWith', 10) }) it('submits guestbook for the dataset when all files are selected and guestbook exists', () => { @@ -531,7 +509,18 @@ describe('DownloadFilesButton', () => { 'file-c': undefined } - const guestbookRepository = createGuestbookRepository() + cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) const submitDatasetStub = cy .stub(submitGuestbookForDatasetDownload, 'execute') .resolves('/api/v1/access/dataset/999?token=test') @@ -543,12 +532,9 @@ describe('DownloadFilesButton', () => { }) cy.mountAuthenticated( - withGuestbookRepository( - withDataset( - , - datasetWithGuestbook - ), - guestbookRepository + withDataset( + , + datasetWithGuestbook ) ) @@ -577,27 +563,35 @@ describe('DownloadFilesButton', () => { const fileSelection = { 'some-file-id': files[0] } - const guestbookRepository = createGuestbookRepository() + const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.window().then((window) => { cy.stub(window.HTMLAnchorElement.prototype, 'click').as('anchorClick') }) cy.mountAuthenticated( - withGuestbookRepository( - withAccessRepository( - withDataset( - , - datasetWithGuestbook - ) - ), - guestbookRepository + withAccessRepository( + withDataset( + , + datasetWithGuestbook + ) ) ) cy.get('#download-files').click() - cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') + cy.wrap(getGuestbookExecute).should('not.have.been.called') cy.get('@anchorClick').should('have.been.calledOnce') cy.findByRole('dialog').should('not.exist') cy.findByText('Your download has started.').should('exist') @@ -618,27 +612,35 @@ describe('DownloadFilesButton', () => { const fileSelection = { 'some-file-id': files[0] } - const guestbookRepository = createGuestbookRepository() + const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + }) cy.window().then((window) => { cy.stub(window.HTMLAnchorElement.prototype, 'click').as('anchorClick') }) cy.mountAuthenticated( - withGuestbookRepository( - withAccessRepository( - withDataset( - , - datasetWithGuestbook - ) - ), - guestbookRepository + withAccessRepository( + withDataset( + , + datasetWithGuestbook + ) ) ) cy.get('#download-files').click() - cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') + cy.wrap(getGuestbookExecute).should('not.have.been.called') cy.get('@anchorClick').should('have.been.calledOnce') cy.findByRole('dialog').should('not.exist') cy.findByText('Your download has started.').should('exist') diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx index ea0282851..0200ace9b 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx @@ -25,7 +25,7 @@ describe('EditFilesMenu', () => { datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -37,7 +37,10 @@ describe('EditFilesMenu', () => { it('renders the Edit Files menu', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.get('#edit-files-menu').should('exist') @@ -45,7 +48,10 @@ describe('EditFilesMenu', () => { it('does not render the Edit Files menu when the user is not authenticated', () => { cy.customMount( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.get('#edit-files-menu').should('not.exist') @@ -53,7 +59,10 @@ describe('EditFilesMenu', () => { it('does not render the Edit Files menu when there are no files in the dataset', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.get('#edit-files-menu').should('not.exist') @@ -61,7 +70,10 @@ describe('EditFilesMenu', () => { it('renders the Edit Files options', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.get('#edit-files-menu').click() @@ -75,7 +87,7 @@ describe('EditFilesMenu', () => { cy.mountAuthenticated( withDataset( - , + , datasetWithNoUpdatePermissions ) ) @@ -90,7 +102,10 @@ describe('EditFilesMenu', () => { }) cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.get('#edit-files-menu').should('be.disabled') @@ -103,7 +118,10 @@ describe('EditFilesMenu', () => { }) cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.get('#edit-files-menu').should('be.disabled') diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx index aa86cb6b9..05c60829f 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx @@ -21,8 +21,13 @@ const datasetInfo = { describe('EditFilesOptions', () => { it('renders the EditFilesOptions', () => { cy.customMount( - - + + ) @@ -36,8 +41,13 @@ describe('EditFilesOptions', () => { it('renders the restrict option if some file is unrestricted', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -49,8 +59,13 @@ describe('EditFilesOptions', () => { it('renders the unrestrict option if some file is restricted', () => { const fileRestricted = FilePreviewMother.createRestricted() cy.customMount( - - + + ) @@ -61,8 +76,13 @@ describe('EditFilesOptions', () => { it.skip('renders the embargo option if the embargo is allowed by settings', () => { cy.customMount( - - + + ) @@ -73,8 +93,13 @@ describe('EditFilesOptions', () => { it.skip('renders provenance option if provenance is enabled in config', () => { cy.customMount( - - + + ) @@ -85,8 +110,13 @@ describe('EditFilesOptions', () => { it('shows the No Selected Files message when no files are selected and one option is clicked', () => { cy.customMount( - - + + ) @@ -101,10 +131,11 @@ describe('EditFilesOptions', () => { it('does not show the No Selected Files message when files are selected and one option is clicked', () => { cy.customMount( - + @@ -122,8 +153,13 @@ describe('EditFilesOptions for a single file', () => { it('renders the EditFilesOptions', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -146,8 +182,13 @@ describe('EditFilesOptions for a single file', () => { it('renders the restrict option if some file is unrestricted', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -161,8 +202,13 @@ describe('EditFilesOptions for a single file', () => { it('renders the unrestrict option if file is restricted', () => { const fileRestricted = FilePreviewMother.createRestricted() cy.customMount( - - + + ) @@ -177,8 +223,13 @@ describe('EditFilesOptions for a single file', () => { it('renders delete modal', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -192,9 +243,10 @@ describe('EditFilesOptions for a single file', () => { it('should delete file if delete button clicked', () => { fileRepository.delete = cy.stub().resolves() cy.customMount( - + @@ -211,8 +263,13 @@ describe('EditFilesOptions for a single file', () => { it('should reset the modal if cancel is clicked in delete modal', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -235,8 +292,13 @@ describe('EditFilesOptions for a single file', () => { const fileToDelete = FilePreviewMother.createDefault() cy.customMount( - - + + , [ `${Route.DATASETS}?${QueryParamKey.PERSISTENT_ID}=${datasetInfo.persistentId}&${QueryParamKey.VERSION}=DRAFT` @@ -262,9 +324,10 @@ describe('EditFilesOptions for a single file', () => { it('should restrict file if restrict button clicked', () => { fileRepository.restrict = cy.stub().resolves() cy.customMount( - + @@ -283,8 +346,13 @@ describe('EditFilesOptions for a single file', () => { const fileRestricted = FilePreviewMother.createRestricted() cy.customMount( - - + + ) @@ -299,9 +367,10 @@ describe('EditFilesOptions for a single file', () => { it('should restrict file and call refreshFiles if restrict button clicked in draft version', () => { fileRepository.restrict = cy.stub().resolves() cy.customMount( - + @@ -321,8 +390,13 @@ describe('EditFilesOptions for a single file', () => { it('should reset the modal if cancel is clicked in restrict modal', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -339,8 +413,13 @@ describe('EditFilesOptions for a single file', () => { it('opens and closes the edit file tags modal', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -355,8 +434,13 @@ describe('EditFilesOptions for a single file', () => { const fileWithTags = FilePreviewMother.createWithLabels() cy.customMount( - - + + ) @@ -376,8 +460,13 @@ describe('EditFilesOptions for a single file', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -393,8 +482,13 @@ describe('EditFilesOptions for a single file', () => { const fileRestricted = FilePreviewMother.createRestricted() cy.customMount( - - + + ) @@ -410,8 +504,13 @@ describe('EditFilesOptions for a single file', () => { const fileToDelete = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -429,8 +528,13 @@ describe('EditFilesOptions for a single file', () => { fileRepository.updateCategories = cy.stub().resolves() cy.customMount( - - + + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.spec.tsx index 5d0da5783..9240bdfb8 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.spec.tsx @@ -16,8 +16,8 @@ const datasetRepository: DatasetRepository = {} as DatasetRepository describe('FileActionButtons', () => { it('renders the file action buttons', () => { cy.customMount( - - + + ) @@ -35,11 +35,11 @@ describe('FileActionButtons', () => { datasetRepository.getByPrivateUrlToken = cy.stub().resolves(datasetWithUpdatePermissions) cy.mountAuthenticated( - + - + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileTools.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileTools.spec.tsx index 948aea5a6..9ff86f86a 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileTools.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileTools.spec.tsx @@ -5,7 +5,6 @@ import { ExternalToolsProvider } from '@/shared/contexts/external-tools/External import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { FileMetadataMother } from '@tests/component/files/domain/models/FileMetadataMother' import { FilePreviewMother } from '@tests/component/files/domain/models/FilePreviewMother' -import { WithRepositories } from '@tests/component/WithRepositories' const testFilePreview = FilePreviewMother.createDefault() // text/plain file const testExternalToolsRepository: ExternalToolsRepository = {} as ExternalToolsRepository @@ -22,11 +21,9 @@ describe('FileTools', () => { it('renders external tool buttons when user can download the file and there are applicable tools', () => { cy.customMount( - - - - - + + + ) cy.findByRole('link', { name: `Preview ${testFilePreview.name}` }) @@ -52,11 +49,9 @@ describe('FileTools', () => { it('does not render external tool buttons when user cannot download the file', () => { cy.customMount( - - - - - + + + ) }) @@ -68,11 +63,9 @@ describe('FileTools', () => { }) cy.customMount( - - - - - + + + ) }) }) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.spec.tsx index 6ea05e687..eab1da5a2 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.spec.tsx @@ -25,7 +25,7 @@ describe('FileOptionsMenu', () => { datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -37,14 +37,20 @@ describe('FileOptionsMenu', () => { it('renders the FileOptionsMenu', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.findByRole('button', { name: 'File Options' }).should('exist') }) it('renders the file options menu with tooltip', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.findByRole('button', { name: 'File Options' }).trigger('mouseover') @@ -53,7 +59,10 @@ describe('FileOptionsMenu', () => { it('renders the dropdown header', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.findByRole('button', { name: 'File Options' }).should('exist').click() @@ -61,7 +70,12 @@ describe('FileOptionsMenu', () => { }) it('does not render is the user is not authenticated', () => { - cy.customMount(withDataset(, datasetWithUpdatePermissions)) + cy.customMount( + withDataset( + , + datasetWithUpdatePermissions + ) + ) cy.findByRole('button', { name: 'File Options' }).should('not.exist') }) @@ -71,7 +85,10 @@ describe('FileOptionsMenu', () => { permissions: DatasetPermissionsMother.createWithUpdateDatasetNotAllowed() }) cy.mountAuthenticated( - withDataset(, datasetWithNoUpdatePermissions) + withDataset( + , + datasetWithNoUpdatePermissions + ) ) cy.findByRole('button', { name: 'File Options' }).should('not.exist') }) @@ -80,7 +97,12 @@ describe('FileOptionsMenu', () => { const datasetWithNoTermsOfAccess = DatasetMother.create({ hasValidTermsOfAccess: false }) - cy.mountAuthenticated(withDataset(, datasetWithNoTermsOfAccess)) + cy.mountAuthenticated( + withDataset( + , + datasetWithNoTermsOfAccess + ) + ) cy.findByRole('button', { name: 'File Options' }).should('not.exist') }) @@ -90,7 +112,12 @@ describe('FileOptionsMenu', () => { locks: [DatasetLockMother.createLockedInEditInProgress()], hasValidTermsOfAccess: true }) - cy.mountAuthenticated(withDataset(, datasetLockedFromEdits)) + cy.mountAuthenticated( + withDataset( + , + datasetLockedFromEdits + ) + ) cy.findByRole('button', { name: 'File Options' }).should('exist').should('be.disabled') }) @@ -99,7 +126,10 @@ describe('FileOptionsMenu', () => { const file = FilePreviewMother.createDeleted() cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.findByRole('button', { name: 'File Options' }).should('exist').click() @@ -113,7 +143,10 @@ describe('FileOptionsMenu', () => { it('renders the menu options', () => { cy.mountAuthenticated( - withDataset(, datasetWithUpdatePermissions) + withDataset( + , + datasetWithUpdatePermissions + ) ) cy.findByRole('button', { name: 'File Options' }).click() cy.findByRole('button', { name: 'Restrict' }).should('exist') diff --git a/tests/component/sections/dataset/dataset-files/guestbook/DownloadWithTermsAndGuestbookModal.spec.tsx b/tests/component/sections/dataset/dataset-files/guestbook/DownloadWithTermsAndGuestbookModal.spec.tsx index 42c8e564c..66095d974 100644 --- a/tests/component/sections/dataset/dataset-files/guestbook/DownloadWithTermsAndGuestbookModal.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/guestbook/DownloadWithTermsAndGuestbookModal.spec.tsx @@ -9,9 +9,9 @@ import { GuestbookResponseDTO } from '@/access/domain/repositories/AccessRepository' import { AccessRepositoryProvider } from '@/sections/access/AccessRepositoryProvider' +import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' import { SessionContext } from '@/sections/session/SessionContext' import { DatasetMother } from '@tests/component/dataset/domain/models/DatasetMother' -import { WithRepositories } from '@tests/component/WithRepositories' const guestbook: Guestbook = { id: 10, @@ -113,11 +113,11 @@ describe('DownloadWithTermsAndGuestbookModal', () => { isLoading: false, refreshDataset: () => {} }}> - + {component} - + ) diff --git a/tests/component/sections/dataset/dataset-guestbook/DatasetGuestbook.spec.tsx b/tests/component/sections/dataset/dataset-guestbook/DatasetGuestbook.spec.tsx index 926cc83da..42df75645 100644 --- a/tests/component/sections/dataset/dataset-guestbook/DatasetGuestbook.spec.tsx +++ b/tests/component/sections/dataset/dataset-guestbook/DatasetGuestbook.spec.tsx @@ -1,10 +1,10 @@ import { DatasetGuestbook } from '@/sections/dataset/dataset-guestbook/DatasetGuestbook' import { DatasetContext } from '@/sections/dataset/DatasetContext' +import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { Guestbook } from '@/guestbooks/domain/models/Guestbook' import { Dataset as DatasetModel } from '@/dataset/domain/models/Dataset' import { DatasetMother } from '@tests/component/dataset/domain/models/DatasetMother' -import { WithRepositories } from '@tests/component/WithRepositories' const guestbook: Guestbook = { id: 10, @@ -35,7 +35,7 @@ describe('DatasetGuestbook', () => { dataset: DatasetModel = DatasetMother.create({ guestbookId: guestbook.id }) ) => cy.customMount( - + { }}> - + ) it('renders a spinner while the guestbook is loading', () => { diff --git a/tests/component/sections/dataset/dataset-reviews/useGetDatasetReviews.spec.tsx b/tests/component/sections/dataset/dataset-reviews/useGetDatasetReviews.spec.tsx deleted file mode 100644 index 4967a3686..000000000 --- a/tests/component/sections/dataset/dataset-reviews/useGetDatasetReviews.spec.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { renderHook, waitFor } from '@testing-library/react' -import { DatasetReview } from '@/dataset/domain/models/DatasetReview' -import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' -import { useGetDatasetReviews } from '@/sections/dataset/dataset-reviews/useGetDatasetReviews' - -const datasetRepository: DatasetRepository = {} as DatasetRepository -const datasetId = 'doi:10.5072/FK2/ABC123' -const datasetReviews: DatasetReview[] = [ - { - id: 23, - title: 'Review of Some Title', - authors: ['Reviewer One'], - persistentId: 'doi:10.5072/FK2/REVIEW1', - persistentIdUrl: 'https://doi.org/10.5072/FK2/REVIEW1', - citation: 'Review citation', - citationHtml: 'Review citation', - datePublished: '2026-02-03', - description: 'A review dataset', - rubricMetadataBlocks: [] - } -] - -describe('useGetDatasetReviews', () => { - it('should return dataset reviews correctly', async () => { - datasetRepository.getDatasetReviews = cy.stub().resolves(datasetReviews) - - const { result } = renderHook(() => - useGetDatasetReviews({ - datasetRepository, - datasetId - }) - ) - - expect(result.current.isLoading).to.equal(true) - expect(result.current.error).to.equal(null) - expect(result.current.datasetReviews).to.deep.equal([]) - - await waitFor(() => { - expect(result.current.isLoading).to.equal(false) - expect(result.current.error).to.equal(null) - expect(result.current.datasetReviews).to.deep.equal(datasetReviews) - }) - cy.wrap(datasetRepository.getDatasetReviews).should('have.been.calledWith', datasetId) - }) - - it('should return the error message when the request fails with an Error', async () => { - datasetRepository.getDatasetReviews = cy.stub().rejects(new Error('Error message')) - - const { result } = renderHook(() => - useGetDatasetReviews({ - datasetRepository, - datasetId - }) - ) - - expect(result.current.isLoading).to.equal(true) - expect(result.current.error).to.equal(null) - expect(result.current.datasetReviews).to.deep.equal([]) - - await waitFor(() => { - expect(result.current.isLoading).to.equal(false) - expect(result.current.error).to.equal('Error message') - expect(result.current.datasetReviews).to.deep.equal([]) - }) - }) - - it('should return the default error message when the request fails with an Error without a message', async () => { - datasetRepository.getDatasetReviews = cy.stub().rejects(new Error('')) - - const { result } = renderHook(() => - useGetDatasetReviews({ - datasetRepository, - datasetId - }) - ) - - await waitFor(() => { - expect(result.current.isLoading).to.equal(false) - expect(result.current.error).to.equal( - 'Something went wrong getting the dataset reviews. Try again later.' - ) - expect(result.current.datasetReviews).to.deep.equal([]) - }) - }) - - it('should return the default error message when the request fails with a non-Error exception', async () => { - datasetRepository.getDatasetReviews = cy.stub().rejects('Unexpected error') - - const { result } = renderHook(() => - useGetDatasetReviews({ - datasetRepository, - datasetId - }) - ) - - await waitFor(() => { - expect(result.current.isLoading).to.equal(false) - expect(result.current.error).to.equal( - 'Something went wrong getting the dataset reviews. Try again later.' - ) - expect(result.current.datasetReviews).to.deep.equal([]) - }) - }) -}) diff --git a/tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx b/tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx index 72ea785a3..7bcb507c5 100644 --- a/tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx +++ b/tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx @@ -10,9 +10,9 @@ import { } from '../../../dataset/domain/models/TermsOfUseMother' import { DatasetContext } from '@/sections/dataset/DatasetContext' import { Dataset as DatasetModel } from '@/dataset/domain/models/Dataset' -import { ComponentProps, ReactNode } from 'react' +import { ReactNode } from 'react' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { WithRepositories } from '@tests/component/WithRepositories' +import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' const datasetPersistentId = 'test-dataset-persistent-id' const datasetVersion = DatasetMother.create().version @@ -54,18 +54,17 @@ const termsOfUseWithUndefinedValue = TermsOfUseMother.create({ }) const guestbookRepository: GuestbookRepository = {} as GuestbookRepository -const DatasetTermsWithRepositories = (props: ComponentProps) => ( - - - -) - describe('DatasetTerms', () => { const withDatasetContext = (component: ReactNode, dataset?: DatasetModel) => ( {} }}> {component} ) + const withGuestbookRepository = (component: ReactNode) => ( + + {component} + + ) beforeEach(() => { fileRepository.getFilesCountInfoByDatasetPersistentId = cy @@ -76,9 +75,10 @@ describe('DatasetTerms', () => { it('renders the license and terms of use sections', () => { cy.customMount( - @@ -91,9 +91,10 @@ describe('DatasetTerms', () => { it('shows no guestbook assigned message after expanding the guestbook accordion', () => { cy.customMount( withDatasetContext( - , @@ -126,14 +127,17 @@ describe('DatasetTerms', () => { }) cy.customMount( - withDatasetContext( - , - DatasetMother.create({ guestbookId }) + withGuestbookRepository( + withDatasetContext( + , + DatasetMother.create({ guestbookId }) + ) ) ) @@ -163,14 +167,17 @@ describe('DatasetTerms', () => { }) cy.customMount( - withDatasetContext( - , - DatasetMother.create({ guestbookId }) + withGuestbookRepository( + withDatasetContext( + , + DatasetMother.create({ guestbookId }) + ) ), ['/datasets?tab=terms&termsTab=guestbook'] ) @@ -181,9 +188,10 @@ describe('DatasetTerms', () => { it('check that the terms of use sections are rendered even without edit permissions', () => { cy.customMount( - { it('renders the correct number of restricted files', () => { cy.customMount( - @@ -216,9 +225,10 @@ describe('DatasetTerms', () => { }) it('does not render a row if the value is undefined', () => { cy.customMount( - @@ -232,9 +242,10 @@ describe('DatasetTerms', () => { .stub() .resolves(singleRestrictedFilesCountInfo) cy.customMount( - @@ -245,9 +256,10 @@ describe('DatasetTerms', () => { }) it('renders the custom terms', () => { cy.customMount( - @@ -261,9 +273,10 @@ describe('DatasetTerms', () => { it('renders the request access allowed message', () => { cy.customMount( - @@ -274,9 +287,10 @@ describe('DatasetTerms', () => { }) it('renders the request access not allowed message', () => { cy.customMount( - @@ -288,9 +302,10 @@ describe('DatasetTerms', () => { it('renders the data access place', () => { cy.customMount( - @@ -302,9 +317,10 @@ describe('DatasetTerms', () => { it('renders the original archive information', () => { cy.customMount( - @@ -316,9 +332,10 @@ describe('DatasetTerms', () => { it('renders the availability status', () => { cy.customMount( - @@ -330,9 +347,10 @@ describe('DatasetTerms', () => { it('renders the contact for access information', () => { cy.customMount( - @@ -344,9 +362,10 @@ describe('DatasetTerms', () => { it('renders the size of collection information', () => { cy.customMount( - @@ -358,9 +377,10 @@ describe('DatasetTerms', () => { it('renders the study completion information', () => { cy.customMount( - @@ -374,9 +394,10 @@ describe('DatasetTerms', () => { .stub() .resolves(noRestrictedFilesCountInfo) cy.customMount( - @@ -389,9 +410,10 @@ describe('DatasetTerms', () => { .stub() .resolves(singleRestrictedFilesCountInfo) cy.customMount( - @@ -404,9 +426,10 @@ describe('DatasetTerms', () => { .stub() .resolves(noRestrictedFilesCountInfo) cy.customMount( - diff --git a/tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx b/tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx index 3fee56978..0caf998fd 100644 --- a/tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx +++ b/tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx @@ -93,9 +93,7 @@ describe('EditDatasetTerms', () => { datasetRepository.getByPersistentId = cy.stub().resolves(dataset) datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -153,6 +151,7 @@ describe('EditDatasetTerms', () => { ) @@ -172,6 +171,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -193,6 +193,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -225,6 +226,7 @@ describe('EditDatasetTerms', () => { EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS.restrictedFilesTerms } licenseRepository={licenseRepository} + guestbookRepository={guestbookRepository} />, dataset ) @@ -249,6 +251,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -267,6 +270,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -305,6 +309,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -338,6 +343,8 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -360,6 +367,7 @@ describe('EditDatasetTerms', () => { // Force an invalid key to hit the default branch in getCurrentFormDirtyState defaultActiveTabKey={'unknown-tab' as unknown as EditDatasetTermsTabKey} licenseRepository={licenseRepository} + datasetRepository={datasetRepository} />, dataset ) @@ -381,6 +389,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -404,6 +413,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -424,6 +434,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -453,6 +464,7 @@ describe('EditDatasetTerms', () => { EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS.restrictedFilesTerms } licenseRepository={licenseRepository} + guestbookRepository={guestbookRepository} />, dataset ) @@ -481,6 +493,7 @@ describe('EditDatasetTerms', () => { EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS.restrictedFilesTerms } licenseRepository={licenseRepository} + guestbookRepository={guestbookRepository} />, dataset ) @@ -524,6 +537,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -547,6 +561,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -568,6 +583,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -591,9 +607,7 @@ describe('EditDatasetTerms', () => { datasetRepository.getByPersistentId = cy.stub().resolves(dataset) datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -609,6 +623,7 @@ describe('EditDatasetTerms', () => { , undefined ) @@ -626,6 +641,7 @@ describe('EditDatasetTerms', () => { EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS.restrictedFilesTerms } licenseRepository={licenseRepository} + guestbookRepository={guestbookRepository} />, dataset ) @@ -647,6 +663,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -664,6 +681,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -682,6 +700,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -703,6 +722,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -727,6 +747,7 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -742,9 +763,7 @@ describe('EditDatasetTerms Mobile View', () => { datasetRepository.getByPersistentId = cy.stub().resolves(dataset) datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -766,6 +785,7 @@ describe('EditDatasetTerms Mobile View', () => { , dataset ) diff --git a/tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx b/tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx index ff3814cf3..3dff96744 100644 --- a/tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx +++ b/tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx @@ -12,7 +12,6 @@ import { import { Dataset } from '@/dataset/domain/models/Dataset' import { Guestbook } from '@/guestbooks/domain/models/Guestbook' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { WithRepositories } from '@tests/component/WithRepositories' const LocationDisplay = () => { const location = useLocation() @@ -63,27 +62,23 @@ describe('EditGuestbook', () => { datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - - - {component} - - + + {component} + ) } const withDatasetContext = (component: ReactNode, dataset: Dataset | undefined) => ( - - {} - }}> - {component} - - + {} + }}> + {component} + ) beforeEach(() => { @@ -98,7 +93,9 @@ describe('EditGuestbook', () => { it('renders guestbook options and keeps Save Changes disabled for current guestbook', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByLabelText('Data Request Guestbook').should('be.checked') cy.findByLabelText('Secondary Guestbook').should('not.be.checked') @@ -109,7 +106,9 @@ describe('EditGuestbook', () => { it('enables Save Changes when selecting a different guestbook', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByLabelText('Secondary Guestbook').click() cy.findByRole('button', { name: 'Save Changes' }).should('be.enabled') @@ -119,7 +118,15 @@ describe('EditGuestbook', () => { const onFormStateChange = cy.stub().as('onFormStateChange') const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders( + , + dataset + ) + ) cy.findByLabelText('Secondary Guestbook').click() @@ -129,7 +136,9 @@ describe('EditGuestbook', () => { it('keeps Save Changes disabled when dataset has no assigned guestbook and none is selected', () => { const dataset = DatasetMother.create({ guestbookId: undefined }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByLabelText('Data Request Guestbook').should('not.be.checked') cy.findByLabelText('Secondary Guestbook').should('not.be.checked') @@ -140,7 +149,9 @@ describe('EditGuestbook', () => { it('falls back to no preselection when dataset has no guestbook id', () => { const dataset = DatasetMother.create({ guestbookId: undefined }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByLabelText('Data Request Guestbook').should('not.be.checked') cy.findByLabelText('Secondary Guestbook').should('not.be.checked') @@ -151,7 +162,9 @@ describe('EditGuestbook', () => { it('clears the selected guestbook when clicking Clear Selection', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByRole('button', { name: 'Clear Selection' }).click() @@ -167,7 +180,9 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByLabelText('Data Request Guestbook').should('not.exist') cy.findByLabelText('Secondary Guestbook').should('not.exist') @@ -185,7 +200,9 @@ describe('EditGuestbook', () => { it('opens preview modal when clicking Preview Guestbook', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findAllByRole('button', { name: 'Preview Guestbook' }).should('have.length', 2) cy.findAllByRole('button', { name: 'Preview Guestbook' }).eq(1).click() @@ -209,7 +226,9 @@ describe('EditGuestbook', () => { it('closes preview modal when clicking Close', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findAllByRole('button', { name: 'Preview Guestbook' }).eq(0).click() cy.findByRole('dialog').should('be.visible') @@ -221,7 +240,12 @@ describe('EditGuestbook', () => { const onPreview = cy.stub().as('onPreview') const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders( + , + dataset + ) + ) cy.findAllByRole('button', { name: 'Preview Guestbook' }).eq(0).click() cy.get('@onPreview').should('have.been.calledOnce') @@ -235,7 +259,9 @@ describe('EditGuestbook', () => { assignDatasetGuestbookStub.as('assignDatasetGuestbookExecute') const dataset = DatasetMother.create({ id: 999, guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByLabelText('Secondary Guestbook').click() cy.findByRole('button', { name: 'Save Changes' }).click() @@ -261,7 +287,7 @@ describe('EditGuestbook', () => { cy.customMount( withProviders( <> - + , releasedDataset @@ -290,7 +316,7 @@ describe('EditGuestbook', () => { cy.customMount( withProviders( <> - + , draftDataset @@ -319,7 +345,7 @@ describe('EditGuestbook', () => { cy.customMount( withProviders( <> - + , releasedDataset @@ -359,7 +385,7 @@ describe('EditGuestbook', () => { const [dataset, setDataset] = useState(createDataset(collectionA)) return ( <> - {withDatasetContext(, dataset)} + {withDatasetContext(, dataset)} @@ -401,7 +427,7 @@ describe('EditGuestbook', () => { const [dataset, setDataset] = useState(createDataset(collectionA)) return ( <> - {withDatasetContext(, dataset)} + {withDatasetContext(, dataset)} @@ -426,7 +452,9 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ id: 999, guestbookId: undefined }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.get('form').submit() cy.get('@assignDatasetGuestbookExecute').should('not.have.been.called') @@ -441,7 +469,9 @@ describe('EditGuestbook', () => { 'removeDatasetGuestbookExecute' ) - cy.customMount(withDatasetContext(, undefined)) + cy.customMount( + withDatasetContext(, undefined) + ) cy.get('form').submit() cy.get('@assignDatasetGuestbookExecute').should('not.have.been.called') @@ -455,7 +485,9 @@ describe('EditGuestbook', () => { removeDatasetGuestbookStub.as('removeDatasetGuestbookExecute') const dataset = DatasetMother.create({ id: 999, guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByRole('button', { name: 'Clear Selection' }).click() cy.findByRole('button', { name: 'Save Changes' }).click() @@ -470,7 +502,9 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ id: 999, guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByLabelText('Secondary Guestbook').click() cy.findByRole('button', { name: 'Save Changes' }).click() @@ -485,7 +519,9 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ id: 999, guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByRole('button', { name: 'Clear Selection' }).click() cy.findByRole('button', { name: 'Save Changes' }).click() @@ -500,7 +536,9 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount(withProviders(, dataset)) + cy.customMount( + withProviders(, dataset) + ) cy.findByText( /Something went wrong getting guestbooks by collection id. Try again later./ diff --git a/tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx b/tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx index dae92cf97..d5865d514 100644 --- a/tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx +++ b/tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx @@ -38,10 +38,6 @@ const mockDataset = DatasetMother.create({ }) describe('EditTermsOfAccess', () => { - beforeEach(() => { - datasetRepository.updateTermsOfAccess = cy.stub().resolves() - }) - const withProviders = (component: ReactNode, dataset: Dataset) => { datasetRepository.getByPersistentId = cy.stub().resolves(dataset) datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) diff --git a/tests/component/sections/file/File.spec.tsx b/tests/component/sections/file/File.spec.tsx index c9d6a18ae..1f9cf81bd 100644 --- a/tests/component/sections/file/File.spec.tsx +++ b/tests/component/sections/file/File.spec.tsx @@ -171,10 +171,8 @@ describe('File', () => { it('renders the External Tools tab with "Preview" title if only one tool applicable and is a preview tool', () => { cy.customMount( - - + + { .resolves([ExternalToolsMother.createFileQueryTool()]) cy.customMount( - - + + { ]) cy.customMount( - - + + { externalToolsRepository.getExternalTools = cy.stub().resolves([]) cy.customMount( - - + + { fileRepository.getById = cy.stub().resolves(testFile) cy.customMount( - - + + { const guestbook: Guestbook = { @@ -214,7 +214,7 @@ describe('AccessFileMenu', () => { cy.customMount( - + { datasetPersistentId="doi:10.5072/FK2/FILEPAGE" /> - + ) @@ -345,7 +345,7 @@ describe('AccessFileMenu', () => { cy.customMount( - + { }} /> - + ) diff --git a/tests/component/sections/file/file-action-buttons/access-file-menu/FileToolOptions.spec.tsx b/tests/component/sections/file/file-action-buttons/access-file-menu/FileToolOptions.spec.tsx index f3834d112..3efcb91de 100644 --- a/tests/component/sections/file/file-action-buttons/access-file-menu/FileToolOptions.spec.tsx +++ b/tests/component/sections/file/file-action-buttons/access-file-menu/FileToolOptions.spec.tsx @@ -7,7 +7,6 @@ import { import { ExternalToolsProvider } from '@/shared/contexts/external-tools/ExternalToolsProvider' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { FileExternalToolResolvedMother } from '@tests/component/externalTools/domain/models/FileExternalToolResolvedMother' -import { WithRepositories } from '@tests/component/WithRepositories' const testExternalToolsRepository: ExternalToolsRepository = {} as ExternalToolsRepository const testFileExploreTool = ExternalToolsMother.createFileExploreTool() @@ -24,11 +23,9 @@ describe('FileToolOptions', () => { describe('FileExploreToolsOptions', () => { it('renders the tool options if file explore tools are available and compatible with the type', () => { cy.customMount( - - - - - + + + ) cy.findByText('Query Options').should('not.exist') @@ -39,11 +36,9 @@ describe('FileToolOptions', () => { it('does not render the tool options if there are not applicable tools for the file type', () => { cy.customMount( - - - - - + + + ) cy.findByText('Explore Options').should('not.exist') @@ -54,11 +49,9 @@ describe('FileToolOptions', () => { describe('FileQueryToolsOptions', () => { it('renders the tool options if file query tools are available and compatible with the type', () => { cy.customMount( - - - - - + + + ) cy.findByText('Explore Options').should('not.exist') @@ -69,11 +62,9 @@ describe('FileToolOptions', () => { it('does not render the tool options if there are not applicable tools for the file type', () => { cy.customMount( - - - - - + + + ) cy.findByText('Explore Options').should('not.exist') @@ -84,11 +75,9 @@ describe('FileToolOptions', () => { describe('FileConfigureToolsOptions', () => { it('renders the tool options if file configure tools are available and compatible with the type', () => { cy.customMount( - - - - - + + + ) cy.findByText('Explore Options').should('not.exist') @@ -99,11 +88,9 @@ describe('FileToolOptions', () => { it('does not render the tool options if there are not applicable tools for the file type', () => { cy.customMount( - - - - - + + + ) cy.findByText('Explore Options').should('not.exist') @@ -132,11 +119,9 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('File Explore Tool').should('exist').click() @@ -167,11 +152,9 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('File Explore Tool').should('exist').click() @@ -216,11 +199,9 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('File Explore Tool').should('exist').as('toolButton') @@ -243,11 +224,9 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('File Explore Tool').should('exist').click() @@ -273,11 +252,9 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('File Explore Tool').should('exist').click() @@ -303,11 +280,9 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - - - + + + ) cy.findByText('File Explore Tool').should('exist').click() diff --git a/tests/component/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.spec.tsx b/tests/component/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.spec.tsx index f134bbdb0..469c57039 100644 --- a/tests/component/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.spec.tsx +++ b/tests/component/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.spec.tsx @@ -2,11 +2,9 @@ import { ExternalToolsRepository } from '@/externalTools/domain/repositories/Ext import { FileEmbeddedExternalTool } from '@/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool' import { FilePageHelper } from '@/sections/file/FilePageHelper' import { WriteError } from '@iqss/dataverse-client-javascript' -import { ComponentProps } from 'react' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { FileExternalToolResolvedMother } from '@tests/component/externalTools/domain/models/FileExternalToolResolvedMother' import { FileMother } from '@tests/component/files/domain/models/FileMother' -import { WithRepositories } from '@tests/component/WithRepositories' const externalToolsRepository: ExternalToolsRepository = {} as ExternalToolsRepository // Used for fetching the tool resolved URL @@ -22,14 +20,6 @@ const fileQueryToolResolved = FileExternalToolResolvedMother.create({ toolUrlResolved: 'https://example.com/query-tool?fileId=1' }) -const FileEmbeddedExternalToolWithRepositories = ( - props: ComponentProps -) => ( - - - -) - describe('FileEmbeddedExternalTool', () => { it('renders a single preview tool', () => { externalToolsRepository.getFileExternalToolResolved = cy @@ -37,10 +27,11 @@ describe('FileEmbeddedExternalTool', () => { .resolves(filePreviewToolResolved) cy.customMount( - ) @@ -76,10 +67,11 @@ describe('FileEmbeddedExternalTool', () => { externalToolsRepository.getFileExternalToolResolved = getFileExternalToolResolvedStub cy.customMount( - ) @@ -127,10 +119,11 @@ describe('FileEmbeddedExternalTool', () => { .resolves(filePreviewToolResolved) cy.customMount( - ) @@ -143,10 +136,11 @@ describe('FileEmbeddedExternalTool', () => { .stub() .rejects(new WriteError('Some js dataverse processed error message.')) cy.customMount( - ) @@ -161,10 +155,11 @@ describe('FileEmbeddedExternalTool', () => { .rejects(new Error('Failed to fetch tool URL')) cy.customMount( - ) diff --git a/tests/component/sections/session/SessionProvider.spec.tsx b/tests/component/sections/session/SessionProvider.spec.tsx index 79b575880..cce06c8e7 100644 --- a/tests/component/sections/session/SessionProvider.spec.tsx +++ b/tests/component/sections/session/SessionProvider.spec.tsx @@ -1,4 +1,4 @@ -import { Route, Routes, useLocation } from 'react-router-dom' +import { Route, Routes } from 'react-router-dom' import { AuthContext } from 'react-oauth2-code-pkce' import { ReadError } from '@iqss/dataverse-client-javascript' import { UserRepository } from '@/users/domain/repositories/UserRepository' @@ -9,7 +9,6 @@ import { SessionProvider } from '@/sections/session/SessionProvider' import { useSession } from '@/sections/session/SessionContext' -import { WithRepositories } from '@tests/component/WithRepositories' const userRepository: UserRepository = {} as UserRepository const testUser = UserMother.create() @@ -35,12 +34,6 @@ describe('SessionProvider', () => { ) } - const LocationDisplay = () => { - const location = useLocation() - - return

{`${location.pathname}${location.search}`}

- } - const renderComponent = ({ loginInProgress, withTokenPresent @@ -61,23 +54,12 @@ describe('SessionProvider', () => { error: null, login: () => {} // 👈 deprecated }}> - - - }> - } /> - - - -
Sign up
- - } - />{' '} -
-
-
+ + }> + } /> + Sign up
} /> + + ) } @@ -184,8 +166,6 @@ describe('SessionProvider', () => { }) cy.findByText('Sign up').should('exist') - cy.findByText(BEARER_TOKEN_IS_VALID_BUT_NOT_LINKED_MESSAGE).should('exist') - cy.findByTestId('location').should('have.text', '/sign-up?validTokenButNotLinkedAccount=true') }) it('should detect any other ReadError instances', () => { diff --git a/tests/component/sections/shared/citation/CitationDownloadButton.spec.tsx b/tests/component/sections/shared/citation/CitationDownloadButton.spec.tsx index 48a3520fd..2d143683e 100644 --- a/tests/component/sections/shared/citation/CitationDownloadButton.spec.tsx +++ b/tests/component/sections/shared/citation/CitationDownloadButton.spec.tsx @@ -1,9 +1,8 @@ import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' import { CitationDownloadButton } from '../../../../../src/sections/shared/citation/citation-download/CitationDownloadButton' import { FormattedCitation } from '@/dataset/domain/models/DatasetCitation' -import { WithRepositories } from '@tests/component/WithRepositories' import { ViewStyledCitationModal } from '@/sections/shared/citation/citation-download/ViewStyledCitationModal' -import i18next from '@/i18n' +import { WithRepositories } from '@tests/component/WithRepositories' const datasetRepository: DatasetRepository = {} as DatasetRepository const mockCitation: FormattedCitation = { @@ -13,16 +12,11 @@ const mockCitation: FormattedCitation = { describe('CitationDownloadButton', () => { beforeEach(() => { - cy.wrap(i18next.loadNamespaces('files')) - + // Mock URL.createObjectURL and URL.revokeObjectURL cy.window().then((win) => { - cy.stub(win.URL, 'createObjectURL').as('createObjectURL').returns('mock-url') - cy.stub(win.URL, 'revokeObjectURL').as('revokeObjectURL') + cy.stub(win.URL, 'createObjectURL').returns('mock-url') + cy.stub(win.URL, 'revokeObjectURL') }) - - cy.customMount( - {}} citation={mockCitation} /> - ) }) it('renders the button', () => { @@ -53,8 +47,10 @@ describe('CitationDownloadButton', () => { 'EndNote' ) }) - cy.get('@createObjectURL').should('have.been.called') - cy.get('@revokeObjectURL').should('have.been.called') + cy.window().then((win) => { + expect(win.URL['createObjectURL']).to.have.been.called + expect(win.URL['revokeObjectURL']).to.have.been.called + }) }) it('downloads RIS citation and triggers file download', () => { @@ -76,7 +72,9 @@ describe('CitationDownloadButton', () => { 'RIS' ) }) - cy.get('@createObjectURL').should('have.been.called') + cy.window().then((win) => { + expect(win.URL['createObjectURL']).to.have.been.called + }) }) it('downloads BibTeX citation and creates download link', () => { @@ -99,8 +97,10 @@ describe('CitationDownloadButton', () => { ) }) - cy.get('@createObjectURL').should('have.been.called') - cy.get('@revokeObjectURL').should('have.been.called') + cy.window().then((win) => { + expect(win.URL['createObjectURL']).to.have.been.called + expect(win.URL['revokeObjectURL']).to.have.been.called + }) }) it('verifies correct filename is used for download', () => { @@ -157,13 +157,15 @@ describe('CitationDownloadButton', () => { ) - cy.findByRole('button', { name: 'Cite Dataset' }).click() - cy.findByText('View Styled Citation').click() + cy.customMount( + {}} citation={mockCitation} /> + ) - cy.findByRole('dialog').should('exist') + cy.findByText('Styled Citation').click() cy.findByText('Select a CSL Style').should('exist') cy.findByText(mockCitation.content).should('exist') cy.findByRole('button', { name: /Copy to clipboard icon/ }).should('exist') + cy.findByRole('dialog').should('exist') }) it('closes styled citation modal when close is triggered', () => { @@ -179,8 +181,7 @@ describe('CitationDownloadButton', () => { cy.findByText('View Styled Citation').click() cy.findByRole('dialog').should('exist') - cy.findByText(mockCitation.content).should('exist') - cy.findByRole('button', { name: 'Cancel' }).click() + cy.findByRole('button', { name: /close/i }).click() cy.findByRole('dialog').should('not.exist') }) diff --git a/tests/component/sections/sign-up/SignUp.spec.tsx b/tests/component/sections/sign-up/SignUp.spec.tsx index 2ea92f1dd..f359d17f8 100644 --- a/tests/component/sections/sign-up/SignUp.spec.tsx +++ b/tests/component/sections/sign-up/SignUp.spec.tsx @@ -3,7 +3,6 @@ import { SignUp } from '@/sections/sign-up/SignUp' import { UserRepository } from '@/users/domain/repositories/UserRepository' import { AuthContextMother } from '@tests/component/auth/AuthContextMother' import { AuthContext } from 'react-oauth2-code-pkce' -import { WithRepositories } from '@tests/component/WithRepositories' const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository const userRepository: UserRepository = {} as UserRepository @@ -15,25 +14,24 @@ describe('SignUp', () => { it('renders the valid token not linked account form and correct alerts when hasValidTokenButNotLinkedAccount prop is true', () => { cy.customMount( - - {}, - logOut: () => {}, - loginInProgress: false, - tokenData: AuthContextMother.createTokenData(), - idTokenData: AuthContextMother.createTokenData(), - error: null, - login: () => {} // 👈 deprecated - }}> - - - + {}, + logOut: () => {}, + loginInProgress: false, + tokenData: AuthContextMother.createTokenData(), + idTokenData: AuthContextMother.createTokenData(), + error: null, + login: () => {} // 👈 deprecated + }}> + + ) cy.findByTestId('valid-token-not-linked-account-alert-text').should('exist') @@ -46,25 +44,24 @@ describe('SignUp', () => { // For now we are only rendering the form for the case when theres is a valid token but is not linked to any account, but we prepare the test for other cases it('renders the default create account alert when hasValidTokenButNotLinkedAccount prop is false', () => { cy.customMount( - - {}, - logOut: () => {}, - loginInProgress: false, - tokenData: AuthContextMother.createTokenData(), - idTokenData: AuthContextMother.createTokenData(), - error: null, - login: () => {} // 👈 deprecated - }}> - - - + {}, + logOut: () => {}, + loginInProgress: false, + tokenData: AuthContextMother.createTokenData(), + idTokenData: AuthContextMother.createTokenData(), + error: null, + login: () => {} // 👈 deprecated + }}> + + ) cy.findByTestId('default-create-account-alert-text').should('exist') diff --git a/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx b/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx index 5065cb08b..23c9ba527 100644 --- a/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx +++ b/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx @@ -7,8 +7,6 @@ import { JSTermsOfUseMapper } from '@/info/infrastructure/mappers/JSTermsOfUseMa import { ValidTokenNotLinkedAccountForm } from '@/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm' import { AuthContextMother } from '@tests/component/auth/AuthContextMother' import { TermsOfUseMother } from '@tests/component/info/domain/models/TermsOfUseMother' -import { WithRepositories } from '@tests/component/WithRepositories' -import { ComponentProps } from 'react' const userRepository: UserRepository = {} as UserRepository const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository @@ -21,14 +19,6 @@ const mockFirstName = 'mockFirstName' const mockLastName = 'mockLastName' const mockEmail = 'mockEmail@email.com' -const ValidTokenNotLinkedAccountFormWithRepositories = ( - props: ComponentProps -) => ( - - - -) - describe('ValidTokenNotLinkedAccountForm', () => { beforeEach(() => { cy.viewport(1280, 720) @@ -57,7 +47,8 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -84,7 +75,8 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -123,7 +115,8 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -163,7 +156,8 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -252,7 +246,8 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -279,7 +274,8 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -306,7 +302,8 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -344,7 +341,8 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - diff --git a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx index 497b46b01..019e13624 100644 --- a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx +++ b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx @@ -1079,8 +1079,7 @@ describe('Dataset', () => { cy.wrap( DatasetHelper.createWithFiles(FileHelper.createMany(3)).then((dataset) => DatasetHelper.publish(dataset.persistentId) - ), - { timeout: 30_000 } + ) ) .its('persistentId') .then((persistentId: string) => { diff --git a/tests/e2e-integration/shared/TestsUtils.ts b/tests/e2e-integration/shared/TestsUtils.ts index 00f0bffaa..b64d10dd4 100644 --- a/tests/e2e-integration/shared/TestsUtils.ts +++ b/tests/e2e-integration/shared/TestsUtils.ts @@ -95,8 +95,7 @@ export class TestsUtils { } cy.findByTestId('sign-up-page').should('be.visible') - cy.findByTestId('valid-token-not-linked-account-form').should('exist') - cy.findByTestId('termsAcceptedCheckbox').check({ force: true }) + cy.get('#termsAccepted').should('be.visible').check({ force: true }) cy.findByRole('button', { name: 'Create Account' }).should('be.enabled').click() }) } diff --git a/tests/support/commands.tsx b/tests/support/commands.tsx index 29162a078..9b989c77d 100644 --- a/tests/support/commands.tsx +++ b/tests/support/commands.tsx @@ -53,7 +53,6 @@ import { requireAppConfig } from '@/config' import { ToastContainer } from 'react-toastify' import { ExternalToolsProvider } from '@/shared/contexts/external-tools/ExternalToolsProvider' import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' -import { WithRepositories } from '@tests/component/WithRepositories' // Define your custom mount function @@ -75,11 +74,9 @@ Cypress.Commands.add( return cy.mount( - - - - - + + + diff --git a/vite.config.ts b/vite.config.ts index 9b612ecd2..472516589 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -42,9 +42,6 @@ export default defineConfig({ } }) ], - optimizeDeps: { - include: ['react-dom/client'] - }, preview: { port: 5173 }, From 9dc992b3a4979ed243b2bcac71149e44158b1ad4 Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Thu, 16 Jul 2026 12:36:53 -0400 Subject: [PATCH 10/15] fix: avoid prop-drilling with exportMetadata repo --- .github/workflows/deploy-beta-testing.yml | 2 +- .github/workflows/deploy.yml | 6 +- .storybook/preview.tsx | 10 +- CHANGELOG.md | 4 +- src/App.tsx | 22 +- src/router/routes.tsx | 5 +- src/sections/account/Account.tsx | 5 +- src/sections/account/AccountFactory.tsx | 3 - .../api-token-section/ApiTokenSection.tsx | 15 +- src/sections/collection/Collection.tsx | 4 +- src/sections/dataset/Dataset.tsx | 6 - src/sections/dataset/DatasetFactory.tsx | 33 +- .../DatasetToolsOptions.tsx | 14 +- .../dataset/dataset-files/DatasetFiles.tsx | 13 +- .../dataset-files/DatasetFilesScrollable.tsx | 12 +- .../dataset-files/files-table/FilesTable.tsx | 9 +- .../FilesTableColumnsDefinition.tsx | 7 +- .../files-table/FilesTableScrollable.tsx | 13 +- .../file-actions/FileActionsHeader.tsx | 10 +- .../DatasetDeleteFileButton.tsx | 10 +- .../DatasetEditFileTagsButton.tsx | 5 +- .../DatasetRestrictFileButton.tsx | 5 +- .../edit-files-menu/EditFilesMenu.tsx | 11 +- .../edit-files-menu/EditFilesOptions.tsx | 12 +- .../file-actions-cell/FileActionsCell.tsx | 6 +- .../file-action-buttons/FileActionButtons.tsx | 6 +- .../DownloadWithTermsAndGuestbookModal.tsx | 4 +- .../file-options-menu/FileOptionsMenu.tsx | 11 +- .../files-table/useFilesTable.tsx | 16 +- .../files-table/useFilesTableScrollable.tsx | 14 +- .../dataset-guestbook/DatasetGuestbook.tsx | 4 +- .../ExportMetadataDropdown.tsx | 1 - .../useExportMetadata.ts | 5 +- .../dataset/dataset-terms/DatasetTerms.tsx | 7 +- .../edit-dataset-terms/EditDatasetTerms.tsx | 15 +- .../EditDatasetTermsFactory.tsx | 3 - .../edit-guestbook/EditGuestbook.tsx | 10 +- src/sections/file/File.tsx | 3 +- src/sections/file/FileFactory.tsx | 11 +- .../access-file-menu/FileToolOptions.tsx | 15 +- .../FileEmbeddedExternalTool.tsx | 7 +- .../guestbooks/GuestbookRepositoryContext.ts | 9 - .../GuestbookRepositoryProvider.tsx | 18 - src/sections/session/SessionProvider.tsx | 13 +- .../shared/pagination/PaginationControls.tsx | 34 +- src/sections/sign-up/SignUp.tsx | 8 +- src/sections/sign-up/SignUpFactory.tsx | 3 - .../FormFields.tsx | 6 +- .../ValidTokenNotLinkedAccountForm.tsx | 11 +- .../external-tools/ExternalToolsProvider.tsx | 16 +- .../repositories/RepositoriesProvider.tsx | 57 ++- src/stories/WithRepositories.tsx | 32 +- src/stories/account/Account.stories.tsx | 5 +- .../AccountInfoSection.stories.tsx | 5 +- .../ApiTokenSection.stories.tsx | 20 +- .../NotificationSection.stories.tsx | 10 +- .../CollectionCard.stories.tsx | 48 ++- src/stories/dataset/Dataset.stories.tsx | 58 ++- .../AccessDatasetMenu.stories.tsx | 29 +- .../dataset-files/DatasetFiles.stories.tsx | 60 +-- .../DatasetFilesScrollable.stories.tsx | 60 +-- .../edit-files-menu/EditFilesMenu.stories.tsx | 13 +- .../FileOptionsMenu.stories.tsx | 47 +-- .../DatasetGuestbook.stories.tsx | 6 +- .../DatasetMetadata.stories.tsx | 30 +- .../dataset-terms/DatasetTerms.stories.tsx | 60 +-- .../EditDatasetTerms.stories.tsx | 10 +- src/stories/file/File.stories.tsx | 62 +-- .../AccessFileMenu.stories.tsx | 23 +- .../EditFileDropdown.stories.tsx | 37 +- .../FileEmbeddedExternalTool.stories.tsx | 37 +- .../file-metadata/FileMetadata.stories.tsx | 9 +- ...loadWithTermsAndGuestbookModal.stories.tsx | 6 +- src/stories/sign-up/SignUp.stories.tsx | 34 +- tests/component/WithRepositories.tsx | 20 +- .../sections/account/Account.spec.tsx | 3 - .../sections/account/ApiTokenSection.spec.tsx | 13 +- .../sections/dataset/Dataset.spec.tsx | 100 ++++- .../DatasetToolOptions.spec.tsx | 97 +++-- .../AccessDatasetMenu.spec.tsx | 354 ++++++++---------- .../dataset-files/DatasetFiles.spec.tsx | 156 ++------ .../DatasetFilesScrollable.spec.tsx | 117 ++---- .../files-table/FilesTable.spec.tsx | 30 +- .../file-actions/FileActionsHeader.spec.tsx | 4 +- .../DownloadFilesButton.spec.tsx | 136 ++++--- .../edit-files-menu/EditFilesMenu.spec.tsx | 34 +- .../edit-files-menu/EditFilesOptions.spec.tsx | 192 +++------- .../FileActionButtons.spec.tsx | 8 +- .../file-action-buttons/FileTools.spec.tsx | 25 +- .../FileOptionsMenu.spec.tsx | 53 +-- ...ownloadWithTermsAndGuestbookModal.spec.tsx | 6 +- .../DatasetGuestbook.spec.tsx | 6 +- .../useExportMetadata.spec.tsx | 14 +- .../useGetDatasetReviews.spec.tsx | 104 +++++ .../dataset-terms/DatasetTerms.spec.tsx | 107 +++--- .../EditDatasetTerms.spec.tsx | 38 +- .../edit-dataset-terms/EditGuestbook.spec.tsx | 118 ++---- .../EditTermsOfAccess.spec.tsx | 4 + tests/component/sections/file/File.spec.tsx | 30 +- .../access-file-menu/AccessFileMenu.spec.tsx | 10 +- .../access-file-menu/FileToolOptions.spec.tsx | 97 +++-- .../FileEmbeddedExternalTool.spec.tsx | 25 +- .../sections/session/SessionProvider.spec.tsx | 34 +- .../citation/CitationDownloadButton.spec.tsx | 41 +- .../sections/sign-up/SignUp.spec.tsx | 75 ++-- .../ValidTokenNotLinkedAccountForm.spec.tsx | 34 +- .../e2e/sections/dataset/Dataset.spec.tsx | 3 +- tests/e2e-integration/shared/TestsUtils.ts | 3 +- tests/support/commands.tsx | 9 +- vite.config.ts | 3 + 110 files changed, 1593 insertions(+), 1760 deletions(-) delete mode 100644 src/sections/guestbooks/GuestbookRepositoryContext.ts delete mode 100644 src/sections/guestbooks/GuestbookRepositoryProvider.tsx create mode 100644 tests/component/sections/dataset/dataset-reviews/useGetDatasetReviews.spec.tsx diff --git a/.github/workflows/deploy-beta-testing.yml b/.github/workflows/deploy-beta-testing.yml index 238d0fb93..56a60b5ef 100644 --- a/.github/workflows/deploy-beta-testing.yml +++ b/.github/workflows/deploy-beta-testing.yml @@ -97,7 +97,7 @@ jobs: script: | APPLICATION_NAME=dataverse-frontend APPLICATION_WAR_PATH=deployment/payara/target/$APPLICATION_NAME.war - ASADMIN='/usr/local/payara6/bin/asadmin --user admin' + ASADMIN='/usr/local/payara7/bin/asadmin --user admin' DATAVERSE_FRONTEND=`$ASADMIN list-applications |grep $APPLICATION_NAME |awk '{print $1}'` $ASADMIN undeploy $DATAVERSE_FRONTEND $ASADMIN deploy --name $APPLICATION_NAME --contextroot ${{ env.FRONTEND_BASE_PATH }} $APPLICATION_WAR_PATH diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3a17e535b..0fa39dd5d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -155,7 +155,7 @@ jobs: script: | APPLICATION_NAME=dataverse-frontend APPLICATION_WAR_PATH=deployment/payara/target/$APPLICATION_NAME.war - ASADMIN='/usr/local/payara6/bin/asadmin --user admin' + ASADMIN='/usr/local/payara7/bin/asadmin --user admin' DATAVERSE_FRONTEND=`$ASADMIN list-applications |grep $APPLICATION_NAME |awk '{print $1}'` $ASADMIN undeploy $DATAVERSE_FRONTEND $ASADMIN deploy --name $APPLICATION_NAME --contextroot /${{ github.event.inputs.basepath }} $APPLICATION_WAR_PATH @@ -171,7 +171,7 @@ jobs: script: | APPLICATION_NAME=dataverse-frontend APPLICATION_WAR_PATH=/tmp/deployment/payara/target/$APPLICATION_NAME.war - ASADMIN='/usr/local/payara6/bin/asadmin --user admin' + ASADMIN='/usr/local/payara7/bin/asadmin --user admin' DATAVERSE_FRONTEND=`$ASADMIN list-applications |grep $APPLICATION_NAME |awk '{print $1}'` $ASADMIN undeploy $DATAVERSE_FRONTEND $ASADMIN deploy --name $APPLICATION_NAME --contextroot /${{ github.event.inputs.basepath }} $APPLICATION_WAR_PATH @@ -187,7 +187,7 @@ jobs: script: | APPLICATION_NAME=dataverse-frontend APPLICATION_WAR_PATH=/tmp/deployment/payara/target/$APPLICATION_NAME.war - ASADMIN='/usr/local/payara6/bin/asadmin --user admin' + ASADMIN='/usr/local/payara7/bin/asadmin --user admin' DATAVERSE_FRONTEND=`$ASADMIN list-applications |grep $APPLICATION_NAME |awk '{print $1}'` $ASADMIN undeploy $DATAVERSE_FRONTEND $ASADMIN deploy --name $APPLICATION_NAME --contextroot /${{ github.event.inputs.basepath }} $APPLICATION_WAR_PATH diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 9c9223e68..5b68e2be5 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -5,6 +5,7 @@ import { createBrowserRouter, RouteObject, RouterProvider } from 'react-router-d import { FakerHelper } from '../tests/component/shared/FakerHelper' import { ExternalToolsProvider } from '../src/shared/contexts/external-tools/ExternalToolsProvider' import { ExternalToolsEmptyMockRepository } from '../src/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' +import { RepositoriesStoryProvider } from '../src/stories/WithRepositories' import 'react-loading-skeleton/dist/skeleton.css' import '../src/assets/global.scss' import '../src/assets/swal-custom.scss' @@ -40,9 +41,12 @@ const preview: Preview = { return ( - - - + + + + + ) } diff --git a/CHANGELOG.md b/CHANGELOG.md index 221028b82..5167090ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,9 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel ### Changed -- Hide "Export Metadata" on file pages that are not for the latest released dataset version. +- Hide "Export Metadata" on dataset and file pages that are not for the latest released dataset version. +- Show "Export Metadata" on dataset and file pages for draft version. +- Avoided prop-drilling for file, guestbook, user and external tool repository, so used context to share repository instances. ### Fixed diff --git a/src/App.tsx b/src/App.tsx index 07ef2e934..11c402b4f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,6 +8,9 @@ import { ExternalToolsJSDataverseRepository } from './externalTools/infrastructu import { RepositoriesProvider } from './shared/contexts/repositories/RepositoriesProvider' import { CollectionJSDataverseRepository } from './collection/infrastructure/repositories/CollectionJSDataverseRepository' import { DatasetJSDataverseRepository } from './dataset/infrastructure/repositories/DatasetJSDataverseRepository' +import { FileJSDataverseRepository } from './files/infrastructure/FileJSDataverseRepository' +import { GuestbookJSDataverseRepository } from './guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository' +import { UserJSDataverseRepository } from './users/infrastructure/repositories/UserJSDataverseRepository' import 'react-loading-skeleton/dist/skeleton.css' import './assets/global.scss' import './assets/react-toastify-custom.scss' @@ -16,6 +19,9 @@ import './assets/swal-custom.scss' const externalToolsRepository = new ExternalToolsJSDataverseRepository() const collectionRepository = new CollectionJSDataverseRepository() const datasetRepository = new DatasetJSDataverseRepository() +const fileRepository = new FileJSDataverseRepository() +const guestbookRepository = new GuestbookJSDataverseRepository() +const userRepository = new UserJSDataverseRepository() function App() { const appConfig = requireAppConfig() @@ -38,13 +44,17 @@ function App() { return ( <> - - + + - - + + diff --git a/src/router/routes.tsx b/src/router/routes.tsx index c664b40fa..976ca5e97 100644 --- a/src/router/routes.tsx +++ b/src/router/routes.tsx @@ -1,6 +1,5 @@ import { lazy, Suspense } from 'react' import { Navigate, RouteObject } from 'react-router-dom' -import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' import { Route } from '@/sections/Route.enum' import { Layout } from '@/sections/layout/Layout' import { ErrorPage } from '@/sections/error-page/ErrorPage' @@ -9,8 +8,6 @@ import { AuthCallback } from '@/sections/auth-callback/AuthCallback' import { SessionProvider } from '@/sections/session/SessionProvider' import { ProtectedRoute } from './ProtectedRoute' -const userRepository = new UserJSDataverseRepository() - const Homepage = lazy(() => import('../sections/homepage/HomepageFactory').then(({ HomepageFactory }) => ({ default: () => HomepageFactory.create() @@ -155,7 +152,7 @@ const AdvancedSearchPage = lazy(() => export const routes: RouteObject[] = [ { - element: , + element: , children: [ { path: '/', diff --git a/src/sections/account/Account.tsx b/src/sections/account/Account.tsx index bd29f9185..a53bbd1d9 100644 --- a/src/sections/account/Account.tsx +++ b/src/sections/account/Account.tsx @@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next' import { useSearchParams } from 'react-router-dom' import { Tabs } from '@iqss/dataverse-design-system' import { AccountHelper, AccountPanelTabKey } from './AccountHelper' -import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' import { NotificationRepository } from '@/notifications/domain/repositories/NotificationRepository' import { ApiTokenSection } from './api-token-section/ApiTokenSection' import { AccountInfoSection } from './account-info-section/AccountInfoSection' @@ -17,14 +16,12 @@ const tabsKeys = AccountHelper.ACCOUNT_PANEL_TABS_KEYS interface AccountProps { defaultActiveTabKey: AccountPanelTabKey - userRepository: UserJSDataverseRepository roleRepository: RoleJSDataverseRepository notificationRepository: NotificationRepository } export const Account = ({ defaultActiveTabKey, - userRepository, roleRepository, notificationRepository }: AccountProps) => { @@ -66,7 +63,7 @@ export const Account = ({
- +
diff --git a/src/sections/account/AccountFactory.tsx b/src/sections/account/AccountFactory.tsx index a0fa99c97..a127ec977 100644 --- a/src/sections/account/AccountFactory.tsx +++ b/src/sections/account/AccountFactory.tsx @@ -2,11 +2,9 @@ import { ReactElement } from 'react' import { useSearchParams } from 'react-router-dom' import { AccountHelper } from './AccountHelper' import { Account } from './Account' -import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' import { RoleJSDataverseRepository } from '@/roles/infrastructure/repositories/RoleJSDataverseRepository' import { NotificationJSDataverseRepository } from '@/notifications/infrastructure/repositories/NotificationJSDataverseRepository' -const userRepository = new UserJSDataverseRepository() const roleRepository = new RoleJSDataverseRepository() const notificationRepository = new NotificationJSDataverseRepository() @@ -23,7 +21,6 @@ function AccountWithSearchParams() { return ( diff --git a/src/sections/account/api-token-section/ApiTokenSection.tsx b/src/sections/account/api-token-section/ApiTokenSection.tsx index a9efd7374..212d7eea7 100644 --- a/src/sections/account/api-token-section/ApiTokenSection.tsx +++ b/src/sections/account/api-token-section/ApiTokenSection.tsx @@ -7,21 +7,18 @@ import { useRevokeApiToken } from './useRevokeApiToken' import { ApiTokenSectionSkeleton } from './ApiTokenSectionSkeleton' import { TokenInfo } from '@/users/domain/models/TokenInfo' import { DateHelper } from '@/shared/helpers/DateHelper' -import { UserRepository } from '@/users/domain/repositories/UserRepository' import { Button } from '@iqss/dataverse-design-system' import { Alert } from '@iqss/dataverse-design-system' +import { useUserRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import accountStyles from '../Account.module.scss' import styles from './ApiTokenSection.module.scss' -interface ApiTokenSectionProps { - repository: UserRepository -} - -export const ApiTokenSection = ({ repository }: ApiTokenSectionProps) => { +export const ApiTokenSection = () => { + const { userRepository } = useUserRepositories() const { t } = useTranslation('account', { keyPrefix: 'apiToken' }) const [currentApiTokenInfo, setCurrentApiTokenInfo] = useState() - const { error: getError, apiTokenInfo, isLoading } = useGetApiToken(repository) + const { error: getError, apiTokenInfo, isLoading } = useGetApiToken(userRepository) useEffect(() => { setCurrentApiTokenInfo(apiTokenInfo) @@ -32,7 +29,7 @@ export const ApiTokenSection = ({ repository }: ApiTokenSectionProps) => { isRecreating, error: recreatingError, apiTokenInfo: updatedTokenInfo - } = useRecreateApiToken(repository) + } = useRecreateApiToken(userRepository) useEffect(() => { if (updatedTokenInfo) { @@ -44,7 +41,7 @@ export const ApiTokenSection = ({ repository }: ApiTokenSectionProps) => { void recreateToken() } - const { revokeToken, isRevoking, error: revokingError } = useRevokeApiToken(repository) + const { revokeToken, isRevoking, error: revokingError } = useRevokeApiToken(userRepository) const handleRevokeToken = async () => { await revokeToken() diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index 212eafcbc..bc3fdb137 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -22,7 +22,7 @@ import { ContactRepository } from '@/contact/domain/repositories/ContactReposito import { NotFoundPage } from '../not-found-page/NotFoundPage' import { LinkCollectionDropdown } from './link-collection-dropdown/LinkCollectionDropdown' import { useSession } from '../session/SessionContext' -import { useCollectionRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' +import { useRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from './Collection.module.scss' interface CollectionProps { @@ -41,7 +41,7 @@ export function Collection({ contactRepository, accountCreated }: CollectionProps) { - const { collectionRepository } = useCollectionRepositories() + const { collectionRepository } = useRepositories() useScrollTop() const { previousPath } = useHistoryTracker() const previousPathIsHomepage = previousPath === Route.HOME diff --git a/src/sections/dataset/Dataset.tsx b/src/sections/dataset/Dataset.tsx index 68d5dfaa4..4bac21004 100644 --- a/src/sections/dataset/Dataset.tsx +++ b/src/sections/dataset/Dataset.tsx @@ -11,7 +11,6 @@ import { DatasetMetadata } from './dataset-metadata/DatasetMetadata' import { DatasetSummary } from './dataset-summary/DatasetSummary' import { DatasetCitation } from './dataset-citation/DatasetCitation' import { DatasetFiles } from './dataset-files/DatasetFiles' -import { FileRepository } from '../../files/domain/repositories/FileRepository' import { DatasetActionButtons } from './dataset-action-buttons/DatasetActionButtons' import { useDataset } from './DatasetContext' import { useNotImplementedModal } from '../not-implemented/NotImplementedModalContext' @@ -35,7 +34,6 @@ import { useDatasetRepositories } from '@/shared/contexts/repositories/Repositor import { DatasetReviews } from './dataset-reviews/DatasetReviews' interface DatasetProps { - fileRepository: FileRepository metadataBlockInfoRepository: MetadataBlockInfoRepository contactRepository: ContactRepository dataverseInfoRepository: DataverseInfoRepository @@ -45,7 +43,6 @@ interface DatasetProps { } export function Dataset({ - fileRepository, metadataBlockInfoRepository, contactRepository, dataverseInfoRepository, @@ -175,7 +172,6 @@ export function Dataset({
{filesTabInfiniteScrollEnabled ? ( ) : ( @@ -207,7 +202,6 @@ export function Dataset({ - - - - - - - - - - - - - - + + + + + + + + + + + + + ) } } @@ -82,7 +77,6 @@ function DatasetWithSearchParams() { searchParams={{ privateUrlToken: privateUrlToken }} isPublishing={publishInProgress}> { const { t } = useTranslation('shared') - const { datasetExploreTools, datasetConfigureTools, externalToolsRepository } = useExternalTools() + const { datasetExploreTools, datasetConfigureTools } = useExternalTools() const tools = kind === 'explore' ? datasetExploreTools : datasetConfigureTools if (!tools || tools.length === 0) return null @@ -35,7 +35,6 @@ const DatasetToolOptions = ({ persistentId, kind }: DatasetToolOptionsProps) => @@ -48,15 +47,10 @@ interface ToolOptionProps { toolId: number toolDisplayName: string persistentId: string - externalToolsRepository: ExternalToolsRepository } -const ToolOption = ({ - toolId, - toolDisplayName, - persistentId, - externalToolsRepository -}: ToolOptionProps) => { +const ToolOption = ({ toolId, toolDisplayName, persistentId }: ToolOptionProps) => { + const { externalToolsRepository } = useExternalToolsRepositories() const [isOpening, setIsOpening] = useState(false) const { t, i18n } = useTranslation('shared') const openingRef = useRef(false) diff --git a/src/sections/dataset/dataset-files/DatasetFiles.tsx b/src/sections/dataset/dataset-files/DatasetFiles.tsx index df6a4820f..d321e6298 100644 --- a/src/sections/dataset/dataset-files/DatasetFiles.tsx +++ b/src/sections/dataset/dataset-files/DatasetFiles.tsx @@ -1,4 +1,3 @@ -import { FileRepository } from '../../../files/domain/repositories/FileRepository' import { useState } from 'react' import { FilesTable } from './files-table/FilesTable' import { FileCriteriaForm } from './file-criteria-form/FileCriteriaForm' @@ -7,22 +6,19 @@ import { useFiles } from './useFiles' import { PaginationControls } from '../../shared/pagination/PaginationControls' import { DatasetVersion } from '../../../dataset/domain/models/Dataset' import { FilePaginationInfo } from '../../../files/domain/models/FilePaginationInfo' +import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface DatasetFilesProps { - filesRepository: FileRepository datasetPersistentId: string datasetVersion: DatasetVersion } -export function DatasetFiles({ - filesRepository, - datasetPersistentId, - datasetVersion -}: DatasetFilesProps) { +export function DatasetFiles({ datasetPersistentId, datasetVersion }: DatasetFilesProps) { + const { fileRepository } = useDatasetRepositories() const [paginationInfo, setPaginationInfo] = useState(new FilePaginationInfo()) const [criteria, setCriteria] = useState(new FileCriteria()) const { files, isLoading, filesCountInfo, filesTotalDownloadSize } = useFiles( - filesRepository, + fileRepository, datasetPersistentId, datasetVersion, setPaginationInfo, @@ -39,7 +35,6 @@ export function DatasetFiles({ /> (null) const criteriaContainerRef = useRef(null) const criteriaContainerSize = useObserveElementSize(criteriaContainerRef) @@ -44,7 +43,7 @@ export function DatasetFilesScrollable({ isLoading: _isLoadingFilesCountInfo, error: errorFilesCountInfo } = useGetFilesCountInfo({ - filesRepository, + filesRepository: fileRepository, datasetPersistentId, datasetVersion, criteria, @@ -56,7 +55,7 @@ export function DatasetFilesScrollable({ isLoading: _isLoadingFilesTotalDownloadSize, error: errorFilesTotalDownloadSize } = useGetFilesTotalDownloadSize({ - filesRepository, + filesRepository: fileRepository, datasetPersistentId, datasetVersion, criteria, @@ -75,7 +74,7 @@ export function DatasetFilesScrollable({ isEmptyFiles, refreshFiles } = useGetAccumulatedFiles({ - filesRepository, + filesRepository: fileRepository, datasetPersistentId, datasetVersion }) @@ -183,7 +182,6 @@ export function DatasetFilesScrollable({ showSentryRef={showSentryRef} isEmptyFiles={isEmptyFiles} accumulatedCount={accumulatedCount} - fileRepository={filesRepository} />
diff --git a/src/sections/dataset/dataset-files/files-table/FilesTable.tsx b/src/sections/dataset/dataset-files/files-table/FilesTable.tsx index 319bd4ada..75afed124 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTable.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTable.tsx @@ -10,12 +10,9 @@ import { useEffect, useState } from 'react' import { FileSelection } from './row-selection/useFileSelection' import { FileCriteria } from '../../../../files/domain/models/FileCriteria' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' -import { FileRepository } from '@/files/domain/repositories/FileRepository' -import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface FilesTableProps { files: FilePreview[] - fileRepository: FileRepository isLoading: boolean paginationInfo: FilePaginationInfo filesTotalDownloadSize: number @@ -27,15 +24,11 @@ export function FilesTable({ isLoading, paginationInfo, filesTotalDownloadSize, - fileRepository, criteria }: FilesTableProps) { - const { datasetRepository } = useDatasetRepositories() const { table, fileSelection, selectAllFiles, clearFileSelection } = useFilesTable( files, - paginationInfo, - fileRepository, - datasetRepository + paginationInfo ) const [visitedPagination, setVisitedPagination] = useState(paginationInfo) diff --git a/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx b/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx index 66c8d8083..3cfba53f8 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTableColumnsDefinition.tsx @@ -7,14 +7,10 @@ import { FileActionsCell } from './file-actions/file-actions-cell/FileActionsCel import { FileSelection } from './row-selection/useFileSelection' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' import { RowSelectionCheckbox } from '@/sections/shared/form/row-selection-checkbox/RowSelectionCheckbox' -import { FileRepository } from '@/files/domain/repositories/FileRepository' -import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' export const createColumnsDefinition = ( paginationInfo: FilePaginationInfo, fileSelection: FileSelection, - fileRepository: FileRepository, - datasetRepository: DatasetRepository, accumulatedFilesCount?: number ): ColumnDef[] => [ { @@ -56,10 +52,9 @@ export const createColumnsDefinition = ( row.original)} fileSelection={fileSelection} - fileRepository={fileRepository} /> ), accessorKey: 'status', - cell: (props) => + cell: (props) => } ] diff --git a/src/sections/dataset/dataset-files/files-table/FilesTableScrollable.tsx b/src/sections/dataset/dataset-files/files-table/FilesTableScrollable.tsx index 72e9739bf..878aa45f9 100644 --- a/src/sections/dataset/dataset-files/files-table/FilesTableScrollable.tsx +++ b/src/sections/dataset/dataset-files/files-table/FilesTableScrollable.tsx @@ -11,8 +11,6 @@ import { ZipDownloadLimitMessage } from './zip-download-limit-message/ZipDownloa import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' import { type SentryRef } from '../DatasetFilesScrollable' import styles from './FilesTable.module.scss' -import { FileRepository } from '@/files/domain/repositories/FileRepository' -import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface FilesTableScrollableProps { files: FilePreview[] @@ -23,7 +21,6 @@ interface FilesTableScrollableProps { sentryRef: SentryRef showSentryRef: boolean isEmptyFiles: boolean - fileRepository: FileRepository accumulatedCount: number } @@ -36,18 +33,10 @@ export const FilesTableScrollable = ({ sentryRef, showSentryRef, isEmptyFiles, - fileRepository, accumulatedCount }: FilesTableScrollableProps) => { - const { datasetRepository } = useDatasetRepositories() const { table, fileSelection, selectAllPossibleRows, clearRowsSelection } = - useFilesTableScrollable( - files, - paginationInfo, - accumulatedCount, - fileRepository, - datasetRepository - ) + useFilesTableScrollable(files, paginationInfo, accumulatedCount) const [previousCriteria, setPreviousCriteria] = useState(criteria) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx index e7eac554b..2162ae7e7 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.tsx @@ -4,23 +4,17 @@ import styles from './FileActionsHeader.module.scss' import { useTranslation } from 'react-i18next' import { DownloadFilesButton } from './download-files/DownloadFilesButton' import { FileSelection } from '../row-selection/useFileSelection' -import { FileRepository } from '@/files/domain/repositories/FileRepository' interface FileActionsHeaderProps { files: FilePreview[] fileSelection: FileSelection - fileRepository: FileRepository } -export function FileActionsHeader({ - files, - fileSelection, - fileRepository -}: FileActionsHeaderProps) { +export function FileActionsHeader({ files, fileSelection }: FileActionsHeaderProps) { const { t } = useTranslation('files') return (
- +
) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton.tsx index 7359f5f4e..e324b7e1a 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton.tsx @@ -3,25 +3,21 @@ import { useTranslation } from 'react-i18next' import { useNavigate, useSearchParams } from 'react-router-dom' import { toast } from 'react-toastify' import { DropdownButtonItem } from '@iqss/dataverse-design-system' -import { FileRepository } from '@/files/domain/repositories/FileRepository' import { QueryParamKey, Route } from '@/sections/Route.enum' import { DatasetNonNumericVersionSearchParam } from '@/dataset/domain/models/Dataset' import { ConfirmDeleteFileModal } from '@/sections/file/file-action-buttons/edit-file-menu/delete-file-button/confirm-delete-file-modal/ConfirmDeleteFileModal' import { useDeleteFile } from '@/sections/file/file-action-buttons/edit-file-menu/delete-file-button/useDeleteFile' import { useFilesContext } from '@/sections/file/FilesContext' import { EditFilesMenuDatasetInfo } from './EditFilesOptions' +import { useFileRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface DatasetDeleteFileButtonProps { fileId: number - fileRepository: FileRepository datasetInfo: EditFilesMenuDatasetInfo } -export const DatasetDeleteFileButton = ({ - fileId, - fileRepository, - datasetInfo -}: DatasetDeleteFileButtonProps) => { +export const DatasetDeleteFileButton = ({ fileId, datasetInfo }: DatasetDeleteFileButtonProps) => { + const { fileRepository } = useFileRepositories() const [showConfirmationModal, setShowConfirmationModal] = useState(false) const navigate = useNavigate() const { t } = useTranslation('file') diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetEditFileTagsButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetEditFileTagsButton.tsx index 27a308276..3e03f4cd8 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetEditFileTagsButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetEditFileTagsButton.tsx @@ -4,17 +4,16 @@ import { DropdownButtonItem } from '@iqss/dataverse-design-system' import { EditFileTagsModal } from '@/sections/file/file-action-buttons/edit-file-menu/edit-file-tags/edit-file-tags-modal/EditFileTagsModal' import { useUpdateFileCategories } from '@/sections/file/file-action-buttons/edit-file-menu/edit-file-tags/useUpdateFileCategories' import { useUpdateFileTabularTags } from '@/sections/file/file-action-buttons/edit-file-menu/edit-file-tags/useUpdateFileTabularTags' -import { FileRepository } from '@/files/domain/repositories/FileRepository' import { toast } from 'react-toastify' import { FileLabel } from '@/files/domain/models/FileMetadata' import { useFilesContext } from '@/sections/file/FilesContext' import { QueryParamKey, Route } from '@/sections/Route.enum' import { useNavigate, useSearchParams } from 'react-router-dom' import { DatasetNonNumericVersionSearchParam } from '@/dataset/domain/models/Dataset' +import { useFileRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface EditFileTagsButtonProps { fileId: number - fileRepository: FileRepository existingLabels?: FileLabel[] datasetPersistentId: string isTabularFile: boolean @@ -22,11 +21,11 @@ interface EditFileTagsButtonProps { export const DatasetEditFileTagsButton = ({ fileId, - fileRepository, existingLabels, datasetPersistentId, isTabularFile }: EditFileTagsButtonProps) => { + const { fileRepository } = useFileRepositories() const [isModalOpen, setIsModalOpen] = useState(false) const { t } = useTranslation('file') const { refreshFiles } = useFilesContext() diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton.tsx index 9b91daaba..5716e4659 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton.tsx @@ -3,27 +3,26 @@ import { useTranslation } from 'react-i18next' import { useNavigate, useSearchParams } from 'react-router-dom' import { toast } from 'react-toastify' import { DropdownButtonItem } from '@iqss/dataverse-design-system' -import { FileRepository } from '@/files/domain/repositories/FileRepository' import { QueryParamKey, Route } from '@/sections/Route.enum' import { DatasetNonNumericVersionSearchParam } from '@/dataset/domain/models/Dataset' import { ConfirmRestrictFileModal } from '@/sections/file/file-action-buttons/edit-file-menu/restrict-file-button/confirm-restrict-file-modal/ConfirmRestrictFileModal' import { useRestrictFile } from '@/sections/file/file-action-buttons/edit-file-menu/restrict-file-button/useRestrictFile' import { useFilesContext } from '@/sections/file/FilesContext' import { EditFilesMenuDatasetInfo } from './EditFilesOptions' +import { useFileRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface DatasetRestrictFileButtonProps { fileId: number isRestricted: boolean - fileRepository: FileRepository datasetInfo: EditFilesMenuDatasetInfo } export const DatasetRestrictFileButton = ({ fileId, isRestricted, - fileRepository, datasetInfo }: DatasetRestrictFileButtonProps) => { + const { fileRepository } = useFileRepositories() const [showConfirmationModal, setShowConfirmationModal] = useState(false) const navigate = useNavigate() const { t } = useTranslation('file') diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx index df49cb577..e03335c43 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.tsx @@ -7,16 +7,14 @@ import { useTranslation } from 'react-i18next' import { useDataset } from '../../../../DatasetContext' import { FileSelection } from '../../row-selection/useFileSelection' import { useMediaQuery } from '../../../../../../shared/hooks/useMediaQuery' -import { FileRepository } from '@/files/domain/repositories/FileRepository' import styles from './EditFilesMenu.module.scss' interface EditFilesMenuProps { files: FilePreview[] fileSelection: FileSelection - fileRepository: FileRepository } const MINIMUM_FILES_COUNT_TO_SHOW_EDIT_FILES_BUTTON = 1 -export function EditFilesMenu({ files, fileSelection, fileRepository }: EditFilesMenuProps) { +export function EditFilesMenu({ files, fileSelection }: EditFilesMenuProps) { const { t } = useTranslation('files') const { user } = useSession() const { dataset } = useDataset() @@ -40,12 +38,7 @@ export function EditFilesMenu({ files, fileSelection, fileRepository }: EditFile disabled={ dataset.checkIsLockedFromEdits(user.persistentId) || !dataset.hasValidTermsOfAccess }> - + ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx index 62b46755b..7c9d6bcb0 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.tsx @@ -6,7 +6,6 @@ import { useState } from 'react' import { FileSelection } from '../../row-selection/useFileSelection' import { NoSelectedFilesModal } from '../no-selected-files-modal/NoSelectedFilesModal' import { useNotImplementedModal } from '../../../../../not-implemented/NotImplementedModalContext' -import { FileRepository } from '@/files/domain/repositories/FileRepository' import { DatasetRestrictFileButton } from '@/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetRestrictFileButton' import { DatasetDeleteFileButton } from '@/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/DatasetDeleteFileButton' import { RouteWithParams } from '@/sections/Route.enum' @@ -21,7 +20,6 @@ type EditFilesOptionsProps = files: FilePreview[] file?: never fileSelection: FileSelection - fileRepository: FileRepository datasetInfo?: never isHeader: true } @@ -29,7 +27,6 @@ type EditFilesOptionsProps = files?: never file: FilePreview fileSelection?: never - fileRepository: FileRepository datasetInfo: EditFilesMenuDatasetInfo isHeader: false } @@ -47,7 +44,6 @@ export function EditFilesOptions({ file, files, fileSelection, - fileRepository, datasetInfo, isHeader }: EditFilesOptionsProps) { @@ -78,7 +74,6 @@ export function EditFilesOptions({ {/* TODO: remove this when we can handle non-S3 files */} @@ -97,17 +92,12 @@ export function EditFilesOptions({ - + ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.tsx index 525cb407b..111ca468f 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.tsx @@ -1,4 +1,3 @@ -import { FileRepository } from '@/files/domain/repositories/FileRepository' import { FilePreview } from '../../../../../../files/domain/models/FilePreview' import { FileActionButtons } from './file-action-buttons/FileActionButtons' import { FileInfoMessages } from './file-info-messages/FileInfoMessages' @@ -6,13 +5,12 @@ import styles from './FileActionsCell.module.scss' interface FileActionsCellProps { file: FilePreview - fileRepository: FileRepository } -export function FileActionsCell({ file, fileRepository }: FileActionsCellProps) { +export function FileActionsCell({ file }: FileActionsCellProps) { return (
- +
) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.tsx index 8f1c891f6..5b2bcd055 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.tsx @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next' import { ButtonGroup } from '@iqss/dataverse-design-system' -import { FileRepository } from '@/files/domain/repositories/FileRepository' import { FilePreview } from '@/files/domain/models/FilePreview' import { DatasetPublishingStatus } from '@/dataset/domain/models/Dataset' import { AccessFileMenu } from '@/sections/file/file-action-buttons/access-file-menu/AccessFileMenu' @@ -10,9 +9,8 @@ import { FileTools } from './FileTools' interface FileActionButtonsProps { file: FilePreview - fileRepository: FileRepository } -export function FileActionButtons({ file, fileRepository }: FileActionButtonsProps) { +export function FileActionButtons({ file }: FileActionButtonsProps) { const { t } = useTranslation('files') const isBelow768px = useMediaQuery('(max-width: 768px)') @@ -30,7 +28,7 @@ export function FileActionButtons({ file, fileRepository }: FileActionButtonsPro ingestInProgress={file.ingest.isInProgress} asIcon /> - + ) } diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx index 1e873c074..7edb8b112 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/DownloadWithTermsAndGuestbookModal.tsx @@ -12,7 +12,7 @@ import { Guestbook, GuestbookCustomQuestion } from '@/guestbooks/domain/models/G import { useGuestbookCollectSubmission } from './useGuestbookCollectSubmission' import { CustomTerms as CustomTermsModel, DatasetLicense } from '@/dataset/domain/models/Dataset' import { useAccessRepository } from '@/sections/access/AccessRepositoryContext' -import { useGuestbookRepository } from '@/sections/guestbooks/GuestbookRepositoryContext' +import { useGuestbookRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import { GuestbookAnswerDTO, GuestbookResponseDTO @@ -50,7 +50,7 @@ export function DownloadWithTermsAndGuestbookModal({ const { t: tDataset } = useTranslation('dataset') const { user } = useSession() const accessRepository = useAccessRepository() - const guestbookRepository = useGuestbookRepository() + const { guestbookRepository } = useGuestbookRepositories() const hasGuestbook = guestbookId !== undefined const [formValues, setFormValues] = useState({}) diff --git a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.tsx b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.tsx index 325e3adaa..1cf941f25 100644 --- a/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.tsx @@ -7,16 +7,14 @@ import { useTranslation } from 'react-i18next' import { useState } from 'react' import { FileAlreadyDeletedModal } from './FileAlreadyDeletedModal' import { useDataset } from '../../../../../../DatasetContext' -import { FileRepository } from '@/files/domain/repositories/FileRepository' import { EditFilesMenuDatasetInfo } from '../../../edit-files-menu/EditFilesOptions' import { FileConfigureToolsOptions } from '@/sections/file/file-action-buttons/access-file-menu/FileToolOptions' interface FileOptionsMenuProps { file: FilePreview - fileRepository: FileRepository } -export function FileOptionsMenu({ file, fileRepository }: FileOptionsMenuProps) { +export function FileOptionsMenu({ file }: FileOptionsMenuProps) { const { t } = useTranslation('files') const { user } = useSession() const { dataset } = useDataset() @@ -71,12 +69,7 @@ export function FileOptionsMenu({ file, fileRepository }: FileOptionsMenuProps) {t('actions.optionsMenu.headers.editOptions')} - + diff --git a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx index 09202cda5..02e7be148 100644 --- a/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx +++ b/src/sections/dataset/dataset-files/files-table/useFilesTable.tsx @@ -4,19 +4,12 @@ import { getCoreRowModel, Row, useReactTable } from '@tanstack/react-table' import { createColumnsDefinition } from './FilesTableColumnsDefinition' import { useFileSelection } from './row-selection/useFileSelection' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' -import { FileRepository } from '@/files/domain/repositories/FileRepository' -import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' export type RowSelection = { [key: string]: boolean } -export function useFilesTable( - files: FilePreview[], - paginationInfo: FilePaginationInfo, - fileRepository: FileRepository, - datasetRepository: DatasetRepository -) { +export function useFilesTable(files: FilePreview[], paginationInfo: FilePaginationInfo) { const [currentPageRowSelection, setCurrentPageRowSelection] = useState({}) const [currentPageSelectedRowModel, setCurrentPageSelectedRowModel] = useState< Record> @@ -28,12 +21,7 @@ export function useFilesTable( ) const table = useReactTable({ data: files, - columns: createColumnsDefinition( - paginationInfo, - fileSelection, - fileRepository, - datasetRepository - ), + columns: createColumnsDefinition(paginationInfo, fileSelection), state: { rowSelection: currentPageRowSelection }, diff --git a/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx b/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx index 7eefb8dac..3210028f1 100644 --- a/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx +++ b/src/sections/dataset/dataset-files/files-table/useFilesTableScrollable.tsx @@ -4,8 +4,6 @@ import { FilePreview } from '../../../../files/domain/models/FilePreview' import { getCoreRowModel, Row, useReactTable } from '@tanstack/react-table' import { createColumnsDefinition } from './FilesTableColumnsDefinition' import { FilePaginationInfo } from '../../../../files/domain/models/FilePaginationInfo' -import { FileRepository } from '@/files/domain/repositories/FileRepository' -import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' export type RowSelection = { [key: string]: boolean @@ -18,9 +16,7 @@ export type FileSelection = { export function useFilesTableScrollable( files: FilePreview[], paginationInfo: FilePaginationInfo, - accumulatedFilesCount: number, - fileRepository: FileRepository, - datasetRepository: DatasetRepository + accumulatedFilesCount: number ) { const [rowSelection, setRowSelection] = useState({}) const [selectedRowsModels, setSelectedRowsModels] = useState>>({}) @@ -42,13 +38,7 @@ export function useFilesTableScrollable( const table = useReactTable({ data: files, - columns: createColumnsDefinition( - paginationInfo, - fileSelection, - fileRepository, - datasetRepository, - accumulatedFilesCount - ), + columns: createColumnsDefinition(paginationInfo, fileSelection, accumulatedFilesCount), state: { rowSelection: rowSelection }, diff --git a/src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx b/src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx index 338d37c18..1b4da518a 100644 --- a/src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx +++ b/src/sections/dataset/dataset-guestbook/DatasetGuestbook.tsx @@ -4,13 +4,13 @@ import { Button, Col, QuestionMarkTooltip, Row, Spinner } from '@iqss/dataverse- import { useGetGuestbookById } from './useGetGuestbookById' import { PreviewGuestbookModal } from '@/sections/guestbooks/preview-modal/PreviewGuestbookModal' import { useDataset } from '@/sections/dataset/DatasetContext' -import { useGuestbookRepository } from '@/sections/guestbooks/GuestbookRepositoryContext' +import { useGuestbookRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from '@/sections/dataset/dataset-terms/DatasetTerms.module.scss' export const DatasetGuestbook = () => { const { t } = useTranslation('dataset') const { dataset } = useDataset() - const guestbookRepository = useGuestbookRepository() + const { guestbookRepository } = useGuestbookRepositories() const [showPreview, setShowPreview] = useState(false) const { guestbook, isLoadingGuestbook } = useGetGuestbookById({ guestbookRepository, diff --git a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx index 0068955c4..57ebbfbb2 100644 --- a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx +++ b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx @@ -41,7 +41,6 @@ export const ExportMetadataDropdown = ({ const { datasetMetadataExportFormats, isLoadingExportFormats, errorGetExportFormats } = useGetAvailableDatasetMetadataExportFormats({ dataverseInfoRepository }) const { handleExportMetadata } = useExportMetadata({ - datasetRepository, datasetPersistentId, datasetVersion }) diff --git a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.ts b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.ts index 62b371822..c83a85d97 100644 --- a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.ts +++ b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.ts @@ -3,11 +3,10 @@ import { useTranslation } from 'react-i18next' import { toast } from 'react-toastify' import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' import { DatasetPublishingStatus, DatasetVersion } from '@/dataset/domain/models/Dataset' -import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' import { exportDatasetMetadata } from '@/dataset/domain/useCases/exportDatasetMetadata' +import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface UseExportMetadataParams { - datasetRepository: DatasetRepository datasetPersistentId: string datasetVersion: DatasetVersion } @@ -17,10 +16,10 @@ interface UseExportMetadataReturn { } export const useExportMetadata = ({ - datasetRepository, datasetPersistentId, datasetVersion }: UseExportMetadataParams): UseExportMetadataReturn => { + const { datasetRepository } = useDatasetRepositories() const { t } = useTranslation('shared') const handleExportMetadata = useCallback( diff --git a/src/sections/dataset/dataset-terms/DatasetTerms.tsx b/src/sections/dataset/dataset-terms/DatasetTerms.tsx index f5d823130..bbb957d5d 100644 --- a/src/sections/dataset/dataset-terms/DatasetTerms.tsx +++ b/src/sections/dataset/dataset-terms/DatasetTerms.tsx @@ -7,7 +7,6 @@ import { import { EditDatasetTermsButton } from '@/sections/dataset/dataset-terms/EditDatasetTermsButton' import { useTranslation } from 'react-i18next' import { useGetFilesCountInfo } from '@/sections/dataset/dataset-files/useGetFilesCountInfo' -import { FileRepository } from '@/files/domain/repositories/FileRepository' import { FileAccessCount } from '@/files/domain/models/FilesCountInfo' import { FileAccessOption } from '@/files/domain/models/FileCriteria' import { SpinnerSymbol } from '@/sections/dataset/dataset-files/files-table/spinner-symbol/SpinnerSymbol' @@ -16,11 +15,11 @@ import { TermsOfAccess } from '@/sections/dataset/dataset-terms/TermsOfAccess' import { License } from '@/sections/dataset/dataset-terms/License' import { DatasetGuestbook } from '@/sections/dataset/dataset-guestbook/DatasetGuestbook' import { useSearchParams } from 'react-router-dom' +import { useDatasetRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' interface DatasetTermsProps { license: DatasetLicense | undefined termsOfUse: DatasetTermsOfUse - filesRepository: FileRepository datasetPersistentId: string datasetVersion: DatasetVersion canUpdateDataset?: boolean @@ -29,15 +28,15 @@ interface DatasetTermsProps { export function DatasetTerms({ license, termsOfUse, - filesRepository, datasetPersistentId, datasetVersion, canUpdateDataset }: DatasetTermsProps) { + const { fileRepository } = useDatasetRepositories() const { t } = useTranslation('dataset') const [searchParams] = useSearchParams() const { filesCountInfo, isLoading } = useGetFilesCountInfo({ - filesRepository, + filesRepository: fileRepository, datasetPersistentId, datasetVersion, includeDeaccessioned: canUpdateDataset diff --git a/src/sections/edit-dataset-terms/EditDatasetTerms.tsx b/src/sections/edit-dataset-terms/EditDatasetTerms.tsx index 55c39a851..65597bbbf 100644 --- a/src/sections/edit-dataset-terms/EditDatasetTerms.tsx +++ b/src/sections/edit-dataset-terms/EditDatasetTerms.tsx @@ -7,7 +7,6 @@ import { EditLicenseAndTerms } from './edit-license-and-terms/EditLicenseAndTerm import { EditTermsOfAccess } from './edit-terms-of-access/EditTermsOfAccess' import { LicenseRepository } from '../../licenses/domain/repositories/LicenseRepository' import { EditGuestbook } from './edit-guestbook/EditGuestbook' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { useDataset } from '../dataset/DatasetContext' import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator' import { NotFoundPage } from '../not-found-page/NotFoundPage' @@ -21,13 +20,11 @@ const tabsKeys = EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS interface EditDatasetTermsProps { defaultActiveTabKey: EditDatasetTermsTabKey licenseRepository: LicenseRepository - guestbookRepository: GuestbookRepository } export const EditDatasetTerms = ({ defaultActiveTabKey, - licenseRepository, - guestbookRepository + licenseRepository }: EditDatasetTermsProps) => { const { t } = useTranslation('dataset') const [activeKey, setActiveKey] = useState(defaultActiveTabKey) @@ -131,10 +128,7 @@ export const EditDatasetTerms = ({ {t('editTerms.tabs.guestbook')}
- +
@@ -160,10 +154,7 @@ export const EditDatasetTerms = ({
- +
diff --git a/src/sections/edit-dataset-terms/EditDatasetTermsFactory.tsx b/src/sections/edit-dataset-terms/EditDatasetTermsFactory.tsx index 4ce635b97..8b7197ce6 100644 --- a/src/sections/edit-dataset-terms/EditDatasetTermsFactory.tsx +++ b/src/sections/edit-dataset-terms/EditDatasetTermsFactory.tsx @@ -6,11 +6,9 @@ import { DatasetJSDataverseRepository } from '@/dataset/infrastructure/repositor import { DatasetProvider } from '../dataset/DatasetProvider' import { ReactElement } from 'react' import { DatasetNonNumericVersion } from '@/dataset/domain/models/Dataset' -import { GuestbookJSDataverseRepository } from '@/guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository' const licenseRepository = new LicenseJSDataverseRepository() const datasetRepository = new DatasetJSDataverseRepository() -const guestbookRepository = new GuestbookJSDataverseRepository() export class EditDatasetTermsFactory { static create(): ReactElement { @@ -32,7 +30,6 @@ function EditDatasetTermsWithSearchParams() { ) diff --git a/src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx b/src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx index d91e7185b..8f87175c5 100644 --- a/src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx +++ b/src/sections/edit-dataset-terms/edit-guestbook/EditGuestbook.tsx @@ -4,7 +4,6 @@ import { Trans, useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import { toast } from 'react-toastify' import { Guestbook } from '@/guestbooks/domain/models/Guestbook' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { DatasetNonNumericVersionSearchParam, DatasetPublishingStatus @@ -15,19 +14,16 @@ import { useAssignDatasetGuestbook } from './useAssignDatasetGuestbook' import { useRemoveDatasetGuestbook } from './useRemoveDatasetGuestbook' import { useDataset } from '../../dataset/DatasetContext' import { PreviewGuestbookModal } from '@/sections/guestbooks/preview-modal/PreviewGuestbookModal' +import { useGuestbookRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from './EditGuestbook.module.scss' interface EditGuestbookProps { - guestbookRepository: GuestbookRepository onPreview?: () => void onFormStateChange?: (isDirty: boolean) => void } -export function EditGuestbook({ - guestbookRepository, - onPreview, - onFormStateChange -}: EditGuestbookProps) { +export function EditGuestbook({ onPreview, onFormStateChange }: EditGuestbookProps) { + const { guestbookRepository } = useGuestbookRepositories() const { t } = useTranslation('dataset') const { t: tShared } = useTranslation('shared') const [selectedGuestbookId, setSelectedGuestbookId] = useState(undefined) diff --git a/src/sections/file/File.tsx b/src/sections/file/File.tsx index a19dfcc80..74d8b954b 100644 --- a/src/sections/file/File.tsx +++ b/src/sections/file/File.tsx @@ -48,7 +48,7 @@ export function File({ const { setIsLoading } = useLoading() const { t } = useTranslation('file') const { file, isLoading } = useFile(repository, id, datasetVersionNumber) - const { externalTools, externalToolsRepository } = useExternalTools() + const { externalTools } = useExternalTools() const [activeTab, setActiveTab] = useState( toolTypeSelectedQueryParam && file?.permissions.canDownloadFile ? FilePageHelper.EXT_TOOL_TAB_KEY @@ -198,7 +198,6 @@ export function File({ applicableTools={fileApplicablePreviewOrQueryTools} toolTypeSelectedQueryParam={toolTypeSelectedQueryParam} isInView={activeTab === FilePageHelper.EXT_TOOL_TAB_KEY} - externalToolsRepository={externalToolsRepository} /> diff --git a/src/sections/file/FileFactory.tsx b/src/sections/file/FileFactory.tsx index ff960825c..f144fca2a 100644 --- a/src/sections/file/FileFactory.tsx +++ b/src/sections/file/FileFactory.tsx @@ -9,23 +9,18 @@ import { DataverseInfoJSDataverseRepository } from '@/info/infrastructure/reposi import { ContactJSDataverseRepository } from '@/contact/infrastructure/ContactJSDataverseRepository' import { AccessJSDataverseRepository } from '@/access/infrastructure/repositories/AccessJSDataverseRepository' import { AccessRepositoryProvider } from '../access/AccessRepositoryProvider' -import { GuestbookJSDataverseRepository } from '@/guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository' -import { GuestbookRepositoryProvider } from '../guestbooks/GuestbookRepositoryProvider' const repository = new FileJSDataverseRepository() const dataverseInfoRepository = new DataverseInfoJSDataverseRepository() const contactRepository = new ContactJSDataverseRepository() const accessRepository = new AccessJSDataverseRepository() -const guestbookRepository = new GuestbookJSDataverseRepository() export class FileFactory { static create(): ReactElement { return ( - - - - - + + + ) } } diff --git a/src/sections/file/file-action-buttons/access-file-menu/FileToolOptions.tsx b/src/sections/file/file-action-buttons/access-file-menu/FileToolOptions.tsx index 1b3adf353..875021727 100644 --- a/src/sections/file/file-action-buttons/access-file-menu/FileToolOptions.tsx +++ b/src/sections/file/file-action-buttons/access-file-menu/FileToolOptions.tsx @@ -9,9 +9,9 @@ import { import { DropdownButtonItem, DropdownHeader } from '@iqss/dataverse-design-system' import { useExternalTools } from '@/shared/contexts/external-tools/ExternalToolsProvider' import { getFileExternalToolResolved } from '@/externalTools/domain/useCases/GetFileExternalToolResolved' -import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' import { FilePageHelper } from '../../FilePageHelper' import { ExternalTool } from '@/externalTools/domain/models/ExternalTool' +import { useExternalToolsRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' type ToolKind = 'explore' | 'query' | 'configure' @@ -23,8 +23,7 @@ interface FileToolOptionsProps { const FileToolOptions = ({ fileId, fileType, kind }: FileToolOptionsProps) => { const { t } = useTranslation('shared') - const { fileExploreTools, fileQueryTools, fileConfigureTools, externalToolsRepository } = - useExternalTools() + const { fileExploreTools, fileQueryTools, fileConfigureTools } = useExternalTools() /** Per-kind config (single source of truth) */ const configByKind: Record< @@ -61,7 +60,6 @@ const FileToolOptions = ({ fileId, fileType, kind }: FileToolOptionsProps) => { toolId={tool.id} toolDisplayName={tool.displayName} fileId={fileId} - externalToolsRepository={externalToolsRepository} /> ))} @@ -72,15 +70,10 @@ interface ToolOptionProps { toolId: number toolDisplayName: string fileId: number - externalToolsRepository: ExternalToolsRepository } -const ToolOption = ({ - toolId, - toolDisplayName, - fileId, - externalToolsRepository -}: ToolOptionProps) => { +const ToolOption = ({ toolId, toolDisplayName, fileId }: ToolOptionProps) => { + const { externalToolsRepository } = useExternalToolsRepositories() const [isOpening, setIsOpening] = useState(false) const { t, i18n } = useTranslation('shared') const openingRef = useRef(false) diff --git a/src/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.tsx b/src/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.tsx index c9c384660..ddaced12f 100644 --- a/src/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.tsx +++ b/src/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.tsx @@ -5,12 +5,12 @@ import { WriteError } from '@iqss/dataverse-client-javascript' import { Alert, DropdownButton, DropdownButtonItem, Spinner } from '@iqss/dataverse-design-system' import { BoxArrowUpRight } from 'react-bootstrap-icons' import { ExternalTool } from '@/externalTools/domain/models/ExternalTool' -import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' import { FileExternalToolResolved } from '@/externalTools/domain/models/FileExternalToolResolved' import { getFileExternalToolResolved } from '@/externalTools/domain/useCases/GetFileExternalToolResolved' import { JSDataverseWriteErrorHandler } from '@/shared/helpers/JSDataverseWriteErrorHandler' import { File } from '@/files/domain/models/File' import { FilePageHelper } from '../FilePageHelper' +import { useExternalToolsRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from './FileEmbeddedExternalTool.module.scss' interface FileEmbeddedExternalToolProps { @@ -18,16 +18,15 @@ interface FileEmbeddedExternalToolProps { isInView: boolean applicableTools: ExternalTool[] toolTypeSelectedQueryParam: string | undefined - externalToolsRepository: ExternalToolsRepository } export const FileEmbeddedExternalTool = ({ file, isInView, applicableTools, - toolTypeSelectedQueryParam, - externalToolsRepository + toolTypeSelectedQueryParam }: FileEmbeddedExternalToolProps) => { + const { externalToolsRepository } = useExternalToolsRepositories() const { t, i18n } = useTranslation('file', { keyPrefix: 'previewTab' }) const [toolIdSelected, setToolIdSelected] = useState( FilePageHelper.getDefaultSelectedToolId(toolTypeSelectedQueryParam, applicableTools) diff --git a/src/sections/guestbooks/GuestbookRepositoryContext.ts b/src/sections/guestbooks/GuestbookRepositoryContext.ts deleted file mode 100644 index 09fae761f..000000000 --- a/src/sections/guestbooks/GuestbookRepositoryContext.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createContext, useContext } from 'react' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { GuestbookJSDataverseRepository } from '@/guestbooks/infrastructure/repositories/GuestbookJSDataverseRepository' - -const guestbookRepository = new GuestbookJSDataverseRepository() - -export const GuestbookRepositoryContext = createContext(guestbookRepository) - -export const useGuestbookRepository = () => useContext(GuestbookRepositoryContext) diff --git a/src/sections/guestbooks/GuestbookRepositoryProvider.tsx b/src/sections/guestbooks/GuestbookRepositoryProvider.tsx deleted file mode 100644 index f4b026d5f..000000000 --- a/src/sections/guestbooks/GuestbookRepositoryProvider.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { PropsWithChildren } from 'react' -import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { GuestbookRepositoryContext } from './GuestbookRepositoryContext' - -interface GuestbookRepositoryProviderProps { - repository: GuestbookRepository -} - -export function GuestbookRepositoryProvider({ - repository, - children -}: PropsWithChildren) { - return ( - - {children} - - ) -} diff --git a/src/sections/session/SessionProvider.tsx b/src/sections/session/SessionProvider.tsx index 8e14aef36..b1a776a9f 100644 --- a/src/sections/session/SessionProvider.tsx +++ b/src/sections/session/SessionProvider.tsx @@ -5,18 +5,15 @@ import { ReadError } from '@iqss/dataverse-client-javascript' import { User } from '../../users/domain/models/User' import { SessionContext, SessionError } from './SessionContext' import { getUser } from '../../users/domain/useCases/getUser' -import { UserRepository } from '../../users/domain/repositories/UserRepository' import { JSDataverseReadErrorHandler } from '@/shared/helpers/JSDataverseReadErrorHandler' import { QueryParamKey, Route } from '../Route.enum' +import { useUserRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' export const BEARER_TOKEN_IS_VALID_BUT_NOT_LINKED_MESSAGE = 'Bearer token is validated, but there is no linked user account.' -interface SessionProviderProps { - repository: UserRepository -} - -export function SessionProvider({ repository }: SessionProviderProps) { +export function SessionProvider() { + const { userRepository } = useUserRepositories() const navigate = useNavigate() const { token, loginInProgress } = useContext(AuthContext) const [user, setUser] = useState(null) @@ -63,14 +60,14 @@ export function SessionProvider({ repository }: SessionProviderProps) { setIsLoadingUser(true) try { - const user = await getUser(repository) + const user = await getUser(userRepository) setUser(user) } catch (err) { handleFetchError(err) } finally { setIsLoadingUser(false) } - }, [repository, handleFetchError]) + }, [userRepository, handleFetchError]) const refetchUserSession = async () => { await fetchUser() diff --git a/src/sections/shared/pagination/PaginationControls.tsx b/src/sections/shared/pagination/PaginationControls.tsx index 52a79e564..26ec6e83f 100644 --- a/src/sections/shared/pagination/PaginationControls.tsx +++ b/src/sections/shared/pagination/PaginationControls.tsx @@ -18,34 +18,30 @@ export function PaginationControls>({ }: PaginationProps) { const [paginationInfo, setPaginationInfo] = useState(initialPaginationInfo) const goToPage = (newPage: number) => { - setPaginationInfo(paginationInfo.goToPage(newPage)) + const updatedPaginationInfo = paginationInfo.goToPage(newPage) + setPaginationInfo(updatedPaginationInfo) + onPaginationInfoChange(updatedPaginationInfo) } const goToPreviousPage = () => { - setPaginationInfo(paginationInfo.goToPreviousPage()) + const updatedPaginationInfo = paginationInfo.goToPreviousPage() + setPaginationInfo(updatedPaginationInfo) + onPaginationInfoChange(updatedPaginationInfo) } const goToNextPage = () => { - setPaginationInfo(paginationInfo.goToNextPage()) + const updatedPaginationInfo = paginationInfo.goToNextPage() + setPaginationInfo(updatedPaginationInfo) + onPaginationInfoChange(updatedPaginationInfo) } const setPageSize = (newPageSize: number) => { - setPaginationInfo(paginationInfo.withPageSize(newPageSize)) + const updatedPaginationInfo = paginationInfo.withPageSize(newPageSize) + setPaginationInfo(updatedPaginationInfo) + onPaginationInfoChange(updatedPaginationInfo) } useEffect(() => { - onPaginationInfoChange(paginationInfo) - // TODO: Not a priority as not used for inifinite scroll is used but the eslint disable should be removed and the dependency should be added - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [paginationInfo.pageSize]) - - useEffect(() => { - onPaginationInfoChange(paginationInfo) - // TODO: Not a priority as not used for inifinite scroll is used but the eslint disable should be removed and the dependency should be added - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [paginationInfo.page]) - - useEffect(() => { - setPaginationInfo(paginationInfo.withTotal(initialPaginationInfo.totalItems)) - // TODO: Not a priority as not used for inifinite scroll is used but the eslint disable should be removed and the dependency should be added - // eslint-disable-next-line react-hooks/exhaustive-deps + setPaginationInfo((currentPaginationInfo) => + currentPaginationInfo.withTotal(initialPaginationInfo.totalItems) + ) }, [initialPaginationInfo.totalItems]) if (paginationInfo.totalPages < MINIMUM_NUMBER_OF_PAGES_TO_DISPLAY_PAGINATION) { diff --git a/src/sections/sign-up/SignUp.tsx b/src/sections/sign-up/SignUp.tsx index 4d7e60f46..7ad02a282 100644 --- a/src/sections/sign-up/SignUp.tsx +++ b/src/sections/sign-up/SignUp.tsx @@ -1,20 +1,17 @@ import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { Alert, Tabs } from '@iqss/dataverse-design-system' -import { UserRepository } from '@/users/domain/repositories/UserRepository' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' import { useLoading } from '../../shared/contexts/loading/LoadingContext' import { ValidTokenNotLinkedAccountForm } from './valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm' import styles from './SignUp.module.scss' interface SignUpProps { - userRepository: UserRepository dataverseInfoRepository: DataverseInfoRepository hasValidTokenButNotLinkedAccount: boolean } export const SignUp = ({ - userRepository, dataverseInfoRepository, hasValidTokenButNotLinkedAccount }: SignUpProps) => { @@ -64,10 +61,7 @@ export const SignUp = ({
{hasValidTokenButNotLinkedAccount && ( - + )}
diff --git a/src/sections/sign-up/SignUpFactory.tsx b/src/sections/sign-up/SignUpFactory.tsx index 9fa9879c3..3444d7fba 100644 --- a/src/sections/sign-up/SignUpFactory.tsx +++ b/src/sections/sign-up/SignUpFactory.tsx @@ -2,10 +2,8 @@ import { ReactElement } from 'react' import { useSearchParams } from 'react-router-dom' import { SignUp } from './SignUp' import { QueryParamKey } from '../Route.enum' -import { UserJSDataverseRepository } from '@/users/infrastructure/repositories/UserJSDataverseRepository' import { DataverseInfoJSDataverseRepository } from '@/info/infrastructure/repositories/DataverseInfoJSDataverseRepository' -const userRepository = new UserJSDataverseRepository() const dataverseInfoRepository = new DataverseInfoJSDataverseRepository() export class SignUpFactory { @@ -22,7 +20,6 @@ function SignUpWithSearchParams() { return ( diff --git a/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx b/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx index 84a3b22f7..53a00bf88 100644 --- a/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx +++ b/src/sections/sign-up/valid-token-not-linked-account-form/FormFields.tsx @@ -3,20 +3,20 @@ import { AuthContext } from 'react-oauth2-code-pkce' import { Controller, FormProvider, useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { Alert, Button, Col, Form, Stack } from '@iqss/dataverse-design-system' -import { UserRepository } from '@/users/domain/repositories/UserRepository' import { Validator } from '@/shared/helpers/Validator' import { type ValidTokenNotLinkedAccountFormData } from './types' import { TermsOfUse } from '@/info/domain/models/TermsOfUse' import { SubmissionStatus, useSubmitUser } from './useSubmitUser' +import { useUserRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' import styles from './FormFields.module.scss' interface FormFieldsProps { - userRepository: UserRepository formDefaultValues: ValidTokenNotLinkedAccountFormData termsOfUse: TermsOfUse } -export const FormFields = ({ userRepository, formDefaultValues, termsOfUse }: FormFieldsProps) => { +export const FormFields = ({ formDefaultValues, termsOfUse }: FormFieldsProps) => { + const { userRepository } = useUserRepositories() const { tokenData, logOut } = useContext(AuthContext) const { t } = useTranslation('signUp') const { t: tShared } = useTranslation('shared') diff --git a/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx b/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx index 960047ada..31dc27888 100644 --- a/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx +++ b/src/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.tsx @@ -1,7 +1,6 @@ import { useContext } from 'react' import { AuthContext } from 'react-oauth2-code-pkce' import { Alert } from '@iqss/dataverse-design-system' -import { UserRepository } from '@/users/domain/repositories/UserRepository' import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInfoRepository' import { useGetTermsOfUse } from '@/shared/hooks/useGetTermsOfUse' import { OIDC_STANDARD_CLAIMS, type ValidTokenNotLinkedAccountFormData } from './types' @@ -10,12 +9,10 @@ import { FormFields } from './FormFields' import { FormFieldsSkeleton } from './FormFieldsSkeleton' interface ValidTokenNotLinkedAccountFormProps { - userRepository: UserRepository dataverseInfoRepository: DataverseInfoRepository } export const ValidTokenNotLinkedAccountForm = ({ - userRepository, dataverseInfoRepository }: ValidTokenNotLinkedAccountFormProps) => { const { tokenData } = useContext(AuthContext) @@ -72,11 +69,5 @@ export const ValidTokenNotLinkedAccountForm = ({ return {errorTermsOfUse} } - return ( - - ) + return } diff --git a/src/shared/contexts/external-tools/ExternalToolsProvider.tsx b/src/shared/contexts/external-tools/ExternalToolsProvider.tsx index 88ce8ccda..e3337a120 100644 --- a/src/shared/contexts/external-tools/ExternalToolsProvider.tsx +++ b/src/shared/contexts/external-tools/ExternalToolsProvider.tsx @@ -1,9 +1,9 @@ import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react' import { ExternalTool, ToolScope, ToolType } from '@/externalTools/domain/models/ExternalTool' -import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' import { getExternalTools } from '@/externalTools/domain/useCases/GetExternalTools' import { ReadError } from '@iqss/dataverse-client-javascript' import { JSDataverseReadErrorHandler } from '@/shared/helpers/JSDataverseReadErrorHandler' +import { useExternalToolsRepositories } from '@/shared/contexts/repositories/RepositoriesProvider' type ExternalToolsContextValue = { externalTools: ExternalTool[] @@ -16,20 +16,16 @@ type ExternalToolsContextValue = { filePreviewTools: ExternalTool[] fileQueryTools: ExternalTool[] fileConfigureTools: ExternalTool[] - externalToolsRepository: ExternalToolsRepository } const ExternalToolsContext = createContext(undefined) type ExternalToolsProviderProps = { - externalToolsRepository: ExternalToolsRepository children: React.ReactNode } -export function ExternalToolsProvider({ - externalToolsRepository, - children -}: ExternalToolsProviderProps) { +export function ExternalToolsProvider({ children }: ExternalToolsProviderProps) { + const { externalToolsRepository } = useExternalToolsRepositories() const [externalTools, setExternalTools] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) @@ -111,8 +107,7 @@ export function ExternalToolsProvider({ filePreviewTools, fileQueryTools, fileConfigureTools, - refreshExternalTools: fetchExternalTools, - externalToolsRepository + refreshExternalTools: fetchExternalTools }), [ externalTools, @@ -124,8 +119,7 @@ export function ExternalToolsProvider({ filePreviewTools, fileQueryTools, fileConfigureTools, - fetchExternalTools, - externalToolsRepository + fetchExternalTools ] ) diff --git a/src/shared/contexts/repositories/RepositoriesProvider.tsx b/src/shared/contexts/repositories/RepositoriesProvider.tsx index 0141e274f..6b827e426 100644 --- a/src/shared/contexts/repositories/RepositoriesProvider.tsx +++ b/src/shared/contexts/repositories/RepositoriesProvider.tsx @@ -1,10 +1,18 @@ import React, { createContext, useContext, useMemo } from 'react' import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' +import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' +import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' +import { UserRepository } from '@/users/domain/repositories/UserRepository' export interface RepositoriesContextValue { collectionRepository: CollectionRepository datasetRepository: DatasetRepository + externalToolsRepository: ExternalToolsRepository + fileRepository: FileRepository + guestbookRepository: GuestbookRepository + userRepository: UserRepository } const RepositoriesContext = createContext(undefined) @@ -16,14 +24,29 @@ interface RepositoriesProviderProps extends RepositoriesContextValue { export function RepositoriesProvider({ children, collectionRepository, - datasetRepository + datasetRepository, + externalToolsRepository, + fileRepository, + guestbookRepository, + userRepository }: RepositoriesProviderProps) { const value = useMemo( () => ({ collectionRepository, - datasetRepository + datasetRepository, + externalToolsRepository, + fileRepository, + guestbookRepository, + userRepository }), - [collectionRepository, datasetRepository] + [ + collectionRepository, + datasetRepository, + externalToolsRepository, + fileRepository, + guestbookRepository, + userRepository + ] ) return {children} @@ -46,7 +69,31 @@ export function useCollectionRepositories() { } export function useDatasetRepositories() { - const { datasetRepository } = useRepositories() + const { datasetRepository, fileRepository } = useRepositories() + + return { datasetRepository, fileRepository } +} + +export function useExternalToolsRepositories() { + const { externalToolsRepository } = useRepositories() + + return { externalToolsRepository } +} + +export function useFileRepositories() { + const { fileRepository } = useRepositories() + + return { fileRepository } +} + +export function useUserRepositories() { + const { userRepository } = useRepositories() + + return { userRepository } +} + +export function useGuestbookRepositories() { + const { guestbookRepository } = useRepositories() - return { datasetRepository } + return { guestbookRepository } } diff --git a/src/stories/WithRepositories.tsx b/src/stories/WithRepositories.tsx index a12ba61fa..bb08ac7a5 100644 --- a/src/stories/WithRepositories.tsx +++ b/src/stories/WithRepositories.tsx @@ -2,6 +2,10 @@ import { StoryFn } from '@storybook/react' import { ReactNode } from 'react' import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' +import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' +import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' +import { UserRepository } from '@/users/domain/repositories/UserRepository' import { RepositoriesProvider } from '@/shared/contexts/repositories/RepositoriesProvider' function failFastRepository(name: string): T { @@ -21,17 +25,29 @@ function failFastRepository(name: string): T { interface WithRepositoriesProps { collectionRepository?: CollectionRepository datasetRepository?: DatasetRepository + externalToolsRepository?: ExternalToolsRepository + fileRepository?: FileRepository + guestbookRepository?: GuestbookRepository + userRepository?: UserRepository } export function WithRepositories({ collectionRepository = failFastRepository('CollectionRepository'), - datasetRepository = failFastRepository('DatasetRepository') + datasetRepository = failFastRepository('DatasetRepository'), + externalToolsRepository = failFastRepository('ExternalToolsRepository'), + fileRepository = failFastRepository('FileRepository'), + guestbookRepository = failFastRepository('GuestbookRepository'), + userRepository = failFastRepository('UserRepository') }: WithRepositoriesProps) { function WithRepositoriesDecorator(Story: StoryFn) { return ( + datasetRepository={datasetRepository} + externalToolsRepository={externalToolsRepository} + fileRepository={fileRepository} + guestbookRepository={guestbookRepository} + userRepository={userRepository}> ) @@ -49,12 +65,20 @@ interface RepositoriesStoryProviderProps extends WithRepositoriesProps { export function RepositoriesStoryProvider({ children, collectionRepository = failFastRepository('CollectionRepository'), - datasetRepository = failFastRepository('DatasetRepository') + datasetRepository = failFastRepository('DatasetRepository'), + externalToolsRepository = failFastRepository('ExternalToolsRepository'), + fileRepository = failFastRepository('FileRepository'), + guestbookRepository = failFastRepository('GuestbookRepository'), + userRepository = failFastRepository('UserRepository') }: RepositoriesStoryProviderProps) { return ( + datasetRepository={datasetRepository} + externalToolsRepository={externalToolsRepository} + fileRepository={fileRepository} + guestbookRepository={guestbookRepository} + userRepository={userRepository}> {children} ) diff --git a/src/stories/account/Account.stories.tsx b/src/stories/account/Account.stories.tsx index 566a0c416..0cfb3c4a0 100644 --- a/src/stories/account/Account.stories.tsx +++ b/src/stories/account/Account.stories.tsx @@ -25,10 +25,11 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + diff --git a/src/stories/account/account-info-section/AccountInfoSection.stories.tsx b/src/stories/account/account-info-section/AccountInfoSection.stories.tsx index 3e3499c2f..79d071ce4 100644 --- a/src/stories/account/account-info-section/AccountInfoSection.stories.tsx +++ b/src/stories/account/account-info-section/AccountInfoSection.stories.tsx @@ -25,10 +25,11 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + diff --git a/src/stories/account/api-token-section/ApiTokenSection.stories.tsx b/src/stories/account/api-token-section/ApiTokenSection.stories.tsx index 755710008..764e470d8 100644 --- a/src/stories/account/api-token-section/ApiTokenSection.stories.tsx +++ b/src/stories/account/api-token-section/ApiTokenSection.stories.tsx @@ -27,10 +27,11 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + @@ -40,10 +41,11 @@ export const Default: Story = { export const Loading: Story = { render: () => ( - + @@ -53,10 +55,11 @@ export const Loading: Story = { export const Error: Story = { render: () => ( - + @@ -79,10 +82,11 @@ export const NoToken: Story = { } return ( - + diff --git a/src/stories/account/notification-section/NotificationSection.stories.tsx b/src/stories/account/notification-section/NotificationSection.stories.tsx index b0810be73..eeac2f82f 100644 --- a/src/stories/account/notification-section/NotificationSection.stories.tsx +++ b/src/stories/account/notification-section/NotificationSection.stories.tsx @@ -27,10 +27,11 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + @@ -39,10 +40,11 @@ export const Default: Story = { } export const Error: Story = { render: () => ( - + diff --git a/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx b/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx index b39a6c1ab..fb0def6ee 100644 --- a/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx +++ b/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx @@ -2,7 +2,27 @@ import { Meta, StoryObj } from '@storybook/react' import { WithI18next } from '../../WithI18next' import { CollectionCard } from '@/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard' import { CollectionItemTypePreviewMother } from '../../../../tests/component/collection/domain/models/CollectionItemTypePreviewMother' -import { FakerHelper } from '../../../../tests/component/shared/FakerHelper' + +const collectionPreview = CollectionItemTypePreviewMother.createRealistic() +const longDescriptionCollectionPreview = CollectionItemTypePreviewMother.create({ + ...collectionPreview, + description: 'Scientific research collection with a detailed public description. ' + .repeat(20) + .trim() +}) +const unpublishedCollectionPreview = CollectionItemTypePreviewMother.create({ + ...collectionPreview, + isReleased: false +}) +const thumbnailCollectionPreview = CollectionItemTypePreviewMother.create({ + ...collectionPreview, + thumbnail: + 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2264%22 height=%2248%22 viewBox=%220 0 64 48%22%3E%3Crect width=%2264%22 height=%2248%22 fill=%22%23f1f5f9%22/%3E%3Cpath d=%22M10 34l12-12 9 9 8-8 15 15H10z%22 fill=%22%235b728a%22/%3E%3Ccircle cx=%2246%22 cy=%2214%22 r=%226%22 fill=%22%23f9c74f%22/%3E%3C/svg%3E' +}) +const linkedCollectionPreview = CollectionItemTypePreviewMother.create({ + ...collectionPreview, + isLinked: true +}) const meta: Meta = { title: 'Sections/Collection Page/CollectionCard', @@ -15,30 +35,24 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + ) } export const WithLongDescription: Story = { - render: () => { - const collectionPreview = CollectionItemTypePreviewMother.create({ - name: 'Scientific Research Collection', - description: FakerHelper.paragraph(20) - }) - return ( - - ) - } + render: () => ( + + ) } export const Unpublished: Story = { render: () => ( ) } @@ -47,7 +61,7 @@ export const WithThumbnail: Story = { render: () => ( ) } @@ -56,7 +70,7 @@ export const Linked: Story = { render: () => ( ) } diff --git a/src/stories/dataset/Dataset.stories.tsx b/src/stories/dataset/Dataset.stories.tsx index d121e9c12..75aa80eaa 100644 --- a/src/stories/dataset/Dataset.stories.tsx +++ b/src/stories/dataset/Dataset.stories.tsx @@ -23,7 +23,6 @@ import { CollectionMockRepository } from '@/stories/collection/CollectionMockRep import { ContactMockRepository } from '../shared-mock-repositories/contact/ContactMockRepository' import { DataverseInfoMockRepository } from '../shared-mock-repositories/info/DataverseInfoMockRepository' import { GuestbookMockRepository } from '../shared-mock-repositories/guestbook/GuestbookMockRepository' -import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { DatasetProvider } from '@/sections/dataset/DatasetProvider' import { RepositoriesStoryProvider, WithRepositories } from '@/stories/WithRepositories' @@ -55,13 +54,11 @@ const WithDatasetGuestbook = (Story: () => JSX.Element) => { const datasetRepository = new DatasetWithGuestbookMockRepository() return ( - - - - - + + + ) } @@ -72,9 +69,10 @@ export const Default: Story = { render: () => ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()} + guestbookRepository={guestbookRepository}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()}> ( + datasetRepository={new DatasetWithReviewsMockRepository()} + fileRepository={new FileMockRepository()}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockNoDataRepository()}> ( + datasetRepository={new DatasetMockRepository()} + fileRepository={new FileMockRepository()}> = { title: 'Sections/Dataset Page/DatasetActionButtons/AccessDatasetMenu', @@ -65,18 +66,20 @@ export const WithTabularFiles: Story = { export const WithExploreOptionsTools: Story = { render: () => ( - - - + + + + + ) } diff --git a/src/stories/dataset/dataset-files/DatasetFiles.stories.tsx b/src/stories/dataset/dataset-files/DatasetFiles.stories.tsx index be915cd6e..0b083b805 100644 --- a/src/stories/dataset/dataset-files/DatasetFiles.stories.tsx +++ b/src/stories/dataset/dataset-files/DatasetFiles.stories.tsx @@ -8,16 +8,12 @@ import { WithSettings } from '../../WithSettings' import { FileMockNoFiltersRepository } from '../../file/FileMockNoFiltersRepository' import { DatasetMother } from '../../../../tests/component/dataset/domain/models/DatasetMother' import { DatasetMockRepository } from '../../dataset/DatasetMockRepository' -import { WithRepositories } from '../../WithRepositories' +import { RepositoriesStoryProvider } from '../../WithRepositories' const meta: Meta = { title: 'Sections/Dataset Page/DatasetFiles', component: DatasetFiles, - decorators: [ - WithI18next, - WithSettings, - WithRepositories({ datasetRepository: new DatasetMockRepository() }) - ] + decorators: [WithI18next, WithSettings] } export default meta @@ -27,40 +23,52 @@ const testDataset = DatasetMother.createRealistic() export const Default: Story = { render: () => ( - + + + ) } export const Loading: Story = { render: () => ( - + + + ) } export const NoFiles: Story = { render: () => ( - + + + ) } export const NoFilters: Story = { render: () => ( - + + + ) } diff --git a/src/stories/dataset/dataset-files/DatasetFilesScrollable.stories.tsx b/src/stories/dataset/dataset-files/DatasetFilesScrollable.stories.tsx index 564277756..cad78c7f8 100644 --- a/src/stories/dataset/dataset-files/DatasetFilesScrollable.stories.tsx +++ b/src/stories/dataset/dataset-files/DatasetFilesScrollable.stories.tsx @@ -8,16 +8,12 @@ import { WithSettings } from '../../WithSettings' import { FileMockNoFiltersRepository } from '../../file/FileMockNoFiltersRepository' import { DatasetMother } from '../../../../tests/component/dataset/domain/models/DatasetMother' import { DatasetMockRepository } from '../../dataset/DatasetMockRepository' -import { WithRepositories } from '../../WithRepositories' +import { RepositoriesStoryProvider } from '../../WithRepositories' const meta: Meta = { title: 'Sections/Dataset Page/DatasetFilesScrollable', component: DatasetFilesScrollable, - decorators: [ - WithI18next, - WithSettings, - WithRepositories({ datasetRepository: new DatasetMockRepository() }) - ], + decorators: [WithI18next, WithSettings], parameters: { // Sets the delay for all stories. chromatic: { delay: 15000, pauseAnimationAtEnd: true } @@ -31,40 +27,52 @@ const testDataset = DatasetMother.createRealistic() export const Default: Story = { render: () => ( - + + + ) } export const Loading: Story = { render: () => ( - + + + ) } export const NoFiles: Story = { render: () => ( - + + + ) } export const NoFilters: Story = { render: () => ( - + + + ) } diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx index 27d8ad7e6..054a7783f 100644 --- a/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx +++ b/src/stories/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.stories.tsx @@ -17,7 +17,10 @@ const meta: Meta = { WithSettings, WithLoggedInUser, WithDatasetAllPermissionsGranted, - WithRepositories({ datasetRepository: new DatasetMockRepository() }) + WithRepositories({ + datasetRepository: new DatasetMockRepository(), + fileRepository: new FileMockRepository() + }) ] } @@ -25,11 +28,5 @@ export default meta type Story = StoryObj export const Default: Story = { - render: () => ( - - ) + render: () => } diff --git a/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/file-options-menu/FileOptionsMenu.stories.tsx b/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/file-options-menu/FileOptionsMenu.stories.tsx index 723ccef8b..7fcd85395 100644 --- a/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/file-options-menu/FileOptionsMenu.stories.tsx +++ b/src/stories/dataset/dataset-files/files-table/file-actions/file-action-buttons/file-options-menu/FileOptionsMenu.stories.tsx @@ -12,7 +12,7 @@ import { ExternalToolsProvider } from '@/shared/contexts/external-tools/External import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' import { FakerHelper } from '@tests/component/shared/FakerHelper' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' -import { WithRepositories } from '@/stories/WithRepositories' +import { RepositoriesStoryProvider, WithRepositories } from '@/stories/WithRepositories' const meta: Meta = { title: @@ -22,7 +22,10 @@ const meta: Meta = { WithI18next, WithSettings, WithLoggedInUser, - WithRepositories({ datasetRepository: new DatasetMockRepository() }) + WithRepositories({ + datasetRepository: new DatasetMockRepository(), + fileRepository: new FileMockRepository() + }) ] } @@ -31,42 +34,22 @@ type Story = StoryObj export const DefaultWithLoggedInUser: Story = { decorators: [WithDatasetAllPermissionsGranted], - render: () => ( - - ) + render: () => } export const Restricted: Story = { decorators: [WithDatasetAllPermissionsGranted], - render: () => ( - - ) + render: () => } export const WithDatasetLocked: Story = { decorators: [WithDatasetLockedFromEdits], - render: () => ( - - ) + render: () => } export const WithFileAlreadyDeleted: Story = { decorators: [WithDatasetAllPermissionsGranted], - render: () => ( - - ) + render: () => } const externalToolsRepositoryWithFileConfigureTool = new ExternalToolsMockRepository() @@ -81,12 +64,12 @@ externalToolsRepositoryWithFileConfigureTool.getExternalTools = () => { export const WithConfigureTool: Story = { decorators: [WithDatasetAllPermissionsGranted], render: () => ( - - - + + + + + ) } diff --git a/src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx b/src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx index 560252471..be0f67f8a 100644 --- a/src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx +++ b/src/stories/dataset/dataset-guestbook/DatasetGuestbook.stories.tsx @@ -3,11 +3,11 @@ import { DatasetGuestbook } from '@/sections/dataset/dataset-guestbook/DatasetGu import { WithI18next } from '@/stories/WithI18next' import { DatasetContext } from '@/sections/dataset/DatasetContext' import { DatasetMother } from '@tests/component/dataset/domain/models/DatasetMother' -import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' import { GuestbookMockRepository, storybookGuestbook } from '@/stories/shared-mock-repositories/guestbook/GuestbookMockRepository' +import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Sections/Dataset Page/DatasetTerms/DatasetGuestbook', @@ -22,7 +22,7 @@ const guestbookRepository = new GuestbookMockRepository() const withDatasetContext = (datasetWithGuestbook: boolean) => { const DatasetGuestbookStoryDecorator = (StoryComponent: () => JSX.Element) => ( - + { }}> - + ) DatasetGuestbookStoryDecorator.displayName = `DatasetGuestbookStoryDecorator-${String( diff --git a/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx b/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx index b1cced0a2..582ad02bf 100644 --- a/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx +++ b/src/stories/dataset/dataset-metadata/DatasetMetadata.stories.tsx @@ -5,6 +5,8 @@ import { WithAnonymizedView } from '../WithAnonymizedView' import { DatasetMother } from '../../../../tests/component/dataset/domain/models/DatasetMother' import { MetadataBlockInfoMockRepository } from '../../shared-mock-repositories/metadata-block-info/MetadataBlockInfoMockRepository' import { DataverseInfoMockRepository } from '@/stories/shared-mock-repositories/info/DataverseInfoMockRepository' +import { DatasetMockRepository } from '@/stories/dataset/DatasetMockRepository' +import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Sections/Dataset Page/DatasetMetadata', @@ -20,23 +22,27 @@ const datasetMockAnonymized = DatasetMother.createRealisticAnonymized() export const Default: Story = { render: () => ( - + + + ) } export const AnonymizedView: Story = { decorators: [WithAnonymizedView], render: () => ( - + + + ) } diff --git a/src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx b/src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx index 8cfa7409f..857ffe4cc 100644 --- a/src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx +++ b/src/stories/dataset/dataset-terms/DatasetTerms.stories.tsx @@ -13,7 +13,8 @@ import { storybookGuestbook } from '@/stories/shared-mock-repositories/guestbook/GuestbookMockRepository' import { DatasetContext } from '@/sections/dataset/DatasetContext' -import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' +import { RepositoriesStoryProvider } from '@/stories/WithRepositories' +import { FileRepository } from '@/files/domain/repositories/FileRepository' const meta: Meta = { title: 'Sections/Dataset Page/DatasetTerms', @@ -35,29 +36,39 @@ const guestbookRepository = new GuestbookMockRepository() const withDatasetContext = (dataset = testDatasetWithGuestbook) => { const DatasetTermsStoryDecorator = (Story: () => JSX.Element) => ( - - {} - }}> - - - + {} + }}> + + ) DatasetTermsStoryDecorator.displayName = 'DatasetTermsStoryDecorator' return DatasetTermsStoryDecorator } +const withFileRepository = (fileRepository: FileRepository) => { + const DatasetTermsFileRepositoryStoryDecorator = (Story: () => JSX.Element) => ( + + + + ) + + DatasetTermsFileRepositoryStoryDecorator.displayName = 'DatasetTermsFileRepositoryStoryDecorator' + return DatasetTermsFileRepositoryStoryDecorator +} + export const Default: Story = { - decorators: [withDatasetContext()], + decorators: [withDatasetContext(), withFileRepository(new FileMockRepository())], render: () => ( @@ -65,11 +76,11 @@ export const Default: Story = { } export const Loading: Story = { + decorators: [withFileRepository(new FileMockLoadingRepository())], render: () => ( @@ -77,12 +88,11 @@ export const Loading: Story = { } export const RestrictedFiles: Story = { - decorators: [withDatasetContext()], + decorators: [withDatasetContext(), withFileRepository(new FileMockRestrictedFilesRepository())], render: () => ( @@ -90,24 +100,22 @@ export const RestrictedFiles: Story = { } export const NoRestrictedFiles: Story = { - decorators: [withDatasetContext()], + decorators: [withDatasetContext(), withFileRepository(new FileMockNoRestrictedFilesRepository())], render: () => ( ) } export const CustomTerms: Story = { - decorators: [withDatasetContext()], + decorators: [withDatasetContext(), withFileRepository(new FileMockNoRestrictedFilesRepository())], render: () => ( @@ -115,12 +123,14 @@ export const CustomTerms: Story = { } export const WithoutAssignedGuestbook: Story = { - decorators: [withDatasetContext(DatasetMother.createRealistic({ guestbookId: undefined }))], + decorators: [ + withDatasetContext(DatasetMother.createRealistic({ guestbookId: undefined })), + withFileRepository(new FileMockNoRestrictedFilesRepository()) + ], render: () => ( @@ -128,12 +138,14 @@ export const WithoutAssignedGuestbook: Story = { } export const GuestbookEmptyState: Story = { - decorators: [withDatasetContext(DatasetMother.createRealistic({ guestbookId: undefined }))], + decorators: [ + withDatasetContext(DatasetMother.createRealistic({ guestbookId: undefined })), + withFileRepository(new FileMockNoRestrictedFilesRepository()) + ], render: () => ( diff --git a/src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx b/src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx index 04ae10a96..4382b0468 100644 --- a/src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx +++ b/src/stories/edit-dataset-terms/EditDatasetTerms.stories.tsx @@ -20,7 +20,12 @@ const guestbookRepository: GuestbookRepository = const meta: Meta = { title: 'Pages/EditDatasetTerms', component: EditDatasetTerms, - decorators: [WithI18next, WithLayout, WithDataset, WithRepositories({ datasetRepository })], + decorators: [ + WithI18next, + WithLayout, + WithDataset, + WithRepositories({ datasetRepository, guestbookRepository }) + ], parameters: { chromatic: { delay: 15000, pauseAnimationAtEnd: true } } @@ -34,7 +39,6 @@ export const EditLicenseAndTermsTab: Story = { ) } @@ -44,7 +48,6 @@ export const EditTermsOfAccessTab: Story = { ) } @@ -54,7 +57,6 @@ export const EditGuestbookTab: Story = { ) } diff --git a/src/stories/file/File.stories.tsx b/src/stories/file/File.stories.tsx index 1d3b0eda0..0ef7514cf 100644 --- a/src/stories/file/File.stories.tsx +++ b/src/stories/file/File.stories.tsx @@ -13,7 +13,7 @@ import { FakerHelper } from '@tests/component/shared/FakerHelper' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { DataverseInfoMockRepository } from '../shared-mock-repositories/info/DataverseInfoMockRepository' import { ContactMockRepository } from '../shared-mock-repositories/contact/ContactMockRepository' -import { WithRepositories } from '../WithRepositories' +import { RepositoriesStoryProvider, WithRepositories } from '../WithRepositories' const meta: Meta = { title: 'Pages/File', @@ -89,15 +89,17 @@ export const FileNotFound: Story = { export const WithMultipleExternalTools: Story = { render: () => ( - - - + + + + + ) } @@ -112,15 +114,17 @@ externalToolsRepositoryOnlyPreviewTool.getExternalTools = () => { export const WithOnlyOnePreviewExternalTool: Story = { render: () => ( - - - + + + + + ) } @@ -135,14 +139,16 @@ externalToolsRepositoryOnlyQueryTool.getExternalTools = () => { export const WithOnlyOneQueryExternalTool: Story = { render: () => ( - - - + + + + + ) } diff --git a/src/stories/file/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx b/src/stories/file/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx index f55904ea0..da98c451d 100644 --- a/src/stories/file/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx +++ b/src/stories/file/file-action-buttons/access-file-menu/AccessFileMenu.stories.tsx @@ -6,6 +6,7 @@ import { FileAccessMother } from '../../../../../tests/component/files/domain/mo import { FileMetadataMother } from '../../../../../tests/component/files/domain/models/FileMetadataMother' import { ExternalToolsProvider } from '@/shared/contexts/external-tools/ExternalToolsProvider' import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' +import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Sections/File Page/Action Buttons/AccessFileMenu', @@ -162,15 +163,17 @@ export const WithEmbargoAndRestrictedWithAccessGranted: Story = { export const WithExploreAndQueryOptionsTools: Story = { render: () => ( - - - + + + + + ) } diff --git a/src/stories/file/file-action-buttons/edit-file-dropdown/EditFileDropdown.stories.tsx b/src/stories/file/file-action-buttons/edit-file-dropdown/EditFileDropdown.stories.tsx index cd19ea437..f80878c5d 100644 --- a/src/stories/file/file-action-buttons/edit-file-dropdown/EditFileDropdown.stories.tsx +++ b/src/stories/file/file-action-buttons/edit-file-dropdown/EditFileDropdown.stories.tsx @@ -9,7 +9,7 @@ import { ExternalToolsProvider } from '@/shared/contexts/external-tools/External import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { FakerHelper } from '@tests/component/shared/FakerHelper' -import { WithRepositories } from '@/stories/WithRepositories' +import { RepositoriesStoryProvider, WithRepositories } from '@/stories/WithRepositories' const storyFile = FileMother.createRealistic() @@ -56,21 +56,24 @@ externalToolsRepositoryWithFileConfigureTool.getExternalTools = () => { export const WithConfigureToolOption: Story = { render: () => ( - - - + + + + + ) } diff --git a/src/stories/file/file-embedded-external-tool/FileEmbeddedExternalTool.stories.tsx b/src/stories/file/file-embedded-external-tool/FileEmbeddedExternalTool.stories.tsx index dcc339520..47bda11fd 100644 --- a/src/stories/file/file-embedded-external-tool/FileEmbeddedExternalTool.stories.tsx +++ b/src/stories/file/file-embedded-external-tool/FileEmbeddedExternalTool.stories.tsx @@ -4,6 +4,7 @@ import { FileEmbeddedExternalTool } from '@/sections/file/file-embedded-external import { FileMother } from '@tests/component/files/domain/models/FileMother' import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' +import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Sections/File Page/File External Tools Tab', @@ -19,27 +20,29 @@ const externalToolsRepository = new ExternalToolsMockRepository() export const WithOneToolOnly: Story = { render: () => ( - + + + ) } export const WithMoreThanOneTool: Story = { render: () => ( - + + + ) } diff --git a/src/stories/file/file-metadata/FileMetadata.stories.tsx b/src/stories/file/file-metadata/FileMetadata.stories.tsx index 24d19ffcd..533118f88 100644 --- a/src/stories/file/file-metadata/FileMetadata.stories.tsx +++ b/src/stories/file/file-metadata/FileMetadata.stories.tsx @@ -5,9 +5,8 @@ import { FileMetadataMother } from '../../../../tests/component/files/domain/mod import { FilePermissionsMother } from '../../../../tests/component/files/domain/models/FilePermissionsMother' import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' import { DataverseInfoMockRepository } from '@/stories/shared-mock-repositories/info/DataverseInfoMockRepository' -import { RepositoriesProvider } from '@/shared/contexts/repositories/RepositoriesProvider' import { DatasetMockRepository } from '@/stories/dataset/DatasetMockRepository' -import { CollectionMockRepository } from '@/stories/collection/CollectionMockRepository' +import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Sections/File Page/FileMetadata', @@ -20,9 +19,7 @@ type Story = StoryObj export const Default: Story = { render: () => ( - + - + ) } diff --git a/src/stories/guestbooks/guestbook-applied-modal/DownloadWithTermsAndGuestbookModal.stories.tsx b/src/stories/guestbooks/guestbook-applied-modal/DownloadWithTermsAndGuestbookModal.stories.tsx index 1d1dfb810..c7bbf54cb 100644 --- a/src/stories/guestbooks/guestbook-applied-modal/DownloadWithTermsAndGuestbookModal.stories.tsx +++ b/src/stories/guestbooks/guestbook-applied-modal/DownloadWithTermsAndGuestbookModal.stories.tsx @@ -10,7 +10,7 @@ import { GuestbookResponseDTO } from '@/access/domain/repositories/AccessRepository' import { AccessRepositoryProvider } from '@/sections/access/AccessRepositoryProvider' -import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' +import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const accessRepository: AccessRepository = { submitGuestbookForDatasetDownload: ( @@ -32,7 +32,7 @@ const meta: Meta = { WithI18next, WithLoggedInUser, (Story) => ( - + = { - + ) ] } diff --git a/src/stories/sign-up/SignUp.stories.tsx b/src/stories/sign-up/SignUp.stories.tsx index b81684631..fd2253efc 100644 --- a/src/stories/sign-up/SignUp.stories.tsx +++ b/src/stories/sign-up/SignUp.stories.tsx @@ -7,6 +7,7 @@ import { UserMockRepository } from '../shared-mock-repositories/user/UserMockRep import { DataverseInfoMockRepository } from '../shared-mock-repositories/info/DataverseInfoMockRepository' import { DataverseInfoMockLoadingRepository } from '../shared-mock-repositories/info/DataverseInfoMockLoadingkRepository' import { DataverseInfoMockErrorRepository } from '../shared-mock-repositories/info/DataverseInfoMockErrorRepository' +import { RepositoriesStoryProvider } from '@/stories/WithRepositories' const meta: Meta = { title: 'Pages/Sign Up', @@ -22,30 +23,33 @@ type Story = StoryObj export const ValidTokenWithNotLinkedAccount: Story = { render: () => ( - + + + ) } export const LoadingTermsOfUse: Story = { render: () => ( - + + + ) } export const FailedToFetchTermsOfUse: Story = { render: () => ( - + + + ) } diff --git a/tests/component/WithRepositories.tsx b/tests/component/WithRepositories.tsx index ceeb129c5..a8fa94e62 100644 --- a/tests/component/WithRepositories.tsx +++ b/tests/component/WithRepositories.tsx @@ -1,6 +1,10 @@ import { ReactNode } from 'react' import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' +import { ExternalToolsRepository } from '@/externalTools/domain/repositories/ExternalToolsRepository' +import { FileRepository } from '@/files/domain/repositories/FileRepository' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' +import { UserRepository } from '@/users/domain/repositories/UserRepository' import { RepositoriesProvider } from '@/shared/contexts/repositories/RepositoriesProvider' function failFastRepository(name: string): T { @@ -21,17 +25,29 @@ interface WithRepositoriesProps { children: ReactNode collectionRepository?: CollectionRepository datasetRepository?: DatasetRepository + externalToolsRepository?: ExternalToolsRepository + fileRepository?: FileRepository + guestbookRepository?: GuestbookRepository + userRepository?: UserRepository } export function WithRepositories({ children, collectionRepository = failFastRepository('CollectionRepository'), - datasetRepository = failFastRepository('DatasetRepository') + datasetRepository = failFastRepository('DatasetRepository'), + externalToolsRepository = failFastRepository('ExternalToolsRepository'), + fileRepository = failFastRepository('FileRepository'), + guestbookRepository = failFastRepository('GuestbookRepository'), + userRepository = failFastRepository('UserRepository') }: WithRepositoriesProps) { return ( + datasetRepository={datasetRepository} + externalToolsRepository={externalToolsRepository} + fileRepository={fileRepository} + guestbookRepository={guestbookRepository} + userRepository={userRepository}> {children} ) diff --git a/tests/component/sections/account/Account.spec.tsx b/tests/component/sections/account/Account.spec.tsx index 3661ca81a..03b104c36 100644 --- a/tests/component/sections/account/Account.spec.tsx +++ b/tests/component/sections/account/Account.spec.tsx @@ -1,6 +1,5 @@ import { Account } from '../../../../src/sections/account/Account' import { AccountHelper } from '../../../../src/sections/account/AccountHelper' -import { UserJSDataverseRepository } from '../../../../src/users/infrastructure/repositories/UserJSDataverseRepository' import { CollectionMockRepository } from '@/stories/collection/CollectionMockRepository' import { RoleMockRepository } from '@/stories/account/RoleMockRepository' import { NotificationType } from '@/notifications/domain/models/Notification' @@ -55,7 +54,6 @@ describe('Account', () => { @@ -74,7 +72,6 @@ describe('Account', () => { diff --git a/tests/component/sections/account/ApiTokenSection.spec.tsx b/tests/component/sections/account/ApiTokenSection.spec.tsx index e6c9418d0..c82923355 100644 --- a/tests/component/sections/account/ApiTokenSection.spec.tsx +++ b/tests/component/sections/account/ApiTokenSection.spec.tsx @@ -1,6 +1,7 @@ import { ApiTokenSection } from '../../../../src/sections/account/api-token-section/ApiTokenSection' import { DateHelper } from '@/shared/helpers/DateHelper' import { UserRepository } from '@/users/domain/repositories/UserRepository' +import { WithRepositories } from '@tests/component/WithRepositories' describe('ApiTokenSection', () => { const mockApiTokenInfo = { @@ -14,6 +15,12 @@ describe('ApiTokenSection', () => { let userRepository: UserRepository + const ApiTokenSectionWithRepositories = () => ( + + + + ) + beforeEach(() => { userRepository = { getCurrentApiToken: cy.stub().resolves(mockApiTokenInfo), @@ -23,7 +30,7 @@ describe('ApiTokenSection', () => { register: cy.stub().resolves() } - cy.mountAuthenticated() + cy.mountAuthenticated() }) it('should show the loading skeleton while fetching the token', () => { @@ -31,7 +38,7 @@ describe('ApiTokenSection', () => { return Cypress.Promise.delay(500).then(() => mockApiTokenInfo) }) - cy.mount() + cy.mount() cy.get('[data-testid="loadingSkeleton"]').should('exist') cy.wait(500) @@ -81,7 +88,7 @@ describe('ApiTokenSection', () => { it('should show error message when failing to fetch the current API token', () => { userRepository.getCurrentApiToken = cy.stub().rejects(new Error('Failed to fetch API token')) - cy.mountAuthenticated() + cy.mountAuthenticated() cy.findByText(/Failed to fetch API token/).should('exist') }) diff --git a/tests/component/sections/dataset/Dataset.spec.tsx b/tests/component/sections/dataset/Dataset.spec.tsx index 152671a2a..9e2c5b6ec 100644 --- a/tests/component/sections/dataset/Dataset.spec.tsx +++ b/tests/component/sections/dataset/Dataset.spec.tsx @@ -1,5 +1,6 @@ import { DatasetRepository } from '../../../../src/dataset/domain/repositories/DatasetRepository' import { Dataset } from '../../../../src/sections/dataset/Dataset' +import { TabsSkeleton } from '../../../../src/sections/dataset/DatasetSkeleton' import { DatasetMother, DatasetPermissionsMother } from '../../dataset/domain/models/DatasetMother' import { LoadingProvider } from '../../../../src/shared/contexts/loading/LoadingProvider' import { @@ -29,6 +30,8 @@ import { DataverseInfoRepository } from '@/info/domain/repositories/DataverseInf import { DatasetMetadataExportFormatsMother } from '@tests/component/info/domain/models/DatasetMetadataExportFormatsMother' import { WithRepositories } from '@tests/component/WithRepositories' import { DatasetReview } from '@/dataset/domain/models/DatasetReview' +import { useLocation } from 'react-router-dom' +import { TermsOfUseMother } from '@tests/component/dataset/domain/models/TermsOfUseMother' const setAnonymizedView = () => {} const fileRepository: FileRepository = {} as FileRepository @@ -153,6 +156,11 @@ const versionSummaryInfo: DatasetVersionSummaryInfo[] = [ const testDatasetMetadataExportFormats = DatasetMetadataExportFormatsMother.create() const termsTabLabelRegex = /^Terms(?: and Guestbook)?$/ +function LocationDisplay() { + const location = useLocation() + return
{`${location.pathname}${location.search}`}
+} + describe('Dataset', () => { const mountWithDataset = ( component: ReactNode, @@ -314,7 +322,8 @@ describe('Dataset', () => { + datasetRepository={datasetRepository} + fileRepository={fileRepository}> @@ -330,7 +339,6 @@ describe('Dataset', () => { it('renders skeleton while loading', () => { mountWithDataset( { mountWithDataset( { mountWithDataset( { cy.findAllByRole('tab', { name: 'Versions' }).should('have.length', 1) }) + it('redirects to the dataset page when publish completes', () => { + const publishedDataset = DatasetMother.create({ + persistentId: 'doi:10.5072/FK2/PUBLISHDONE' + }) + cy.clock() + + mountWithDataset( + <> + + + , + publishedDataset + ) + + cy.findByText('Publish in Progress').should('exist') + cy.tick(2_000) + cy.findByTestId('location-display').should( + 'have.text', + '/datasets?persistentId=doi:10.5072/FK2/PUBLISHDONE' + ) + }) + it('renders the breadcrumbs', () => { mountWithDataset( { it('renders the Dataset page title and labels', () => { mountWithDataset( { it('renders the Dataset Metadata tab', () => { mountWithDataset( { it('renders the Dataset Terms tab', () => { mountWithDataset( { cy.findByTestId('dataset-guestbook-section').should('exist') }) + it('opens the terms tab from the custom terms summary link', () => { + const customTermsDataset = DatasetMother.create({ + license: undefined, + termsOfUse: TermsOfUseMother.create({ + customTerms: { + termsOfUse: 'Custom terms summary text', + confidentialityDeclaration: '', + specialPermissions: '', + restrictions: '', + citationRequirements: '', + depositorRequirements: '', + conditions: '', + disclaimer: '' + } + }) + }) + + mountWithDataset( + <> + + + , + customTermsDataset + ) + + cy.findByRole('button', { name: 'Custom Dataset Terms' }).click() + cy.findByTestId('location-display').should('contain', '?tab=terms') + }) + it('renders the read-only terms tab title when user cannot edit dataset', () => { const readOnlyDataset = DatasetMother.create({ permissions: DatasetPermissionsMother.createWithUpdateDatasetNotAllowed() @@ -461,7 +525,6 @@ describe('Dataset', () => { mountWithDataset( { it('renders the Dataset Files tab', () => { mountWithDataset( { mountWithDataset( { mountWithDataset( { ) }) + it('renders the dataset tabs skeleton', () => { + cy.customMount() + + cy.findByRole('tab', { name: 'Files' }).should('exist') + cy.findByRole('tab', { name: 'Metadata' }).should('exist') + cy.findByRole('tab', { name: 'Terms' }).should('exist') + cy.findByRole('tab', { name: 'Versions' }).should('exist') + }) + it('should render all tabs if the dataset is in deaccessioned version, and user has edit permission', () => { const testDataset = DatasetMother.createDeaccessionedwithEditPermission() mountWithDataset( { mountWithDataset( { it('renders the Dataset Action Buttons', () => { mountWithDataset( { it('renders the Dataset Files list table with infinite scrolling enabled', () => { mountWithDataset( { it('shows the toast when the information was sent to contact successfully', () => { mountWithDataset( { it('does not show the tooltip for contact owner button', () => { mountWithDataset( { mountWithDataset( { it('renders the dataset configure tools options if they are available', () => { cy.customMount( - - - + + + + + ) cy.findByText('Explore Options').should('not.exist') @@ -34,18 +37,22 @@ describe('DatasetToolOptions', () => { it('renders nothing if there are no dataset configure tools', () => { testExternalToolsRepository.getExternalTools = cy.stub().resolves([]) cy.customMount( - - - + + + + + ) cy.findByText('Configure Options').should('not.exist') }) it('renders the dataset explore tools options if they are available', () => { cy.customMount( - - - + + + + + ) cy.findByText('Configure Options').should('not.exist') @@ -56,9 +63,11 @@ describe('DatasetToolOptions', () => { it('renders nothing if there are no dataset explore tools', () => { testExternalToolsRepository.getExternalTools = cy.stub().resolves([]) cy.customMount( - - - + + + + + ) cy.findByText('Explore Options').should('not.exist') }) @@ -84,9 +93,11 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() @@ -122,9 +133,11 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('Dataset Explore Tool').click() @@ -157,9 +170,11 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() @@ -192,9 +207,11 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('Dataset Explore Tool').click() @@ -235,9 +252,11 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('Dataset Explore Tool').should('exist').as('toolButton') @@ -260,9 +279,11 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() @@ -288,9 +309,11 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() @@ -316,9 +339,11 @@ describe('DatasetToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('Dataset Explore Tool').should('exist').click() diff --git a/tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx b/tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx index 639811886..3d720e64d 100644 --- a/tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx +++ b/tests/component/sections/dataset/dataset-action-buttons/access-dataset-menu/AccessDatasetMenu.spec.tsx @@ -5,12 +5,47 @@ import { DatasetVersionMother } from '../../../../dataset/domain/models/DatasetMother' import { FileSizeUnit } from '../../../../../../src/files/domain/models/FileMetadata' -import { getGuestbook, submitGuestbookForDatasetDownload } from '@iqss/dataverse-client-javascript' +import { submitGuestbookForDatasetDownload } from '@iqss/dataverse-client-javascript' import { ReactNode, Suspense } from 'react' import { useTranslation } from 'react-i18next' import { AccessRepository } from '@/access/domain/repositories/AccessRepository' import { DatasetPermissions } from '@/dataset/domain/models/Dataset' import { AccessRepositoryProvider } from '@/sections/access/AccessRepositoryProvider' +import { Guestbook } from '@/guestbooks/domain/models/Guestbook' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' +import { WithRepositories } from '@tests/component/WithRepositories' + +const guestbook: Guestbook = { + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 +} + +function createGuestbookRepository( + repositoryOverrides: Partial = {} +): GuestbookRepository { + return { + getGuestbook: cy.stub().resolves(guestbook), + getGuestbooksByCollectionId: cy.stub().resolves([]), + assignDatasetGuestbook: cy.stub().resolves(), + removeDatasetGuestbook: cy.stub().resolves(), + ...repositoryOverrides + } +} + +function withGuestbookRepository( + component: React.ReactNode, + guestbookRepository: GuestbookRepository +) { + return {component} +} function TranslationPreloader({ children }: { children: ReactNode }) { useTranslation('dataset') @@ -295,18 +330,7 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() const submitGuestbookForDatasetDownloadExecute = cy .stub(submitGuestbookForDatasetDownload, 'execute') .resolves('/api/v1/access/dataset/test-token') @@ -315,20 +339,23 @@ describe('AccessDatasetMenu', () => { }) cy.customMount( - - - - - + withGuestbookRepository( + + + + + , + guestbookRepository + ) ) cy.findByRole('button', { name: 'Access Dataset' }).click() @@ -351,42 +378,34 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.customMount( - - - - - + withGuestbookRepository( + + + + + , + guestbookRepository + ) ) - cy.wrap(getGuestbookExecute).should('not.have.been.called') + cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') cy.findByRole('button', { name: 'Access Dataset' }).click() cy.findByRole('button', { name: /Download ZIP/ }).click() - cy.wrap(getGuestbookExecute).should('have.been.calledOnceWith', 10) + cy.wrap(guestbookRepository.getGuestbook).should('have.been.calledOnceWith', 10) }) it('renders download option as a button and opens guestbook modal when guestbook exists', () => { @@ -397,34 +416,25 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - - cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.customMount( - - - - - + withGuestbookRepository( + + + + + , + guestbookRepository + ) ) cy.findByRole('button', { name: 'Access Dataset' }).click() @@ -441,34 +451,25 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - - cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.customMount( - - - - - + withGuestbookRepository( + + + + + , + guestbookRepository + ) ) cy.findByRole('button', { name: 'Access Dataset' }).click() @@ -486,34 +487,25 @@ describe('AccessDatasetMenu', () => { DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }), DatasetFileDownloadSizeMother.createArchival({ value: 4000, unit: FileSizeUnit.BYTES }) ] - - cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.customMount( - - - - - + withGuestbookRepository( + + + + + , + guestbookRepository + ) ) cy.findByRole('button', { name: 'Access Dataset' }).click() @@ -583,46 +575,38 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.window().then((window) => { cy.stub(window.HTMLAnchorElement.prototype, 'click').as('anchorClick') }) cy.customMount( - withAccessRepository( - - - - - + withGuestbookRepository( + withAccessRepository( + + + + + + ), + guestbookRepository ) ) cy.findByRole('button', { name: 'Access Dataset' }).click() cy.findByRole('button', { name: /Download ZIP/ }).click() - cy.wrap(getGuestbookExecute).should('not.have.been.called') + cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') cy.get('@anchorClick').should('have.been.calledOnce') cy.findByRole('dialog').should('not.exist') cy.findByText('Your download has started.').should('exist') @@ -637,46 +621,38 @@ describe('AccessDatasetMenu', () => { const fileDownloadSizes = [ DatasetFileDownloadSizeMother.createOriginal({ value: 2000, unit: FileSizeUnit.BYTES }) ] - const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.window().then((window) => { cy.stub(window.HTMLAnchorElement.prototype, 'click').as('anchorClick') }) cy.customMount( - withAccessRepository( - - - - - + withGuestbookRepository( + withAccessRepository( + + + + + + ), + guestbookRepository ) ) cy.findByRole('button', { name: 'Access Dataset' }).click() cy.findByRole('button', { name: /Download ZIP/ }).click() - cy.wrap(getGuestbookExecute).should('not.have.been.called') + cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') cy.get('@anchorClick').should('have.been.calledOnce') cy.findByRole('dialog').should('not.exist') cy.findByText('Your download has started.').should('exist') diff --git a/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx b/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx index 42e943b75..6d35de84c 100644 --- a/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/DatasetFiles.spec.tsx @@ -72,12 +72,8 @@ describe('DatasetFiles', () => { it('renders the files table', () => { cy.customMount( - - + + ) @@ -88,12 +84,8 @@ describe('DatasetFiles', () => { describe('Pagination navigation', () => { it('renders the files table with the correct header on a page different than the first one ', () => { cy.customMount( - - + + ) @@ -104,12 +96,8 @@ describe('DatasetFiles', () => { it('renders the files table with the correct page selected after updating the pageSize', () => { cy.customMount( - - + + ) @@ -124,12 +112,8 @@ describe('DatasetFiles', () => { it('renders the files table with the correct header with a different page size ', () => { cy.customMount( - - + + ) @@ -148,12 +132,8 @@ describe('DatasetFiles', () => { fileRepository.getFilesTotalDownloadSizeByDatasetPersistentId = cy.stub().resolves(19900) cy.customMount( - - + + ) @@ -184,12 +164,8 @@ describe('DatasetFiles', () => { fileRepository.getFilesTotalDownloadSizeByDatasetPersistentId = cy.stub().resolves(19900) cy.customMount( - - + + ) @@ -217,12 +193,8 @@ describe('DatasetFiles', () => { it('maintains the selection when the page changes', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -252,12 +224,8 @@ describe('DatasetFiles', () => { it('maintains the selection when the page size changes', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -278,12 +246,8 @@ describe('DatasetFiles', () => { it('removes the selection when the filters change', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -300,12 +264,8 @@ describe('DatasetFiles', () => { it('removes the selection when the Sort by changes', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -322,12 +282,8 @@ describe('DatasetFiles', () => { it('removes the selection when the Search bar is used', () => { cy.customMount( - - + + ) cy.findByRole('columnheader', { name: '1 to 10 of 200 Files' }).should('exist') @@ -353,10 +309,9 @@ describe('DatasetFiles', () => { }) cy.customMount( - + @@ -381,10 +336,9 @@ describe('DatasetFiles', () => { it('renders the zip download limit message when selecting all rows', () => { cy.customMount( - + @@ -401,10 +355,9 @@ describe('DatasetFiles', () => { it('renders the zip download limit message when selecting all rows and then navigating to other page', () => { cy.customMount( - + @@ -424,12 +377,8 @@ describe('DatasetFiles', () => { describe('Calling use cases', () => { it('calls the useFiles hook with the correct parameters', () => { cy.customMount( - - + + ) @@ -448,12 +397,8 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when sortBy criteria changes', () => { cy.customMount( - - + + ) @@ -470,12 +415,8 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when filterByType criteria changes', () => { cy.customMount( - - + + ) @@ -492,12 +433,8 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when filterByAccess criteria changes', () => { cy.customMount( - - + + ) @@ -514,12 +451,8 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when filterByTag criteria changes', () => { cy.customMount( - - + + ) @@ -536,12 +469,8 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when searchText criteria changes', () => { cy.customMount( - - + + ) @@ -557,12 +486,8 @@ describe('DatasetFiles', () => { it('calls the useFiles hook with the correct parameters when paginationInfo changes', () => { cy.customMount( - - + + ) @@ -577,10 +502,9 @@ describe('DatasetFiles', () => { it('calls getFilesTotalDownloadSizeByDatasetPersistentId with the correct parameters when applying search file criteria', () => { cy.customMount( - + diff --git a/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx b/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx index 006189e31..80e265339 100644 --- a/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/DatasetFilesScrollable.spec.tsx @@ -93,9 +93,8 @@ describe('DatasetFilesScrollable', () => { it('renders the scrollable files table', () => { cy.customMount( - + @@ -109,9 +108,8 @@ describe('DatasetFilesScrollable', () => { it('check that the files sections are rendered even without edit permissions', () => { cy.customMount( - + { it('renders the first 10 files', () => { cy.customMount( - + @@ -165,9 +162,8 @@ describe('DatasetFilesScrollable', () => { totalFilesCount: 0 }) cy.customMount( - + @@ -182,9 +178,8 @@ describe('DatasetFilesScrollable', () => { fileRepository.getAllByDatasetPersistentIdWithCount = cy.stub().resolves(only4Files) cy.customMount( - + @@ -202,9 +197,8 @@ describe('DatasetFilesScrollable', () => { it('loads more files when scrolling to the bottom ', () => { cy.customMount( - + @@ -225,9 +219,8 @@ describe('DatasetFilesScrollable', () => { it('scrolls to the top when criteria changes', () => { cy.customMount( - + @@ -251,9 +244,8 @@ describe('DatasetFilesScrollable', () => { describe('Sticky elements', () => { it('should stick the header table when scrolling down', () => { cy.customMount( - + @@ -278,10 +270,9 @@ describe('DatasetFilesScrollable', () => { it('should stick the table top messages on top of the table header when scrolling down with selected files', () => { cy.customMount( - + @@ -313,10 +304,9 @@ describe('DatasetFilesScrollable', () => { it('table header should have css top value according to criteria container height', () => { cy.customMount( - + @@ -351,10 +341,9 @@ describe('DatasetFilesScrollable', () => { it('table header should have css top value according to criteria container height + top messages container height when selected files ,top messages container should have top value only according to criteria container height', () => { cy.customMount( - + @@ -419,9 +408,8 @@ describe('DatasetFilesScrollable', () => { describe('File selection', () => { it('selects first 10 files when clicking the top header checkbox', () => { cy.customMount( - + @@ -439,9 +427,8 @@ describe('DatasetFilesScrollable', () => { it('selects all files when clicking the select all button', () => { cy.customMount( - + @@ -463,9 +450,8 @@ describe('DatasetFilesScrollable', () => { it('selects all files when clicking the select all button and mantains selection when loading more on scroll to bottom', () => { cy.customMount( - + @@ -504,9 +490,8 @@ describe('DatasetFilesScrollable', () => { it('maintains the selection when scrolling to bottom and loading more files', () => { cy.customMount( - + @@ -550,9 +535,8 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the header checkbox is clicked again', () => { cy.customMount( - + @@ -574,9 +558,8 @@ describe('DatasetFilesScrollable', () => { it('selects all loaded by scroll files when clicking the header checkbox', () => { cy.customMount( - + @@ -603,9 +586,8 @@ describe('DatasetFilesScrollable', () => { it('all new loaded files should be checked if selecting all files when only displayed 10 and then scrolling to bottom to load 10 more files', () => { cy.customMount( - + @@ -640,9 +622,8 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the filters change', () => { cy.customMount( - + @@ -658,9 +639,8 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the Sort by changes', () => { cy.customMount( - + @@ -676,9 +656,8 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the Search bar is used', () => { cy.customMount( - + @@ -693,9 +672,8 @@ describe('DatasetFilesScrollable', () => { it('removes the selection when the clear all button is clicked', () => { cy.customMount( - + @@ -718,10 +696,9 @@ describe('DatasetFilesScrollable', () => { metadata: FileMetadataMother.create({ size: new FileSize(2, FileSizeUnit.BYTES) }) }) cy.customMount( - + @@ -745,10 +722,9 @@ describe('DatasetFilesScrollable', () => { it('renders the zip download limit message when selecting all rows', () => { cy.customMount( - + @@ -766,10 +742,9 @@ describe('DatasetFilesScrollable', () => { it('renders the zip download limit message when selecting all rows and then scrolling to bottom to load more files', () => { cy.customMount( - + @@ -795,9 +770,8 @@ describe('DatasetFilesScrollable', () => { describe('Calling use cases', () => { it('calls the useGetAccumulatedFiles hook with the correct parameters', () => { cy.customMount( - + @@ -816,9 +790,8 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when sortBy criteria changes', () => { cy.customMount( - + @@ -836,9 +809,8 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when filterByType criteria changes', () => { cy.customMount( - + @@ -856,9 +828,8 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when filterByAccess criteria changes', () => { cy.customMount( - + @@ -876,9 +847,8 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when filterByTag criteria changes', () => { cy.customMount( - + @@ -896,9 +866,8 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when searchText criteria changes', () => { cy.customMount( - + @@ -915,9 +884,8 @@ describe('DatasetFilesScrollable', () => { }) it('calls the useGetAccumulatedFiles hook with the correct parameters when scrolling to bottom', () => { cy.customMount( - + @@ -947,10 +915,9 @@ describe('DatasetFilesScrollable', () => { }) it('calls getFilesTotalDownloadSizeByDatasetPersistentId with the correct parameters when applying search file criteria', () => { cy.customMount( - + @@ -983,9 +950,8 @@ describe('DatasetFilesScrollable', () => { .rejects(new Error('Some error on getAllByDatasetPersistentIdWithCount')) cy.customMount( - + @@ -998,9 +964,8 @@ describe('DatasetFilesScrollable', () => { fileRepository.getAllByDatasetPersistentIdWithCount = cy.stub().rejects(new Error()) cy.customMount( - + @@ -1015,9 +980,8 @@ describe('DatasetFilesScrollable', () => { .rejects(new Error('Some error on getFilesTotalDownloadSizeByDatasetPersistentId')) cy.customMount( - + @@ -1030,9 +994,8 @@ describe('DatasetFilesScrollable', () => { fileRepository.getFilesTotalDownloadSizeByDatasetPersistentId = cy.stub().rejects(new Error()) cy.customMount( - + @@ -1047,9 +1010,8 @@ describe('DatasetFilesScrollable', () => { .rejects(new Error('Some error on getFilesCountInfoByDatasetPersistentId')) cy.customMount( - + @@ -1062,9 +1024,8 @@ describe('DatasetFilesScrollable', () => { fileRepository.getFilesCountInfoByDatasetPersistentId = cy.stub().rejects(new Error()) cy.customMount( - + diff --git a/tests/component/sections/dataset/dataset-files/files-table/FilesTable.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/FilesTable.spec.tsx index eaf2b0529..ae3a9262e 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/FilesTable.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/FilesTable.spec.tsx @@ -25,14 +25,13 @@ const defaultCriteria = new FileCriteria() describe('FilesTable', () => { it('renders the files table', () => { cy.customMount( - + ) @@ -48,14 +47,13 @@ describe('FilesTable', () => { it('renders the spinner when the data isLoading', () => { cy.customMount( - + ) @@ -65,14 +63,13 @@ describe('FilesTable', () => { it('renders the no files message when there are no files', () => { cy.customMount( - + ) @@ -83,14 +80,13 @@ describe('FilesTable', () => { describe('Row selection', () => { it('selects all rows in the current page when the header checkbox is clicked', () => { cy.customMount( - + ) @@ -106,14 +102,13 @@ describe('FilesTable', () => { it('clears row selection for the current page when the header checkbox is clicked', () => { cy.customMount( - + ) @@ -133,14 +128,13 @@ describe('FilesTable', () => { it("selects all rows when the 'Select all' button is clicked", () => { cy.customMount( - + ) @@ -156,14 +150,13 @@ describe('FilesTable', () => { it('clears the selection when the clear selection button is clicked', () => { cy.customMount( - + ) @@ -181,14 +174,13 @@ describe('FilesTable', () => { it('highlights the selected rows', () => { cy.customMount( - + ) @@ -214,7 +206,7 @@ describe('FilesTable', () => { .resolves(SettingMother.createZipDownloadLimit(new ZipDownloadLimit(500, FileSizeUnit.BYTES))) cy.customMount( - + { isLoading={false} filesTotalDownloadSize={testFilesTotalDownloadSize} criteria={defaultCriteria} - fileRepository={fileRepository} /> @@ -241,14 +232,13 @@ describe('FilesTable', () => { it('renders the file actions column', () => { cy.customMount( - + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx index c443dd856..61085b02d 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/FileActionsHeader.spec.tsx @@ -20,11 +20,11 @@ describe('FileActionsHeader', () => { datasetRepository.getByPersistentId = cy.stub().resolves(datasetWithUpdatePermissions) const files = FilePreviewMother.createMany(2) cy.mountAuthenticated( - + - + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx index 077c3ae3c..e177e71e4 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/download-files/DownloadFilesButton.spec.tsx @@ -1,6 +1,5 @@ import { ReactNode, Suspense } from 'react' import { - getGuestbook, submitGuestbookForDatasetDownload, submitGuestbookForDatafilesDownload } from '@iqss/dataverse-client-javascript' @@ -24,10 +23,26 @@ import { CustomTermsMother, TermsOfUseMother } from '../../../../../../dataset/domain/models/TermsOfUseMother' +import { Guestbook } from '@/guestbooks/domain/models/Guestbook' +import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' +import { WithRepositories } from '@tests/component/WithRepositories' const datasetRepository: DatasetRepository = {} as DatasetRepository const fileRepository = {} as FileRepository describe('DownloadFilesButton', () => { + const guestbook: Guestbook = { + id: 10, + name: 'Guestbook Test', + enabled: true, + nameRequired: true, + emailRequired: true, + institutionRequired: false, + positionRequired: false, + customQuestions: [], + createTime: '2026-01-01T00:00:00.000Z', + dataverseId: 1 + } + const TranslationPreloader = ({ children }: { children: ReactNode }) => { useTranslation('files') useTranslation('dataset') @@ -67,6 +82,21 @@ describe('DownloadFilesButton', () => { ) } + const createGuestbookRepository = ( + repositoryOverrides: Partial = {} + ): GuestbookRepository => ({ + getGuestbook: cy.stub().resolves(guestbook), + getGuestbooksByCollectionId: cy.stub().resolves([]), + assignDatasetGuestbook: cy.stub().resolves(), + removeDatasetGuestbook: cy.stub().resolves(), + ...repositoryOverrides + }) + + const withGuestbookRepository = ( + component: ReactNode, + guestbookRepository: GuestbookRepository + ) => {component} + beforeEach(() => { fileRepository.getMultipleFileDownloadUrl = cy .stub() @@ -463,32 +493,24 @@ describe('DownloadFilesButton', () => { const fileSelection = { 'some-file-id': files[0] } - const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.mountAuthenticated( - withDataset( - , - datasetWithGuestbook + withGuestbookRepository( + withDataset( + , + datasetWithGuestbook + ), + guestbookRepository ) ) - cy.wrap(getGuestbookExecute).should('not.have.been.called') + cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') cy.get('#download-files').click() cy.findByRole('button', { name: 'Original Format' }).click() - cy.wrap(getGuestbookExecute).should('have.been.calledOnceWith', 10) + cy.wrap(guestbookRepository.getGuestbook).should('have.been.calledOnceWith', 10) }) it('submits guestbook for the dataset when all files are selected and guestbook exists', () => { @@ -509,18 +531,7 @@ describe('DownloadFilesButton', () => { 'file-c': undefined } - cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() const submitDatasetStub = cy .stub(submitGuestbookForDatasetDownload, 'execute') .resolves('/api/v1/access/dataset/999?token=test') @@ -532,9 +543,12 @@ describe('DownloadFilesButton', () => { }) cy.mountAuthenticated( - withDataset( - , - datasetWithGuestbook + withGuestbookRepository( + withDataset( + , + datasetWithGuestbook + ), + guestbookRepository ) ) @@ -563,35 +577,27 @@ describe('DownloadFilesButton', () => { const fileSelection = { 'some-file-id': files[0] } - const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.window().then((window) => { cy.stub(window.HTMLAnchorElement.prototype, 'click').as('anchorClick') }) cy.mountAuthenticated( - withAccessRepository( - withDataset( - , - datasetWithGuestbook - ) + withGuestbookRepository( + withAccessRepository( + withDataset( + , + datasetWithGuestbook + ) + ), + guestbookRepository ) ) cy.get('#download-files').click() - cy.wrap(getGuestbookExecute).should('not.have.been.called') + cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') cy.get('@anchorClick').should('have.been.calledOnce') cy.findByRole('dialog').should('not.exist') cy.findByText('Your download has started.').should('exist') @@ -612,35 +618,27 @@ describe('DownloadFilesButton', () => { const fileSelection = { 'some-file-id': files[0] } - const getGuestbookExecute = cy.stub(getGuestbook, 'execute').resolves({ - id: 10, - name: 'Guestbook Test', - enabled: true, - nameRequired: true, - emailRequired: true, - institutionRequired: false, - positionRequired: false, - customQuestions: [], - createTime: '2026-01-01T00:00:00.000Z', - dataverseId: 1 - }) + const guestbookRepository = createGuestbookRepository() cy.window().then((window) => { cy.stub(window.HTMLAnchorElement.prototype, 'click').as('anchorClick') }) cy.mountAuthenticated( - withAccessRepository( - withDataset( - , - datasetWithGuestbook - ) + withGuestbookRepository( + withAccessRepository( + withDataset( + , + datasetWithGuestbook + ) + ), + guestbookRepository ) ) cy.get('#download-files').click() - cy.wrap(getGuestbookExecute).should('not.have.been.called') + cy.wrap(guestbookRepository.getGuestbook).should('not.have.been.called') cy.get('@anchorClick').should('have.been.calledOnce') cy.findByRole('dialog').should('not.exist') cy.findByText('Your download has started.').should('exist') diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx index 0200ace9b..ea0282851 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesMenu.spec.tsx @@ -25,7 +25,7 @@ describe('EditFilesMenu', () => { datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -37,10 +37,7 @@ describe('EditFilesMenu', () => { it('renders the Edit Files menu', () => { cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.get('#edit-files-menu').should('exist') @@ -48,10 +45,7 @@ describe('EditFilesMenu', () => { it('does not render the Edit Files menu when the user is not authenticated', () => { cy.customMount( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.get('#edit-files-menu').should('not.exist') @@ -59,10 +53,7 @@ describe('EditFilesMenu', () => { it('does not render the Edit Files menu when there are no files in the dataset', () => { cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.get('#edit-files-menu').should('not.exist') @@ -70,10 +61,7 @@ describe('EditFilesMenu', () => { it('renders the Edit Files options', () => { cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.get('#edit-files-menu').click() @@ -87,7 +75,7 @@ describe('EditFilesMenu', () => { cy.mountAuthenticated( withDataset( - , + , datasetWithNoUpdatePermissions ) ) @@ -102,10 +90,7 @@ describe('EditFilesMenu', () => { }) cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.get('#edit-files-menu').should('be.disabled') @@ -118,10 +103,7 @@ describe('EditFilesMenu', () => { }) cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.get('#edit-files-menu').should('be.disabled') diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx index 05c60829f..aa86cb6b9 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/edit-files-menu/EditFilesOptions.spec.tsx @@ -21,13 +21,8 @@ const datasetInfo = { describe('EditFilesOptions', () => { it('renders the EditFilesOptions', () => { cy.customMount( - - + + ) @@ -41,13 +36,8 @@ describe('EditFilesOptions', () => { it('renders the restrict option if some file is unrestricted', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -59,13 +49,8 @@ describe('EditFilesOptions', () => { it('renders the unrestrict option if some file is restricted', () => { const fileRestricted = FilePreviewMother.createRestricted() cy.customMount( - - + + ) @@ -76,13 +61,8 @@ describe('EditFilesOptions', () => { it.skip('renders the embargo option if the embargo is allowed by settings', () => { cy.customMount( - - + + ) @@ -93,13 +73,8 @@ describe('EditFilesOptions', () => { it.skip('renders provenance option if provenance is enabled in config', () => { cy.customMount( - - + + ) @@ -110,13 +85,8 @@ describe('EditFilesOptions', () => { it('shows the No Selected Files message when no files are selected and one option is clicked', () => { cy.customMount( - - + + ) @@ -131,11 +101,10 @@ describe('EditFilesOptions', () => { it('does not show the No Selected Files message when files are selected and one option is clicked', () => { cy.customMount( - + @@ -153,13 +122,8 @@ describe('EditFilesOptions for a single file', () => { it('renders the EditFilesOptions', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -182,13 +146,8 @@ describe('EditFilesOptions for a single file', () => { it('renders the restrict option if some file is unrestricted', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -202,13 +161,8 @@ describe('EditFilesOptions for a single file', () => { it('renders the unrestrict option if file is restricted', () => { const fileRestricted = FilePreviewMother.createRestricted() cy.customMount( - - + + ) @@ -223,13 +177,8 @@ describe('EditFilesOptions for a single file', () => { it('renders delete modal', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -243,10 +192,9 @@ describe('EditFilesOptions for a single file', () => { it('should delete file if delete button clicked', () => { fileRepository.delete = cy.stub().resolves() cy.customMount( - + @@ -263,13 +211,8 @@ describe('EditFilesOptions for a single file', () => { it('should reset the modal if cancel is clicked in delete modal', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -292,13 +235,8 @@ describe('EditFilesOptions for a single file', () => { const fileToDelete = FilePreviewMother.createDefault() cy.customMount( - - + + , [ `${Route.DATASETS}?${QueryParamKey.PERSISTENT_ID}=${datasetInfo.persistentId}&${QueryParamKey.VERSION}=DRAFT` @@ -324,10 +262,9 @@ describe('EditFilesOptions for a single file', () => { it('should restrict file if restrict button clicked', () => { fileRepository.restrict = cy.stub().resolves() cy.customMount( - + @@ -346,13 +283,8 @@ describe('EditFilesOptions for a single file', () => { const fileRestricted = FilePreviewMother.createRestricted() cy.customMount( - - + + ) @@ -367,10 +299,9 @@ describe('EditFilesOptions for a single file', () => { it('should restrict file and call refreshFiles if restrict button clicked in draft version', () => { fileRepository.restrict = cy.stub().resolves() cy.customMount( - + @@ -390,13 +321,8 @@ describe('EditFilesOptions for a single file', () => { it('should reset the modal if cancel is clicked in restrict modal', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -413,13 +339,8 @@ describe('EditFilesOptions for a single file', () => { it('opens and closes the edit file tags modal', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -434,13 +355,8 @@ describe('EditFilesOptions for a single file', () => { const fileWithTags = FilePreviewMother.createWithLabels() cy.customMount( - - + + ) @@ -460,13 +376,8 @@ describe('EditFilesOptions for a single file', () => { const fileUnrestricted = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -482,13 +393,8 @@ describe('EditFilesOptions for a single file', () => { const fileRestricted = FilePreviewMother.createRestricted() cy.customMount( - - + + ) @@ -504,13 +410,8 @@ describe('EditFilesOptions for a single file', () => { const fileToDelete = FilePreviewMother.createDefault() cy.customMount( - - + + ) @@ -528,13 +429,8 @@ describe('EditFilesOptions for a single file', () => { fileRepository.updateCategories = cy.stub().resolves() cy.customMount( - - + + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.spec.tsx index 9240bdfb8..5d0da5783 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileActionButtons.spec.tsx @@ -16,8 +16,8 @@ const datasetRepository: DatasetRepository = {} as DatasetRepository describe('FileActionButtons', () => { it('renders the file action buttons', () => { cy.customMount( - - + + ) @@ -35,11 +35,11 @@ describe('FileActionButtons', () => { datasetRepository.getByPrivateUrlToken = cy.stub().resolves(datasetWithUpdatePermissions) cy.mountAuthenticated( - + - + ) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileTools.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileTools.spec.tsx index 9ff86f86a..948aea5a6 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileTools.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/FileTools.spec.tsx @@ -5,6 +5,7 @@ import { ExternalToolsProvider } from '@/shared/contexts/external-tools/External import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { FileMetadataMother } from '@tests/component/files/domain/models/FileMetadataMother' import { FilePreviewMother } from '@tests/component/files/domain/models/FilePreviewMother' +import { WithRepositories } from '@tests/component/WithRepositories' const testFilePreview = FilePreviewMother.createDefault() // text/plain file const testExternalToolsRepository: ExternalToolsRepository = {} as ExternalToolsRepository @@ -21,9 +22,11 @@ describe('FileTools', () => { it('renders external tool buttons when user can download the file and there are applicable tools', () => { cy.customMount( - - - + + + + + ) cy.findByRole('link', { name: `Preview ${testFilePreview.name}` }) @@ -49,9 +52,11 @@ describe('FileTools', () => { it('does not render external tool buttons when user cannot download the file', () => { cy.customMount( - - - + + + + + ) }) @@ -63,9 +68,11 @@ describe('FileTools', () => { }) cy.customMount( - - - + + + + + ) }) }) diff --git a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.spec.tsx b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.spec.tsx index eab1da5a2..6ea05e687 100644 --- a/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/file-action-buttons/file-options-menu/FileOptionsMenu.spec.tsx @@ -25,7 +25,7 @@ describe('FileOptionsMenu', () => { datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -37,20 +37,14 @@ describe('FileOptionsMenu', () => { it('renders the FileOptionsMenu', () => { cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'File Options' }).should('exist') }) it('renders the file options menu with tooltip', () => { cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'File Options' }).trigger('mouseover') @@ -59,10 +53,7 @@ describe('FileOptionsMenu', () => { it('renders the dropdown header', () => { cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'File Options' }).should('exist').click() @@ -70,12 +61,7 @@ describe('FileOptionsMenu', () => { }) it('does not render is the user is not authenticated', () => { - cy.customMount( - withDataset( - , - datasetWithUpdatePermissions - ) - ) + cy.customMount(withDataset(, datasetWithUpdatePermissions)) cy.findByRole('button', { name: 'File Options' }).should('not.exist') }) @@ -85,10 +71,7 @@ describe('FileOptionsMenu', () => { permissions: DatasetPermissionsMother.createWithUpdateDatasetNotAllowed() }) cy.mountAuthenticated( - withDataset( - , - datasetWithNoUpdatePermissions - ) + withDataset(, datasetWithNoUpdatePermissions) ) cy.findByRole('button', { name: 'File Options' }).should('not.exist') }) @@ -97,12 +80,7 @@ describe('FileOptionsMenu', () => { const datasetWithNoTermsOfAccess = DatasetMother.create({ hasValidTermsOfAccess: false }) - cy.mountAuthenticated( - withDataset( - , - datasetWithNoTermsOfAccess - ) - ) + cy.mountAuthenticated(withDataset(, datasetWithNoTermsOfAccess)) cy.findByRole('button', { name: 'File Options' }).should('not.exist') }) @@ -112,12 +90,7 @@ describe('FileOptionsMenu', () => { locks: [DatasetLockMother.createLockedInEditInProgress()], hasValidTermsOfAccess: true }) - cy.mountAuthenticated( - withDataset( - , - datasetLockedFromEdits - ) - ) + cy.mountAuthenticated(withDataset(, datasetLockedFromEdits)) cy.findByRole('button', { name: 'File Options' }).should('exist').should('be.disabled') }) @@ -126,10 +99,7 @@ describe('FileOptionsMenu', () => { const file = FilePreviewMother.createDeleted() cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'File Options' }).should('exist').click() @@ -143,10 +113,7 @@ describe('FileOptionsMenu', () => { it('renders the menu options', () => { cy.mountAuthenticated( - withDataset( - , - datasetWithUpdatePermissions - ) + withDataset(, datasetWithUpdatePermissions) ) cy.findByRole('button', { name: 'File Options' }).click() cy.findByRole('button', { name: 'Restrict' }).should('exist') diff --git a/tests/component/sections/dataset/dataset-files/guestbook/DownloadWithTermsAndGuestbookModal.spec.tsx b/tests/component/sections/dataset/dataset-files/guestbook/DownloadWithTermsAndGuestbookModal.spec.tsx index 66095d974..42c8e564c 100644 --- a/tests/component/sections/dataset/dataset-files/guestbook/DownloadWithTermsAndGuestbookModal.spec.tsx +++ b/tests/component/sections/dataset/dataset-files/guestbook/DownloadWithTermsAndGuestbookModal.spec.tsx @@ -9,9 +9,9 @@ import { GuestbookResponseDTO } from '@/access/domain/repositories/AccessRepository' import { AccessRepositoryProvider } from '@/sections/access/AccessRepositoryProvider' -import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' import { SessionContext } from '@/sections/session/SessionContext' import { DatasetMother } from '@tests/component/dataset/domain/models/DatasetMother' +import { WithRepositories } from '@tests/component/WithRepositories' const guestbook: Guestbook = { id: 10, @@ -113,11 +113,11 @@ describe('DownloadWithTermsAndGuestbookModal', () => { isLoading: false, refreshDataset: () => {} }}> - + {component} - + ) diff --git a/tests/component/sections/dataset/dataset-guestbook/DatasetGuestbook.spec.tsx b/tests/component/sections/dataset/dataset-guestbook/DatasetGuestbook.spec.tsx index 42df75645..926cc83da 100644 --- a/tests/component/sections/dataset/dataset-guestbook/DatasetGuestbook.spec.tsx +++ b/tests/component/sections/dataset/dataset-guestbook/DatasetGuestbook.spec.tsx @@ -1,10 +1,10 @@ import { DatasetGuestbook } from '@/sections/dataset/dataset-guestbook/DatasetGuestbook' import { DatasetContext } from '@/sections/dataset/DatasetContext' -import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' import { Guestbook } from '@/guestbooks/domain/models/Guestbook' import { Dataset as DatasetModel } from '@/dataset/domain/models/Dataset' import { DatasetMother } from '@tests/component/dataset/domain/models/DatasetMother' +import { WithRepositories } from '@tests/component/WithRepositories' const guestbook: Guestbook = { id: 10, @@ -35,7 +35,7 @@ describe('DatasetGuestbook', () => { dataset: DatasetModel = DatasetMother.create({ guestbookId: guestbook.id }) ) => cy.customMount( - + { }}> - + ) it('renders a spinner while the guestbook is loading', () => { diff --git a/tests/component/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.spec.tsx b/tests/component/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.spec.tsx index 79c507b43..c918b2936 100644 --- a/tests/component/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata.spec.tsx @@ -7,15 +7,17 @@ import i18next from '@/i18n' import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' import { useExportMetadata } from '@/sections/dataset/dataset-metadata/export-metadata-dropdown/useExportMetadata' import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' +import { WithRepositories } from '@tests/component/WithRepositories' const testDatasetPersistentId = 'doi:10.70122/FK2/XXXXXX' -const wrapper = ({ children }: { children: ReactNode }) => ( - {children} -) - describe('useExportMetadata', () => { const datasetRepository: DatasetRepository = {} as DatasetRepository + const wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ) let openedWindow: { closed: boolean document: { title: string } @@ -50,7 +52,6 @@ describe('useExportMetadata', () => { const { result } = renderHook( () => useExportMetadata({ - datasetRepository, datasetPersistentId: testDatasetPersistentId, datasetVersion: DatasetVersionMother.createReleased() }), @@ -82,7 +83,6 @@ describe('useExportMetadata', () => { const { result } = renderHook( () => useExportMetadata({ - datasetRepository, datasetPersistentId: testDatasetPersistentId, datasetVersion: DatasetVersionMother.createDraft() }), @@ -113,7 +113,6 @@ describe('useExportMetadata', () => { const { result } = renderHook( () => useExportMetadata({ - datasetRepository, datasetPersistentId: testDatasetPersistentId, datasetVersion: DatasetVersionMother.createReleased() }), @@ -141,7 +140,6 @@ describe('useExportMetadata', () => { const { result } = renderHook( () => useExportMetadata({ - datasetRepository, datasetPersistentId: testDatasetPersistentId, datasetVersion: DatasetVersionMother.createReleased() }), diff --git a/tests/component/sections/dataset/dataset-reviews/useGetDatasetReviews.spec.tsx b/tests/component/sections/dataset/dataset-reviews/useGetDatasetReviews.spec.tsx new file mode 100644 index 000000000..4967a3686 --- /dev/null +++ b/tests/component/sections/dataset/dataset-reviews/useGetDatasetReviews.spec.tsx @@ -0,0 +1,104 @@ +import { renderHook, waitFor } from '@testing-library/react' +import { DatasetReview } from '@/dataset/domain/models/DatasetReview' +import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' +import { useGetDatasetReviews } from '@/sections/dataset/dataset-reviews/useGetDatasetReviews' + +const datasetRepository: DatasetRepository = {} as DatasetRepository +const datasetId = 'doi:10.5072/FK2/ABC123' +const datasetReviews: DatasetReview[] = [ + { + id: 23, + title: 'Review of Some Title', + authors: ['Reviewer One'], + persistentId: 'doi:10.5072/FK2/REVIEW1', + persistentIdUrl: 'https://doi.org/10.5072/FK2/REVIEW1', + citation: 'Review citation', + citationHtml: 'Review citation', + datePublished: '2026-02-03', + description: 'A review dataset', + rubricMetadataBlocks: [] + } +] + +describe('useGetDatasetReviews', () => { + it('should return dataset reviews correctly', async () => { + datasetRepository.getDatasetReviews = cy.stub().resolves(datasetReviews) + + const { result } = renderHook(() => + useGetDatasetReviews({ + datasetRepository, + datasetId + }) + ) + + expect(result.current.isLoading).to.equal(true) + expect(result.current.error).to.equal(null) + expect(result.current.datasetReviews).to.deep.equal([]) + + await waitFor(() => { + expect(result.current.isLoading).to.equal(false) + expect(result.current.error).to.equal(null) + expect(result.current.datasetReviews).to.deep.equal(datasetReviews) + }) + cy.wrap(datasetRepository.getDatasetReviews).should('have.been.calledWith', datasetId) + }) + + it('should return the error message when the request fails with an Error', async () => { + datasetRepository.getDatasetReviews = cy.stub().rejects(new Error('Error message')) + + const { result } = renderHook(() => + useGetDatasetReviews({ + datasetRepository, + datasetId + }) + ) + + expect(result.current.isLoading).to.equal(true) + expect(result.current.error).to.equal(null) + expect(result.current.datasetReviews).to.deep.equal([]) + + await waitFor(() => { + expect(result.current.isLoading).to.equal(false) + expect(result.current.error).to.equal('Error message') + expect(result.current.datasetReviews).to.deep.equal([]) + }) + }) + + it('should return the default error message when the request fails with an Error without a message', async () => { + datasetRepository.getDatasetReviews = cy.stub().rejects(new Error('')) + + const { result } = renderHook(() => + useGetDatasetReviews({ + datasetRepository, + datasetId + }) + ) + + await waitFor(() => { + expect(result.current.isLoading).to.equal(false) + expect(result.current.error).to.equal( + 'Something went wrong getting the dataset reviews. Try again later.' + ) + expect(result.current.datasetReviews).to.deep.equal([]) + }) + }) + + it('should return the default error message when the request fails with a non-Error exception', async () => { + datasetRepository.getDatasetReviews = cy.stub().rejects('Unexpected error') + + const { result } = renderHook(() => + useGetDatasetReviews({ + datasetRepository, + datasetId + }) + ) + + await waitFor(() => { + expect(result.current.isLoading).to.equal(false) + expect(result.current.error).to.equal( + 'Something went wrong getting the dataset reviews. Try again later.' + ) + expect(result.current.datasetReviews).to.deep.equal([]) + }) + }) +}) diff --git a/tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx b/tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx index 7bcb507c5..72ea785a3 100644 --- a/tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx +++ b/tests/component/sections/dataset/dataset-terms/DatasetTerms.spec.tsx @@ -10,9 +10,9 @@ import { } from '../../../dataset/domain/models/TermsOfUseMother' import { DatasetContext } from '@/sections/dataset/DatasetContext' import { Dataset as DatasetModel } from '@/dataset/domain/models/Dataset' -import { ReactNode } from 'react' +import { ComponentProps, ReactNode } from 'react' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' -import { GuestbookRepositoryProvider } from '@/sections/guestbooks/GuestbookRepositoryProvider' +import { WithRepositories } from '@tests/component/WithRepositories' const datasetPersistentId = 'test-dataset-persistent-id' const datasetVersion = DatasetMother.create().version @@ -54,17 +54,18 @@ const termsOfUseWithUndefinedValue = TermsOfUseMother.create({ }) const guestbookRepository: GuestbookRepository = {} as GuestbookRepository +const DatasetTermsWithRepositories = (props: ComponentProps) => ( + + + +) + describe('DatasetTerms', () => { const withDatasetContext = (component: ReactNode, dataset?: DatasetModel) => ( {} }}> {component} ) - const withGuestbookRepository = (component: ReactNode) => ( - - {component} - - ) beforeEach(() => { fileRepository.getFilesCountInfoByDatasetPersistentId = cy @@ -75,10 +76,9 @@ describe('DatasetTerms', () => { it('renders the license and terms of use sections', () => { cy.customMount( - @@ -91,10 +91,9 @@ describe('DatasetTerms', () => { it('shows no guestbook assigned message after expanding the guestbook accordion', () => { cy.customMount( withDatasetContext( - , @@ -127,17 +126,14 @@ describe('DatasetTerms', () => { }) cy.customMount( - withGuestbookRepository( - withDatasetContext( - , - DatasetMother.create({ guestbookId }) - ) + withDatasetContext( + , + DatasetMother.create({ guestbookId }) ) ) @@ -167,17 +163,14 @@ describe('DatasetTerms', () => { }) cy.customMount( - withGuestbookRepository( - withDatasetContext( - , - DatasetMother.create({ guestbookId }) - ) + withDatasetContext( + , + DatasetMother.create({ guestbookId }) ), ['/datasets?tab=terms&termsTab=guestbook'] ) @@ -188,10 +181,9 @@ describe('DatasetTerms', () => { it('check that the terms of use sections are rendered even without edit permissions', () => { cy.customMount( - { it('renders the correct number of restricted files', () => { cy.customMount( - @@ -225,10 +216,9 @@ describe('DatasetTerms', () => { }) it('does not render a row if the value is undefined', () => { cy.customMount( - @@ -242,10 +232,9 @@ describe('DatasetTerms', () => { .stub() .resolves(singleRestrictedFilesCountInfo) cy.customMount( - @@ -256,10 +245,9 @@ describe('DatasetTerms', () => { }) it('renders the custom terms', () => { cy.customMount( - @@ -273,10 +261,9 @@ describe('DatasetTerms', () => { it('renders the request access allowed message', () => { cy.customMount( - @@ -287,10 +274,9 @@ describe('DatasetTerms', () => { }) it('renders the request access not allowed message', () => { cy.customMount( - @@ -302,10 +288,9 @@ describe('DatasetTerms', () => { it('renders the data access place', () => { cy.customMount( - @@ -317,10 +302,9 @@ describe('DatasetTerms', () => { it('renders the original archive information', () => { cy.customMount( - @@ -332,10 +316,9 @@ describe('DatasetTerms', () => { it('renders the availability status', () => { cy.customMount( - @@ -347,10 +330,9 @@ describe('DatasetTerms', () => { it('renders the contact for access information', () => { cy.customMount( - @@ -362,10 +344,9 @@ describe('DatasetTerms', () => { it('renders the size of collection information', () => { cy.customMount( - @@ -377,10 +358,9 @@ describe('DatasetTerms', () => { it('renders the study completion information', () => { cy.customMount( - @@ -394,10 +374,9 @@ describe('DatasetTerms', () => { .stub() .resolves(noRestrictedFilesCountInfo) cy.customMount( - @@ -410,10 +389,9 @@ describe('DatasetTerms', () => { .stub() .resolves(singleRestrictedFilesCountInfo) cy.customMount( - @@ -426,10 +404,9 @@ describe('DatasetTerms', () => { .stub() .resolves(noRestrictedFilesCountInfo) cy.customMount( - diff --git a/tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx b/tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx index 0caf998fd..3fee56978 100644 --- a/tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx +++ b/tests/component/sections/edit-dataset-terms/EditDatasetTerms.spec.tsx @@ -93,7 +93,9 @@ describe('EditDatasetTerms', () => { datasetRepository.getByPersistentId = cy.stub().resolves(dataset) datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -151,7 +153,6 @@ describe('EditDatasetTerms', () => { ) @@ -171,7 +172,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -193,7 +193,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -226,7 +225,6 @@ describe('EditDatasetTerms', () => { EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS.restrictedFilesTerms } licenseRepository={licenseRepository} - guestbookRepository={guestbookRepository} />, dataset ) @@ -251,7 +249,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -270,7 +267,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -309,7 +305,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -343,8 +338,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -367,7 +360,6 @@ describe('EditDatasetTerms', () => { // Force an invalid key to hit the default branch in getCurrentFormDirtyState defaultActiveTabKey={'unknown-tab' as unknown as EditDatasetTermsTabKey} licenseRepository={licenseRepository} - datasetRepository={datasetRepository} />, dataset ) @@ -389,7 +381,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -413,7 +404,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -434,7 +424,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -464,7 +453,6 @@ describe('EditDatasetTerms', () => { EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS.restrictedFilesTerms } licenseRepository={licenseRepository} - guestbookRepository={guestbookRepository} />, dataset ) @@ -493,7 +481,6 @@ describe('EditDatasetTerms', () => { EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS.restrictedFilesTerms } licenseRepository={licenseRepository} - guestbookRepository={guestbookRepository} />, dataset ) @@ -537,7 +524,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -561,7 +547,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -583,7 +568,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -607,7 +591,9 @@ describe('EditDatasetTerms', () => { datasetRepository.getByPersistentId = cy.stub().resolves(dataset) datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -623,7 +609,6 @@ describe('EditDatasetTerms', () => { , undefined ) @@ -641,7 +626,6 @@ describe('EditDatasetTerms', () => { EditDatasetTermsHelper.EDIT_DATASET_TERMS_TABS_KEYS.restrictedFilesTerms } licenseRepository={licenseRepository} - guestbookRepository={guestbookRepository} />, dataset ) @@ -663,7 +647,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -681,7 +664,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -700,7 +682,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -722,7 +703,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -747,7 +727,6 @@ describe('EditDatasetTerms', () => { , dataset ) @@ -763,7 +742,9 @@ describe('EditDatasetTerms Mobile View', () => { datasetRepository.getByPersistentId = cy.stub().resolves(dataset) datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - + @@ -785,7 +766,6 @@ describe('EditDatasetTerms Mobile View', () => { , dataset ) diff --git a/tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx b/tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx index 3dff96744..ff3814cf3 100644 --- a/tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx +++ b/tests/component/sections/edit-dataset-terms/EditGuestbook.spec.tsx @@ -12,6 +12,7 @@ import { import { Dataset } from '@/dataset/domain/models/Dataset' import { Guestbook } from '@/guestbooks/domain/models/Guestbook' import { GuestbookRepository } from '@/guestbooks/domain/repositories/GuestbookRepository' +import { WithRepositories } from '@tests/component/WithRepositories' const LocationDisplay = () => { const location = useLocation() @@ -62,23 +63,27 @@ describe('EditGuestbook', () => { datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) return ( - - {component} - + + + {component} + + ) } const withDatasetContext = (component: ReactNode, dataset: Dataset | undefined) => ( - {} - }}> - {component} - + + {} + }}> + {component} + + ) beforeEach(() => { @@ -93,9 +98,7 @@ describe('EditGuestbook', () => { it('renders guestbook options and keeps Save Changes disabled for current guestbook', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByLabelText('Data Request Guestbook').should('be.checked') cy.findByLabelText('Secondary Guestbook').should('not.be.checked') @@ -106,9 +109,7 @@ describe('EditGuestbook', () => { it('enables Save Changes when selecting a different guestbook', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByLabelText('Secondary Guestbook').click() cy.findByRole('button', { name: 'Save Changes' }).should('be.enabled') @@ -118,15 +119,7 @@ describe('EditGuestbook', () => { const onFormStateChange = cy.stub().as('onFormStateChange') const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders( - , - dataset - ) - ) + cy.customMount(withProviders(, dataset)) cy.findByLabelText('Secondary Guestbook').click() @@ -136,9 +129,7 @@ describe('EditGuestbook', () => { it('keeps Save Changes disabled when dataset has no assigned guestbook and none is selected', () => { const dataset = DatasetMother.create({ guestbookId: undefined }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByLabelText('Data Request Guestbook').should('not.be.checked') cy.findByLabelText('Secondary Guestbook').should('not.be.checked') @@ -149,9 +140,7 @@ describe('EditGuestbook', () => { it('falls back to no preselection when dataset has no guestbook id', () => { const dataset = DatasetMother.create({ guestbookId: undefined }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByLabelText('Data Request Guestbook').should('not.be.checked') cy.findByLabelText('Secondary Guestbook').should('not.be.checked') @@ -162,9 +151,7 @@ describe('EditGuestbook', () => { it('clears the selected guestbook when clicking Clear Selection', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByRole('button', { name: 'Clear Selection' }).click() @@ -180,9 +167,7 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByLabelText('Data Request Guestbook').should('not.exist') cy.findByLabelText('Secondary Guestbook').should('not.exist') @@ -200,9 +185,7 @@ describe('EditGuestbook', () => { it('opens preview modal when clicking Preview Guestbook', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findAllByRole('button', { name: 'Preview Guestbook' }).should('have.length', 2) cy.findAllByRole('button', { name: 'Preview Guestbook' }).eq(1).click() @@ -226,9 +209,7 @@ describe('EditGuestbook', () => { it('closes preview modal when clicking Close', () => { const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findAllByRole('button', { name: 'Preview Guestbook' }).eq(0).click() cy.findByRole('dialog').should('be.visible') @@ -240,12 +221,7 @@ describe('EditGuestbook', () => { const onPreview = cy.stub().as('onPreview') const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders( - , - dataset - ) - ) + cy.customMount(withProviders(, dataset)) cy.findAllByRole('button', { name: 'Preview Guestbook' }).eq(0).click() cy.get('@onPreview').should('have.been.calledOnce') @@ -259,9 +235,7 @@ describe('EditGuestbook', () => { assignDatasetGuestbookStub.as('assignDatasetGuestbookExecute') const dataset = DatasetMother.create({ id: 999, guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByLabelText('Secondary Guestbook').click() cy.findByRole('button', { name: 'Save Changes' }).click() @@ -287,7 +261,7 @@ describe('EditGuestbook', () => { cy.customMount( withProviders( <> - + , releasedDataset @@ -316,7 +290,7 @@ describe('EditGuestbook', () => { cy.customMount( withProviders( <> - + , draftDataset @@ -345,7 +319,7 @@ describe('EditGuestbook', () => { cy.customMount( withProviders( <> - + , releasedDataset @@ -385,7 +359,7 @@ describe('EditGuestbook', () => { const [dataset, setDataset] = useState(createDataset(collectionA)) return ( <> - {withDatasetContext(, dataset)} + {withDatasetContext(, dataset)} @@ -427,7 +401,7 @@ describe('EditGuestbook', () => { const [dataset, setDataset] = useState(createDataset(collectionA)) return ( <> - {withDatasetContext(, dataset)} + {withDatasetContext(, dataset)} @@ -452,9 +426,7 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ id: 999, guestbookId: undefined }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.get('form').submit() cy.get('@assignDatasetGuestbookExecute').should('not.have.been.called') @@ -469,9 +441,7 @@ describe('EditGuestbook', () => { 'removeDatasetGuestbookExecute' ) - cy.customMount( - withDatasetContext(, undefined) - ) + cy.customMount(withDatasetContext(, undefined)) cy.get('form').submit() cy.get('@assignDatasetGuestbookExecute').should('not.have.been.called') @@ -485,9 +455,7 @@ describe('EditGuestbook', () => { removeDatasetGuestbookStub.as('removeDatasetGuestbookExecute') const dataset = DatasetMother.create({ id: 999, guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByRole('button', { name: 'Clear Selection' }).click() cy.findByRole('button', { name: 'Save Changes' }).click() @@ -502,9 +470,7 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ id: 999, guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByLabelText('Secondary Guestbook').click() cy.findByRole('button', { name: 'Save Changes' }).click() @@ -519,9 +485,7 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ id: 999, guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByRole('button', { name: 'Clear Selection' }).click() cy.findByRole('button', { name: 'Save Changes' }).click() @@ -536,9 +500,7 @@ describe('EditGuestbook', () => { ) const dataset = DatasetMother.create({ guestbookId: mockGuestbooks[0].id }) - cy.customMount( - withProviders(, dataset) - ) + cy.customMount(withProviders(, dataset)) cy.findByText( /Something went wrong getting guestbooks by collection id. Try again later./ diff --git a/tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx b/tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx index d5865d514..dae92cf97 100644 --- a/tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx +++ b/tests/component/sections/edit-dataset-terms/EditTermsOfAccess.spec.tsx @@ -38,6 +38,10 @@ const mockDataset = DatasetMother.create({ }) describe('EditTermsOfAccess', () => { + beforeEach(() => { + datasetRepository.updateTermsOfAccess = cy.stub().resolves() + }) + const withProviders = (component: ReactNode, dataset: Dataset) => { datasetRepository.getByPersistentId = cy.stub().resolves(dataset) datasetRepository.getByPrivateUrlToken = cy.stub().resolves(dataset) diff --git a/tests/component/sections/file/File.spec.tsx b/tests/component/sections/file/File.spec.tsx index 1f9cf81bd..c9d6a18ae 100644 --- a/tests/component/sections/file/File.spec.tsx +++ b/tests/component/sections/file/File.spec.tsx @@ -171,8 +171,10 @@ describe('File', () => { it('renders the External Tools tab with "Preview" title if only one tool applicable and is a preview tool', () => { cy.customMount( - - + + { .resolves([ExternalToolsMother.createFileQueryTool()]) cy.customMount( - - + + { ]) cy.customMount( - - + + { externalToolsRepository.getExternalTools = cy.stub().resolves([]) cy.customMount( - - + + { fileRepository.getById = cy.stub().resolves(testFile) cy.customMount( - - + + { const guestbook: Guestbook = { @@ -214,7 +214,7 @@ describe('AccessFileMenu', () => { cy.customMount( - + { datasetPersistentId="doi:10.5072/FK2/FILEPAGE" /> - + ) @@ -345,7 +345,7 @@ describe('AccessFileMenu', () => { cy.customMount( - + { }} /> - + ) diff --git a/tests/component/sections/file/file-action-buttons/access-file-menu/FileToolOptions.spec.tsx b/tests/component/sections/file/file-action-buttons/access-file-menu/FileToolOptions.spec.tsx index 3efcb91de..f3834d112 100644 --- a/tests/component/sections/file/file-action-buttons/access-file-menu/FileToolOptions.spec.tsx +++ b/tests/component/sections/file/file-action-buttons/access-file-menu/FileToolOptions.spec.tsx @@ -7,6 +7,7 @@ import { import { ExternalToolsProvider } from '@/shared/contexts/external-tools/ExternalToolsProvider' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { FileExternalToolResolvedMother } from '@tests/component/externalTools/domain/models/FileExternalToolResolvedMother' +import { WithRepositories } from '@tests/component/WithRepositories' const testExternalToolsRepository: ExternalToolsRepository = {} as ExternalToolsRepository const testFileExploreTool = ExternalToolsMother.createFileExploreTool() @@ -23,9 +24,11 @@ describe('FileToolOptions', () => { describe('FileExploreToolsOptions', () => { it('renders the tool options if file explore tools are available and compatible with the type', () => { cy.customMount( - - - + + + + + ) cy.findByText('Query Options').should('not.exist') @@ -36,9 +39,11 @@ describe('FileToolOptions', () => { it('does not render the tool options if there are not applicable tools for the file type', () => { cy.customMount( - - - + + + + + ) cy.findByText('Explore Options').should('not.exist') @@ -49,9 +54,11 @@ describe('FileToolOptions', () => { describe('FileQueryToolsOptions', () => { it('renders the tool options if file query tools are available and compatible with the type', () => { cy.customMount( - - - + + + + + ) cy.findByText('Explore Options').should('not.exist') @@ -62,9 +69,11 @@ describe('FileToolOptions', () => { it('does not render the tool options if there are not applicable tools for the file type', () => { cy.customMount( - - - + + + + + ) cy.findByText('Explore Options').should('not.exist') @@ -75,9 +84,11 @@ describe('FileToolOptions', () => { describe('FileConfigureToolsOptions', () => { it('renders the tool options if file configure tools are available and compatible with the type', () => { cy.customMount( - - - + + + + + ) cy.findByText('Explore Options').should('not.exist') @@ -88,9 +99,11 @@ describe('FileToolOptions', () => { it('does not render the tool options if there are not applicable tools for the file type', () => { cy.customMount( - - - + + + + + ) cy.findByText('Explore Options').should('not.exist') @@ -119,9 +132,11 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('File Explore Tool').should('exist').click() @@ -152,9 +167,11 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('File Explore Tool').should('exist').click() @@ -199,9 +216,11 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('File Explore Tool').should('exist').as('toolButton') @@ -224,9 +243,11 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('File Explore Tool').should('exist').click() @@ -252,9 +273,11 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('File Explore Tool').should('exist').click() @@ -280,9 +303,11 @@ describe('FileToolOptions', () => { }) cy.customMount( - - - + + + + + ) cy.findByText('File Explore Tool').should('exist').click() diff --git a/tests/component/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.spec.tsx b/tests/component/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.spec.tsx index 469c57039..f134bbdb0 100644 --- a/tests/component/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.spec.tsx +++ b/tests/component/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool.spec.tsx @@ -2,9 +2,11 @@ import { ExternalToolsRepository } from '@/externalTools/domain/repositories/Ext import { FileEmbeddedExternalTool } from '@/sections/file/file-embedded-external-tool/FileEmbeddedExternalTool' import { FilePageHelper } from '@/sections/file/FilePageHelper' import { WriteError } from '@iqss/dataverse-client-javascript' +import { ComponentProps } from 'react' import { ExternalToolsMother } from '@tests/component/externalTools/domain/models/ExternalToolsMother' import { FileExternalToolResolvedMother } from '@tests/component/externalTools/domain/models/FileExternalToolResolvedMother' import { FileMother } from '@tests/component/files/domain/models/FileMother' +import { WithRepositories } from '@tests/component/WithRepositories' const externalToolsRepository: ExternalToolsRepository = {} as ExternalToolsRepository // Used for fetching the tool resolved URL @@ -20,6 +22,14 @@ const fileQueryToolResolved = FileExternalToolResolvedMother.create({ toolUrlResolved: 'https://example.com/query-tool?fileId=1' }) +const FileEmbeddedExternalToolWithRepositories = ( + props: ComponentProps +) => ( + + + +) + describe('FileEmbeddedExternalTool', () => { it('renders a single preview tool', () => { externalToolsRepository.getFileExternalToolResolved = cy @@ -27,11 +37,10 @@ describe('FileEmbeddedExternalTool', () => { .resolves(filePreviewToolResolved) cy.customMount( - ) @@ -67,11 +76,10 @@ describe('FileEmbeddedExternalTool', () => { externalToolsRepository.getFileExternalToolResolved = getFileExternalToolResolvedStub cy.customMount( - ) @@ -119,11 +127,10 @@ describe('FileEmbeddedExternalTool', () => { .resolves(filePreviewToolResolved) cy.customMount( - ) @@ -136,11 +143,10 @@ describe('FileEmbeddedExternalTool', () => { .stub() .rejects(new WriteError('Some js dataverse processed error message.')) cy.customMount( - ) @@ -155,11 +161,10 @@ describe('FileEmbeddedExternalTool', () => { .rejects(new Error('Failed to fetch tool URL')) cy.customMount( - ) diff --git a/tests/component/sections/session/SessionProvider.spec.tsx b/tests/component/sections/session/SessionProvider.spec.tsx index cce06c8e7..79b575880 100644 --- a/tests/component/sections/session/SessionProvider.spec.tsx +++ b/tests/component/sections/session/SessionProvider.spec.tsx @@ -1,4 +1,4 @@ -import { Route, Routes } from 'react-router-dom' +import { Route, Routes, useLocation } from 'react-router-dom' import { AuthContext } from 'react-oauth2-code-pkce' import { ReadError } from '@iqss/dataverse-client-javascript' import { UserRepository } from '@/users/domain/repositories/UserRepository' @@ -9,6 +9,7 @@ import { SessionProvider } from '@/sections/session/SessionProvider' import { useSession } from '@/sections/session/SessionContext' +import { WithRepositories } from '@tests/component/WithRepositories' const userRepository: UserRepository = {} as UserRepository const testUser = UserMother.create() @@ -34,6 +35,12 @@ describe('SessionProvider', () => { ) } + const LocationDisplay = () => { + const location = useLocation() + + return

{`${location.pathname}${location.search}`}

+ } + const renderComponent = ({ loginInProgress, withTokenPresent @@ -54,12 +61,23 @@ describe('SessionProvider', () => { error: null, login: () => {} // 👈 deprecated }}> - - }> - } /> - Sign up} /> - - + + + }> + } /> + + + +
Sign up
+ + } + />{' '} +
+
+
) } @@ -166,6 +184,8 @@ describe('SessionProvider', () => { }) cy.findByText('Sign up').should('exist') + cy.findByText(BEARER_TOKEN_IS_VALID_BUT_NOT_LINKED_MESSAGE).should('exist') + cy.findByTestId('location').should('have.text', '/sign-up?validTokenButNotLinkedAccount=true') }) it('should detect any other ReadError instances', () => { diff --git a/tests/component/sections/shared/citation/CitationDownloadButton.spec.tsx b/tests/component/sections/shared/citation/CitationDownloadButton.spec.tsx index 2d143683e..48a3520fd 100644 --- a/tests/component/sections/shared/citation/CitationDownloadButton.spec.tsx +++ b/tests/component/sections/shared/citation/CitationDownloadButton.spec.tsx @@ -1,8 +1,9 @@ import { DatasetRepository } from '@/dataset/domain/repositories/DatasetRepository' import { CitationDownloadButton } from '../../../../../src/sections/shared/citation/citation-download/CitationDownloadButton' import { FormattedCitation } from '@/dataset/domain/models/DatasetCitation' -import { ViewStyledCitationModal } from '@/sections/shared/citation/citation-download/ViewStyledCitationModal' import { WithRepositories } from '@tests/component/WithRepositories' +import { ViewStyledCitationModal } from '@/sections/shared/citation/citation-download/ViewStyledCitationModal' +import i18next from '@/i18n' const datasetRepository: DatasetRepository = {} as DatasetRepository const mockCitation: FormattedCitation = { @@ -12,11 +13,16 @@ const mockCitation: FormattedCitation = { describe('CitationDownloadButton', () => { beforeEach(() => { - // Mock URL.createObjectURL and URL.revokeObjectURL + cy.wrap(i18next.loadNamespaces('files')) + cy.window().then((win) => { - cy.stub(win.URL, 'createObjectURL').returns('mock-url') - cy.stub(win.URL, 'revokeObjectURL') + cy.stub(win.URL, 'createObjectURL').as('createObjectURL').returns('mock-url') + cy.stub(win.URL, 'revokeObjectURL').as('revokeObjectURL') }) + + cy.customMount( + {}} citation={mockCitation} /> + ) }) it('renders the button', () => { @@ -47,10 +53,8 @@ describe('CitationDownloadButton', () => { 'EndNote' ) }) - cy.window().then((win) => { - expect(win.URL['createObjectURL']).to.have.been.called - expect(win.URL['revokeObjectURL']).to.have.been.called - }) + cy.get('@createObjectURL').should('have.been.called') + cy.get('@revokeObjectURL').should('have.been.called') }) it('downloads RIS citation and triggers file download', () => { @@ -72,9 +76,7 @@ describe('CitationDownloadButton', () => { 'RIS' ) }) - cy.window().then((win) => { - expect(win.URL['createObjectURL']).to.have.been.called - }) + cy.get('@createObjectURL').should('have.been.called') }) it('downloads BibTeX citation and creates download link', () => { @@ -97,10 +99,8 @@ describe('CitationDownloadButton', () => { ) }) - cy.window().then((win) => { - expect(win.URL['createObjectURL']).to.have.been.called - expect(win.URL['revokeObjectURL']).to.have.been.called - }) + cy.get('@createObjectURL').should('have.been.called') + cy.get('@revokeObjectURL').should('have.been.called') }) it('verifies correct filename is used for download', () => { @@ -157,15 +157,13 @@ describe('CitationDownloadButton', () => {
) - cy.customMount( - {}} citation={mockCitation} /> - ) + cy.findByRole('button', { name: 'Cite Dataset' }).click() + cy.findByText('View Styled Citation').click() - cy.findByText('Styled Citation').click() + cy.findByRole('dialog').should('exist') cy.findByText('Select a CSL Style').should('exist') cy.findByText(mockCitation.content).should('exist') cy.findByRole('button', { name: /Copy to clipboard icon/ }).should('exist') - cy.findByRole('dialog').should('exist') }) it('closes styled citation modal when close is triggered', () => { @@ -181,7 +179,8 @@ describe('CitationDownloadButton', () => { cy.findByText('View Styled Citation').click() cy.findByRole('dialog').should('exist') - cy.findByRole('button', { name: /close/i }).click() + cy.findByText(mockCitation.content).should('exist') + cy.findByRole('button', { name: 'Cancel' }).click() cy.findByRole('dialog').should('not.exist') }) diff --git a/tests/component/sections/sign-up/SignUp.spec.tsx b/tests/component/sections/sign-up/SignUp.spec.tsx index f359d17f8..2ea92f1dd 100644 --- a/tests/component/sections/sign-up/SignUp.spec.tsx +++ b/tests/component/sections/sign-up/SignUp.spec.tsx @@ -3,6 +3,7 @@ import { SignUp } from '@/sections/sign-up/SignUp' import { UserRepository } from '@/users/domain/repositories/UserRepository' import { AuthContextMother } from '@tests/component/auth/AuthContextMother' import { AuthContext } from 'react-oauth2-code-pkce' +import { WithRepositories } from '@tests/component/WithRepositories' const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository const userRepository: UserRepository = {} as UserRepository @@ -14,24 +15,25 @@ describe('SignUp', () => { it('renders the valid token not linked account form and correct alerts when hasValidTokenButNotLinkedAccount prop is true', () => { cy.customMount( - {}, - logOut: () => {}, - loginInProgress: false, - tokenData: AuthContextMother.createTokenData(), - idTokenData: AuthContextMother.createTokenData(), - error: null, - login: () => {} // 👈 deprecated - }}> - - + + {}, + logOut: () => {}, + loginInProgress: false, + tokenData: AuthContextMother.createTokenData(), + idTokenData: AuthContextMother.createTokenData(), + error: null, + login: () => {} // 👈 deprecated + }}> + + + ) cy.findByTestId('valid-token-not-linked-account-alert-text').should('exist') @@ -44,24 +46,25 @@ describe('SignUp', () => { // For now we are only rendering the form for the case when theres is a valid token but is not linked to any account, but we prepare the test for other cases it('renders the default create account alert when hasValidTokenButNotLinkedAccount prop is false', () => { cy.customMount( - {}, - logOut: () => {}, - loginInProgress: false, - tokenData: AuthContextMother.createTokenData(), - idTokenData: AuthContextMother.createTokenData(), - error: null, - login: () => {} // 👈 deprecated - }}> - - + + {}, + logOut: () => {}, + loginInProgress: false, + tokenData: AuthContextMother.createTokenData(), + idTokenData: AuthContextMother.createTokenData(), + error: null, + login: () => {} // 👈 deprecated + }}> + + + ) cy.findByTestId('default-create-account-alert-text').should('exist') diff --git a/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx b/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx index 23c9ba527..5065cb08b 100644 --- a/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx +++ b/tests/component/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm.spec.tsx @@ -7,6 +7,8 @@ import { JSTermsOfUseMapper } from '@/info/infrastructure/mappers/JSTermsOfUseMa import { ValidTokenNotLinkedAccountForm } from '@/sections/sign-up/valid-token-not-linked-account-form/ValidTokenNotLinkedAccountForm' import { AuthContextMother } from '@tests/component/auth/AuthContextMother' import { TermsOfUseMother } from '@tests/component/info/domain/models/TermsOfUseMother' +import { WithRepositories } from '@tests/component/WithRepositories' +import { ComponentProps } from 'react' const userRepository: UserRepository = {} as UserRepository const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository @@ -19,6 +21,14 @@ const mockFirstName = 'mockFirstName' const mockLastName = 'mockLastName' const mockEmail = 'mockEmail@email.com' +const ValidTokenNotLinkedAccountFormWithRepositories = ( + props: ComponentProps +) => ( + + + +) + describe('ValidTokenNotLinkedAccountForm', () => { beforeEach(() => { cy.viewport(1280, 720) @@ -47,8 +57,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -75,8 +84,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -115,8 +123,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -156,8 +163,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -246,8 +252,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -274,8 +279,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -302,8 +306,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - @@ -341,8 +344,7 @@ describe('ValidTokenNotLinkedAccountForm', () => { error: null, login: () => {} // 👈 deprecated }}> - diff --git a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx index 019e13624..497b46b01 100644 --- a/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx +++ b/tests/e2e-integration/e2e/sections/dataset/Dataset.spec.tsx @@ -1079,7 +1079,8 @@ describe('Dataset', () => { cy.wrap( DatasetHelper.createWithFiles(FileHelper.createMany(3)).then((dataset) => DatasetHelper.publish(dataset.persistentId) - ) + ), + { timeout: 30_000 } ) .its('persistentId') .then((persistentId: string) => { diff --git a/tests/e2e-integration/shared/TestsUtils.ts b/tests/e2e-integration/shared/TestsUtils.ts index b64d10dd4..00f0bffaa 100644 --- a/tests/e2e-integration/shared/TestsUtils.ts +++ b/tests/e2e-integration/shared/TestsUtils.ts @@ -95,7 +95,8 @@ export class TestsUtils { } cy.findByTestId('sign-up-page').should('be.visible') - cy.get('#termsAccepted').should('be.visible').check({ force: true }) + cy.findByTestId('valid-token-not-linked-account-form').should('exist') + cy.findByTestId('termsAcceptedCheckbox').check({ force: true }) cy.findByRole('button', { name: 'Create Account' }).should('be.enabled').click() }) } diff --git a/tests/support/commands.tsx b/tests/support/commands.tsx index 9b989c77d..29162a078 100644 --- a/tests/support/commands.tsx +++ b/tests/support/commands.tsx @@ -53,6 +53,7 @@ import { requireAppConfig } from '@/config' import { ToastContainer } from 'react-toastify' import { ExternalToolsProvider } from '@/shared/contexts/external-tools/ExternalToolsProvider' import { ExternalToolsMockRepository } from '@/stories/shared-mock-repositories/externalTools/ExternalToolsMockRepository' +import { WithRepositories } from '@tests/component/WithRepositories' // Define your custom mount function @@ -74,9 +75,11 @@ Cypress.Commands.add( return cy.mount( - - - + + + + + diff --git a/vite.config.ts b/vite.config.ts index 472516589..9b612ecd2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -42,6 +42,9 @@ export default defineConfig({ } }) ], + optimizeDeps: { + include: ['react-dom/client'] + }, preview: { port: 5173 }, From 732916a0652fd7a1ffe21142195715d7dc98731f Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Thu, 16 Jul 2026 13:08:58 -0400 Subject: [PATCH 11/15] fix: deaccessioned version behavior --- .../ExportMetadataDropdown.tsx | 11 +++++++++- .../ExportMetadataDropdown.spec.tsx | 21 ++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx index 57ebbfbb2..8623f43b7 100644 --- a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx +++ b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx @@ -147,6 +147,15 @@ function isPublishedVersionSummary(summary: DatasetVersionSummaryInfo): boolean return ( summary.versionNumber !== DatasetNonNumericVersion.DRAFT && summary.versionNumber !== DatasetNonNumericVersionSearchParam.DRAFT && - summary.summary !== DatasetVersionSummaryStringValues.versionDeaccessioned + !isDeaccessionedVersionSummary(summary) + ) +} + +function isDeaccessionedVersionSummary(summary: DatasetVersionSummaryInfo): boolean { + return ( + summary.summary === DatasetVersionSummaryStringValues.versionDeaccessioned || + (typeof summary.summary === 'object' && + summary.summary !== null && + 'deaccessioned' in summary.summary) ) } diff --git a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx index abeb0e1cf..e5e17ef2a 100644 --- a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx @@ -6,7 +6,11 @@ import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' import { WithRepositories } from '@tests/component/WithRepositories' import { type ComponentProps } from 'react' import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' -import { DatasetNonNumericVersion } from '@/dataset/domain/models/Dataset' +import { + DatasetNonNumericVersion, + DatasetVersionNumber +} from '@/dataset/domain/models/Dataset' +import { DatasetVersionsSummariesMother } from '@tests/component/dataset/domain/models/DatasetVersionsSummariesMother' const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository const datasetRepository: DatasetRepository = {} as DatasetRepository @@ -203,6 +207,21 @@ describe('ExportMetadataDropdown', () => { cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') }) + it('should render for the latest non-deaccessioned published version', () => { + datasetRepository.getDatasetVersionsSummaries = cy + .stub() + .as('getDatasetVersionsSummaries') + .resolves(DatasetVersionsSummariesMother.createDeaccessioned()) + + mountExportMetadataDropdown({ + datasetVersion: DatasetVersionMother.createReleased({ + number: new DatasetVersionNumber(3, 0) + }) + }) + + cy.findByRole('button', { name: 'Export Metadata' }).should('exist') + }) + it('should not render if dataset version is draft and user cannot update dataset', () => { mountExportMetadataDropdown({ datasetVersion: DatasetVersionMother.createDraft(), From db3a515a88673e1d65e2cb1af1e25d35bf81dc76 Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Thu, 16 Jul 2026 13:26:21 -0400 Subject: [PATCH 12/15] fix: lint error --- .../dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx | 5 +---- .../e2e/sections/collection/CollectionItemsPanel.spec.ts | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx index e5e17ef2a..484236477 100644 --- a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx @@ -6,10 +6,7 @@ import { DatasetNotNumberedVersion } from '@iqss/dataverse-client-javascript' import { WithRepositories } from '@tests/component/WithRepositories' import { type ComponentProps } from 'react' import { DatasetVersionMother } from '@tests/component/dataset/domain/models/DatasetMother' -import { - DatasetNonNumericVersion, - DatasetVersionNumber -} from '@/dataset/domain/models/Dataset' +import { DatasetNonNumericVersion, DatasetVersionNumber } from '@/dataset/domain/models/Dataset' import { DatasetVersionsSummariesMother } from '@tests/component/dataset/domain/models/DatasetVersionsSummariesMother' const dataverseInfoRepository: DataverseInfoRepository = {} as DataverseInfoRepository diff --git a/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts b/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts index 4aa9ebb29..558463764 100644 --- a/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts +++ b/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts @@ -44,8 +44,6 @@ function extractInfoFromInterceptedResponse(interception: Interception) { } describe('Collection Items Panel', () => { - let collectionId: string - beforeEach(() => { TestsUtils.login().then((token) => { cy.wrap(TestsUtils.setup(token)).then(async () => { @@ -55,7 +53,6 @@ describe('Collection Items Panel', () => { const collectionName = 'ItemsTestCollection' const collection = await CollectionHelper.create(`${collectionName}-${Date.now()}`) - collectionId = collection.id // Creates 8 datasets with 1 file each for (const _number of numbersOfDatasetsToCreate) { await DatasetHelper.createWithFileAndTitle( From 0edad69409402f9fdbe2283151cf5859080a06f2 Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Thu, 16 Jul 2026 15:34:41 -0400 Subject: [PATCH 13/15] fix: permission changed to ALL members in collection --- .../dataset-metadata/DatasetMetadata.tsx | 1 - .../ExportMetadataDropdown.tsx | 27 +++++++++---------- .../file/file-metadata/FileMetadata.tsx | 1 - .../ExportMetadataDropdown.spec.tsx | 27 +++++-------------- 4 files changed, 19 insertions(+), 37 deletions(-) diff --git a/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx b/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx index 2c016e65c..0a848b713 100644 --- a/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx +++ b/src/sections/dataset/dataset-metadata/DatasetMetadata.tsx @@ -24,7 +24,6 @@ export function DatasetMetadata({ diff --git a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx index 8623f43b7..eeb26d336 100644 --- a/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx +++ b/src/sections/dataset/dataset-metadata/export-metadata-dropdown/ExportMetadataDropdown.tsx @@ -23,7 +23,6 @@ import { interface ExportMetadataDropdownProps { datasetPersistentId: string datasetVersion: DatasetVersion - canUpdateDataset: boolean anonymizedView: boolean dataverseInfoRepository: DataverseInfoRepository } @@ -31,7 +30,6 @@ interface ExportMetadataDropdownProps { export const ExportMetadataDropdown = ({ datasetPersistentId, datasetVersion, - canUpdateDataset, anonymizedView, dataverseInfoRepository }: ExportMetadataDropdownProps) => { @@ -55,21 +53,18 @@ export const ExportMetadataDropdown = ({ } } - void canExportMetadata( - datasetRepository, - datasetPersistentId, - datasetVersion, - canUpdateDataset - ).then((canExportMetadataResult) => { - if (isMounted) { - setShouldRender(canExportMetadataResult) + void canExportMetadata(datasetRepository, datasetPersistentId, datasetVersion).then( + (canExportMetadataResult) => { + if (isMounted) { + setShouldRender(canExportMetadataResult) + } } - }) + ) return () => { isMounted = false } - }, [datasetRepository, datasetPersistentId, datasetVersion, canUpdateDataset, anonymizedView]) + }, [datasetRepository, datasetPersistentId, datasetVersion, anonymizedView]) if (!shouldRender) return null @@ -108,11 +103,13 @@ export const ExportMetadataDropdown = ({ async function canExportMetadata( datasetRepository: DatasetRepository, datasetId: number | string, - datasetVersion: DatasetVersion, - canUpdateDataset: boolean + datasetVersion: DatasetVersion ): Promise { + // Anyone who can load a draft dataset page already has the backend's implicit permission to + // view it (unauthorized users get a 404 before this component ever renders), so exporting its + // metadata should be available to everyone who can see the draft, not just editors. if (datasetVersion.publishingStatus === DatasetPublishingStatus.DRAFT) { - return canUpdateDataset + return true } if (datasetVersion.publishingStatus !== DatasetPublishingStatus.RELEASED) { diff --git a/src/sections/file/file-metadata/FileMetadata.tsx b/src/sections/file/file-metadata/FileMetadata.tsx index 7e2c079f2..db02616e3 100644 --- a/src/sections/file/file-metadata/FileMetadata.tsx +++ b/src/sections/file/file-metadata/FileMetadata.tsx @@ -45,7 +45,6 @@ export function FileMetadata({ diff --git a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx index 484236477..6cd07b18a 100644 --- a/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx +++ b/tests/component/sections/dataset/dataset-metadata/ExportMetadataDropdown.spec.tsx @@ -69,7 +69,6 @@ describe('ExportMetadataDropdown', () => { { }) it('should render latest published metadata export for a guest user', () => { - mountExportMetadataDropdown({ canUpdateDataset: false }) + mountExportMetadataDropdown() cy.findByRole('button', { name: 'Export Metadata' }).should('exist') }) @@ -124,8 +123,7 @@ describe('ExportMetadataDropdown', () => { }) mountExportMetadataDropdown({ - datasetVersion: DatasetVersionMother.createReleasedWithLatestVersionIsADraft(), - canUpdateDataset: true + datasetVersion: DatasetVersionMother.createReleasedWithLatestVersionIsADraft() }) cy.findByRole('button', { name: 'Export Metadata' }).should('exist') @@ -175,8 +173,7 @@ describe('ExportMetadataDropdown', () => { it('should render and export draft metadata when dataset version is draft', () => { mountExportMetadataDropdown({ - datasetVersion: DatasetVersionMother.createDraft(), - canUpdateDataset: true + datasetVersion: DatasetVersionMother.createDraft() }) cy.findByRole('button', { name: 'Export Metadata' }).click() @@ -219,29 +216,19 @@ describe('ExportMetadataDropdown', () => { cy.findByRole('button', { name: 'Export Metadata' }).should('exist') }) - it('should not render if dataset version is draft and user cannot update dataset', () => { + it('should render if dataset version is draft, since seeing the draft page at all implies permission to export it', () => { mountExportMetadataDropdown({ - datasetVersion: DatasetVersionMother.createDraft(), - canUpdateDataset: false + datasetVersion: DatasetVersionMother.createDraft() }) - cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') + cy.findByRole('button', { name: 'Export Metadata' }).should('exist') }) - it('should not render if dataset is deaccessioned and user cannot update dataset', () => { + it('should not render if dataset is deaccessioned', () => { mountExportMetadataDropdown({ datasetVersion: DatasetVersionMother.createDeaccessioned() }) cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') }) - - it('should not render if dataset is deaccessioned and user can update dataset', () => { - mountExportMetadataDropdown({ - datasetVersion: DatasetVersionMother.createDeaccessioned(), - canUpdateDataset: true - }) - - cy.findByRole('button', { name: 'Export Metadata' }).should('not.exist') - }) }) From 2199535ccac35cfb3df47e47fb06265fe8c95ab0 Mon Sep 17 00:00:00 2001 From: Cheng Shi <91049239+ChengShi-1@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:01:52 -0400 Subject: [PATCH 14/15] Update CHANGELOG.md Co-authored-by: Philip Durbin --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5167090ee..d6df9180c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel ### Changed -- Hide "Export Metadata" on dataset and file pages that are not for the latest released dataset version. +- Hide "Export Metadata" on dataset and file pages that are not for the latest published dataset version. - Show "Export Metadata" on dataset and file pages for draft version. - Avoided prop-drilling for file, guestbook, user and external tool repository, so used context to share repository instances. From ff8a757af821003c139a9a15ba0a7cdbfdbd517d Mon Sep 17 00:00:00 2001 From: Cheng Shi Date: Fri, 17 Jul 2026 11:03:17 -0400 Subject: [PATCH 15/15] fix: remove repeated line Vite already copies and serves the root public/ directory. --- .storybook/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/.storybook/main.ts b/.storybook/main.ts index ab07f18b3..ef54f9991 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -15,7 +15,6 @@ const config: StorybookConfig = { options: {} }, docs: {}, - staticDirs: ['../public'], typescript: { reactDocgen: false }