Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
065fa86
Import from v1
jp-tosca Apr 10, 2026
74a8a1f
Update from js dataverse type
jp-tosca Apr 10, 2026
df5e284
Update from js dataverse type
jp-tosca Apr 10, 2026
0b3babe
Update from js dataverse type
jp-tosca Apr 11, 2026
45640a4
Conflict solving errors fix
jp-tosca Apr 13, 2026
5ea86b2
Upgrade develop
jp-tosca Apr 13, 2026
017c4fa
Merge remote-tracking branch 'origin/develop' into 797-dataset-type-s…
jp-tosca Apr 29, 2026
7a0277a
Changes for allowed dataset types
jp-tosca May 27, 2026
0b52007
Merge remote-tracking branch 'origin/develop' into 797-dataset-type-s…
jp-tosca Jun 4, 2026
350a459
Refactor datasetType handling across repositories and use cases for c…
jp-tosca Jun 4, 2026
33d7126
Fix formatting and improve type handling in various components and tests
jp-tosca Jun 4, 2026
c048697
Update @iqss/dataverse-client-javascript dependency to version 2.1.0-…
jp-tosca Jun 5, 2026
8534f2f
Refactor allowedDatasetTypes handling in JSCollectionMapper to ensure…
jp-tosca Jun 5, 2026
9599c01
Fix type casting for allowedDatasetTypes in JSCollectionMapper
jp-tosca Jun 5, 2026
2b62608
lint
jp-tosca Jun 5, 2026
1e9cf6a
cypress
jp-tosca Jun 8, 2026
52c52b7
cypress
jp-tosca Jun 8, 2026
5704d35
cypress
jp-tosca Jun 8, 2026
0660ba1
cypress
jp-tosca Jun 8, 2026
84e7b12
add GetDatasetReviews use case
ekraffmiller Jun 16, 2026
6397bcb
add DatasetReviews component
ekraffmiller Jun 16, 2026
6d1914e
Branch updade
jp-tosca Jun 16, 2026
b4ec629
update get File use case: don't get filePermissions for anonymous users
ekraffmiller Jun 17, 2026
8a0b376
update all File permissions checks to handle guest (not logged in) users
ekraffmiller Jun 17, 2026
1eb3515
add e2e test for Dataset Review sidebar
ekraffmiller Jun 17, 2026
5de16c4
update Login.spec.tsx to fix flaky test
ekraffmiller Jun 17, 2026
4ab46c8
improve spacing within Dataset Reviews sidebar
ekraffmiller Jun 17, 2026
6468999
add entry in CHANGELOG.md
ekraffmiller Jun 17, 2026
7e39fd4
Fix lint errors and warnings
jp-tosca Jun 18, 2026
3bdc708
Update src/sections/create-dataset/dataset-type-select/DatasetTypeSel…
jp-tosca Jun 18, 2026
a56457d
Update src/sections/create-dataset/dataset-type-select/DatasetTypeSel…
jp-tosca Jun 18, 2026
f728548
Update src/sections/create-dataset/CreateDataset.tsx
jp-tosca Jun 18, 2026
1b9b7ec
Spanish translations
jp-tosca Jun 18, 2026
4291ba0
Removing old "available" types in favor of "allowed"
jp-tosca Jun 18, 2026
12f6ea2
Remove datasetType from IDatasetDetails interface
jp-tosca Jun 22, 2026
bfbb6ed
merge with dataset review sidebar
ekraffmiller Jun 22, 2026
c73e74a
fix CreateDataset.spec.tsx
ekraffmiller Jun 23, 2026
f0718fb
fix component tests
ekraffmiller Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
### Added

- Dataset Templates UI integration, including create/edit flows, previews, and skeleton states.
- Dataset Page: added a sidebar to show dataset reviews

### Changed

Expand Down
160 changes: 159 additions & 1 deletion cypress.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import { defineConfig } from 'cypress'
import vitePreprocessor from 'cypress-vite'
import path from 'path'
import fs from 'node:fs'
import os from 'node:os'
import { execFile } from 'node:child_process'
import { promisify } from 'node:util'

const execFileAsync = promisify(execFile)
const solrCollectionUrl = 'http://localhost:8983/solr/collection1'
const solrCoreAdminUrl = 'http://localhost:8983/solr/admin/cores'
const solrSchemaPath = '/var/solr/data/collection1/conf/schema.xml'

type ExecFileError = Error & {
stderr?: string
stdout?: string
}

export default defineConfig({
video: false,
Expand All @@ -13,8 +27,47 @@ export default defineConfig({
viewportWidth: 1920,
viewportHeight: 1080,
supportFile: 'tests/support/e2e.ts',
setupNodeEvents(on) {
setupNodeEvents(on, config) {
on('file:preprocessor', vitePreprocessor(path.resolve(__dirname, './vite.config.ts')))

on('task', {
async solrSchemaFieldExists(fieldName: string): Promise<boolean> {
const statusCode = await runDockerCommand(config, [
'exec',
getSolrContainerName(config),
'curl',
'-sS',
'-o',
'/tmp/solr-schema-field-response.json',
'-w',
'%{http_code}',
`${solrCollectionUrl}/schema/fields/${encodeURIComponent(fieldName)}`
])

if (statusCode.trim() === '200') {
return true
}

if (statusCode.trim() === '404') {
return false
}

throw new Error(`Unexpected Solr schema field check status for ${fieldName}: ${statusCode}`)
},
async replaceSolrSchemaWithDataverseGeneratedSchema(): Promise<null> {
const generatedSchemaFragment = await getDataverseGeneratedSolrSchemaFragment(config)
const currentSchema = await runDockerCommand(config, [
'exec',
getSolrContainerName(config),
'cat',
solrSchemaPath
])
const mergedSchema = mergeGeneratedSchemaFragment(currentSchema, generatedSchemaFragment)
await copySchemaToSolrContainer(config, mergedSchema)
await reloadSolrCore(config)
return null
}
})
},
defaultCommandTimeout: 10_000 // https://docs.cypress.io/guides/references/configuration#Timeouts
},
Expand Down Expand Up @@ -62,3 +115,108 @@ export default defineConfig({
}
}
})

function getSolrContainerName(config: Cypress.PluginConfigOptions): string {
return (config.env.solrContainerName as string | undefined) ?? 'dev_solr'
}

async function getDataverseGeneratedSolrSchemaFragment(
config: Cypress.PluginConfigOptions
): Promise<string> {
const backendUrl = config.env.backendUrl as string
const response = await fetch(`${backendUrl}/api/v1/admin/index/solr/schema`)

if (!response.ok) {
throw new Error(`Error while getting Dataverse-generated Solr schema: ${response.status}`)
}

return response.text()
}

async function copySchemaToSolrContainer(
config: Cypress.PluginConfigOptions,
schemaXml: string
): Promise<void> {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dataverse-solr-schema-'))
const tempSchemaPath = path.join(tempDir, 'schema.xml')

try {
fs.writeFileSync(tempSchemaPath, schemaXml)
await runDockerCommand(config, [
'cp',
tempSchemaPath,
`${getSolrContainerName(config)}:${solrSchemaPath}`
])
} finally {
fs.rmSync(tempDir, { recursive: true, force: true })
}
}

async function reloadSolrCore(config: Cypress.PluginConfigOptions): Promise<void> {
await runDockerCommand(config, [
'exec',
getSolrContainerName(config),
'curl',
'-sS',
`${solrCoreAdminUrl}?action=RELOAD&core=collection1&wt=json`
])
}

function mergeGeneratedSchemaFragment(
currentSchema: string,
generatedSchemaFragment: string
): string {
const generatedSchemaLines = generatedSchemaFragment.split('\n')
const fieldLines = generatedSchemaLines.filter((line) => line.includes('<field '))
const copyFieldLines = generatedSchemaLines.filter((line) => line.includes('<copyField '))

return replaceSchemaSection(
replaceSchemaSection(
currentSchema,
'<!-- SCHEMA-FIELDS::BEGIN -->',
'<!-- SCHEMA-FIELDS::END -->',
fieldLines
),
'<!-- SCHEMA-COPY-FIELDS::BEGIN -->',
'<!-- SCHEMA-COPY-FIELDS::END -->',
copyFieldLines
)
}

function replaceSchemaSection(
schema: string,
beginMarker: string,
endMarker: string,
replacementLines: string[]
): string {
const beginIndex = schema.indexOf(beginMarker)
const endIndex = schema.indexOf(endMarker)

if (beginIndex === -1 || endIndex === -1 || beginIndex > endIndex) {
throw new Error(`Could not find Solr schema section ${beginMarker}.`)
}

return [
schema.slice(0, beginIndex + beginMarker.length),
'',
replacementLines.join('\n'),
schema.slice(endIndex)
].join('\n')
}

async function runDockerCommand(
_config: Cypress.PluginConfigOptions,
args: string[]
): Promise<string> {
try {
const { stdout } = await execFileAsync('docker', args, { maxBuffer: 10 * 1024 * 1024 })
return stdout
} catch (error) {
const execError = error as ExecFileError
throw new Error(
`Docker command failed: docker ${args.join(' ')}. Reason was: ${
execError.stderr ?? execError.stdout ?? execError.message
}`
)
}
}
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.1.0-alpha.4",
"@iqss/dataverse-client-javascript": "2.2.0-pr457.c6b21a4",
"@iqss/dataverse-design-system": "*",
"@istanbuljs/nyc-config-typescript": "1.0.2",
"@tanstack/react-table": "8.9.2",
Expand Down
7 changes: 7 additions & 0 deletions public/locales/en/createDataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,12 @@
"label": "Dataset Template",
"description": "The dataset template which prepopulates info into the form automatically.",
"helpText": "Changing the template will clear any fields you may have entered data into."
},
"datasetType": {
"label": "Dataset Type",
"description": "The type of dataset you are creating.",
"helpText": "Changing the dataset type will clear any fields you may have entered data into.",
"placeholder": "Select a dataset type",
"toggleMenu": "Toggle dataset types options menu"
}
}
3 changes: 3 additions & 0 deletions public/locales/en/dataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@
},
"defaultGetDownloadCountError": "Something went wrong while getting the dataset download count. Try again later."
},
"reviews": {
"title": "Dataset Reviews"
},
"persistentId": {
"name": "Persistent Identifier",
"description": "The Dataset's unique persistent identifier, either a DOI or Handle"
Expand Down
5 changes: 4 additions & 1 deletion public/locales/en/editDatasetMetadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@
"label": "Host Collection",
"description": "The collection which contains this data."
},
"metadata": "Metadata"
"metadata": "Metadata",
"datasetType": {
"label": "Dataset Type"
}
}
7 changes: 7 additions & 0 deletions public/locales/es/createDataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,12 @@
"label": "Plantilla de dataset",
"description": "La plantilla de dataset que completa información automáticamente en el formulario.",
"helpText": "Cambiar la plantilla borrará cualquier campo en el que hayas ingresado datos."
},
"datasetType": {
"label": "Tipo de dataset",
"description": "El tipo de dataset que estás creando.",
"helpText": "Cambiar el tipo de dataset borrará cualquier campo en el que hayas ingresado datos.",
"placeholder": "Selecciona un tipo de dataset",
"toggleMenu": "Alternar menú de opciones de tipos de dataset"
}
}
3 changes: 3 additions & 0 deletions public/locales/es/dataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@
},
"defaultGetDownloadCountError": "Algo salió mal al obtener el conteo de descargas del dataset. Intenta nuevamente más tarde."
},
"reviews": {
"title": "Reseñas del dataset"
},
"persistentId": {
"name": "Identificador persistente",
"description": "El identificador persistente único del Dataset, ya sea un DOI o Handle"
Expand Down
5 changes: 4 additions & 1 deletion public/locales/es/editDatasetMetadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@
"label": "Colección anfitriona",
"description": "La colección que contiene estos datos."
},
"metadata": "Metadatos"
"metadata": "Metadatos",
"datasetType": {
"label": "Tipo de dataset"
}
}
2 changes: 2 additions & 0 deletions src/collection/domain/models/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { UpwardHierarchyNode } from '../../../shared/hierarchy/domain/models/Upw
import { CollectionContact } from './CollectionContact'
import { CollectionType } from './CollectionType'
import { CollectionInputLevel } from './CollectionInputLevel'
import { DatasetType } from '@/dataset/domain/models/DatasetType'

export interface Collection {
id: string
Expand All @@ -16,4 +17,5 @@ export interface Collection {
isMetadataBlockRoot: boolean
isFacetRoot: boolean
childCount: number
allowedDatasetTypes?: DatasetType[]
}
5 changes: 4 additions & 1 deletion src/collection/infrastructure/mappers/JSCollectionMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
UpwardHierarchyNode
} from '../../../shared/hierarchy/domain/models/UpwardHierarchyNode'
import { JSUpwardHierarchyNodeMapper } from '../../../shared/hierarchy/infrastructure/mappers/JSUpwardHierarchyNodeMapper'
import { DatasetType } from '@/dataset/domain/models/DatasetType'

export class JSCollectionMapper {
static toCollection(jsCollection: JSCollection): Collection {
Expand All @@ -27,7 +28,9 @@ export class JSCollectionMapper {
contacts: jsCollection.contacts ?? [],
isMetadataBlockRoot: jsCollection.isMetadataBlockRoot,
isFacetRoot: jsCollection.isFacetRoot,
childCount: jsCollection.childCount
childCount: jsCollection.childCount,
allowedDatasetTypes: (jsCollection as unknown as Record<string, unknown>)
.allowedDatasetTypes as DatasetType[] | undefined
}
}

Expand Down
66 changes: 66 additions & 0 deletions src/dataset/domain/hooks/useGetAvailableDatasetTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useCallback, useEffect, useState } from 'react'
import { ReadError } from '@iqss/dataverse-client-javascript'
import { JSDataverseReadErrorHandler } from '@/shared/helpers/JSDataverseReadErrorHandler'
import { DatasetRepository } from '../repositories/DatasetRepository'
import { getAvailableDatasetTypes } from '../useCases/getAvailableDatasetTypes'
import { DatasetType } from '../models/DatasetType'
import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'

interface useGetAvailableDatasetTypesProps {
datasetRepository: DatasetRepository
collectionRepository?: CollectionRepository
collectionId?: string
autoFetch?: boolean
}

export const useGetAvailableDatasetTypes = ({
datasetRepository,
collectionRepository,
collectionId,
autoFetch = true
}: useGetAvailableDatasetTypesProps) => {
const [datasetTypes, setDatasetTypes] = useState<DatasetType[]>([])
const [isLoadingDatasetTypes, setIsLoadingDatasetTypes] = useState<boolean>(autoFetch)
const [errorGetDatasetTypes, setErrorGetDatasetTypes] = useState<string | null>(null)

const fetchDatasetTypes = useCallback(async () => {
setIsLoadingDatasetTypes(true)
setErrorGetDatasetTypes(null)

try {
const response =
collectionRepository && collectionId
? (await collectionRepository.getById(collectionId)).allowedDatasetTypes ?? []
: await getAvailableDatasetTypes(datasetRepository)

setDatasetTypes(response)

setDatasetTypes(response)
} catch (err) {
if (err instanceof ReadError) {
const error = new JSDataverseReadErrorHandler(err)
const formattedError =
error.getReasonWithoutStatusCode() ?? /* istanbul ignore next */ error.getErrorMessage()

setErrorGetDatasetTypes(formattedError)
} else {
setErrorGetDatasetTypes('Something went wrong getting the dataset types. Try again later.')
}
} finally {
setIsLoadingDatasetTypes(false)
}
}, [datasetRepository, collectionRepository, collectionId])

useEffect(() => {
if (autoFetch) {
void fetchDatasetTypes()
}
}, [autoFetch, fetchDatasetTypes])

return {
datasetTypes,
isLoadingDatasetTypes,
errorGetDatasetTypes,
fetchDatasetTypes
}
}
Loading
Loading