, ,
, , , , , , , "
+ },
+ "contacts": {
+ "label": "Email",
+ "description": "The email address(es) of the contact(s) for the collection.",
+ "required": "Email is required",
+ "invalid": "Email is not a valid email"
+ },
+ "metadataFields": {
+ "sectionLabel": "Metadata Fields",
+ "helperText": "Choose the metadata fields to use in dataset templates and when adding a dataset to this collection.",
+ "useMetadataFieldsFrom": "Use metadata fields from",
+ "inputLevelsTable": {
+ "hideTableAriaLabel": "Hide input levels table",
+ "requiredByDataverse": "Required by Dataverse",
+ "hidden": "Hidden",
+ "optional": "Optional",
+ "required": "Required",
+ "conditionallyRequired": "Conditionally Required"
+ }
+ },
+ "browseSearchFacets": {
+ "label": "Browse/Search Facets",
+ "helperText": "Choose and order the metadata fields to use as facets when browsing this collection.",
+ "useBrowseSearchFacetsFrom": "Use browse/search facets from",
+ "selectedFacets": "Selected",
+ "invalid": {
+ "minLength": "At least one facet must be selected."
+ }
+ }
+ },
+ "confirmResetModal": {
+ "title": "Reset Modifications",
+ "warning": "Are you sure you want to reset the selected metadata fields? If you do this, any customizations (hidden, required, optional) you have done will no longer appear.",
+ "continue": "Continue",
+ "cancel": "Cancel"
+ },
+ "submitStatus": {
+ "createSuccess": "Collection created successfully.",
+ "editSuccess": "Collection updated successfully."
+ },
+ "saveButton": {
+ "createMode": "Create Collection",
+ "editMode": "Save Changes"
+ },
+ "cancelButton": "Cancel"
}
}
diff --git a/src/collection/domain/models/Collection.ts b/src/collection/domain/models/Collection.ts
index 885c0a13a..dd7159ea2 100644
--- a/src/collection/domain/models/Collection.ts
+++ b/src/collection/domain/models/Collection.ts
@@ -1,4 +1,7 @@
import { UpwardHierarchyNode } from '../../../shared/hierarchy/domain/models/UpwardHierarchyNode'
+import { CollectionContact } from './CollectionContact'
+import { CollectionType } from './CollectionType'
+import { CollectionInputLevel } from './CollectionInputLevel'
export interface Collection {
id: string
@@ -8,10 +11,8 @@ export interface Collection {
description?: string
affiliation?: string
inputLevels?: CollectionInputLevel[]
-}
-
-export interface CollectionInputLevel {
- datasetFieldName: string
- include: boolean
- required: boolean
+ type: CollectionType
+ contacts: CollectionContact[]
+ isMetadataBlockRoot: boolean
+ isFacetRoot: boolean
}
diff --git a/src/collection/domain/models/CollectionContact.ts b/src/collection/domain/models/CollectionContact.ts
new file mode 100644
index 000000000..4264b9c07
--- /dev/null
+++ b/src/collection/domain/models/CollectionContact.ts
@@ -0,0 +1,4 @@
+export interface CollectionContact {
+ email: string
+ displayOrder: number
+}
diff --git a/src/collection/domain/models/CollectionInputLevel.ts b/src/collection/domain/models/CollectionInputLevel.ts
new file mode 100644
index 000000000..84cb6f96c
--- /dev/null
+++ b/src/collection/domain/models/CollectionInputLevel.ts
@@ -0,0 +1,5 @@
+export interface CollectionInputLevel {
+ datasetFieldName: string
+ include: boolean
+ required: boolean
+}
diff --git a/src/collection/domain/models/CollectionType.ts b/src/collection/domain/models/CollectionType.ts
new file mode 100644
index 000000000..2b5878958
--- /dev/null
+++ b/src/collection/domain/models/CollectionType.ts
@@ -0,0 +1,11 @@
+export enum CollectionType {
+ RESEARCHERS = 'RESEARCHERS',
+ RESEARCH_PROJECTS = 'RESEARCH_PROJECTS',
+ JOURNALS = 'JOURNALS',
+ ORGANIZATIONS_INSTITUTIONS = 'ORGANIZATIONS_INSTITUTIONS',
+ TEACHING_COURSES = 'TEACHING_COURSES',
+ UNCATEGORIZED = 'UNCATEGORIZED',
+ LABORATORY = 'LABORATORY',
+ RESEARCH_GROUP = 'RESEARCH_GROUP',
+ DEPARTMENT = 'DEPARTMENT'
+}
diff --git a/src/collection/domain/repositories/CollectionRepository.ts b/src/collection/domain/repositories/CollectionRepository.ts
index d4a8567a4..a710c61a8 100644
--- a/src/collection/domain/repositories/CollectionRepository.ts
+++ b/src/collection/domain/repositories/CollectionRepository.ts
@@ -17,4 +17,5 @@ export interface CollectionRepository {
paginationInfo: CollectionItemsPaginationInfo,
searchCriteria?: CollectionSearchCriteria
): Promise
+ edit(collectionIdOrAlias: string, updatedCollection: CollectionDTO): Promise
}
diff --git a/src/collection/domain/useCases/DTOs/CollectionDTO.ts b/src/collection/domain/useCases/DTOs/CollectionDTO.ts
index a88649ef4..3a2d076e3 100644
--- a/src/collection/domain/useCases/DTOs/CollectionDTO.ts
+++ b/src/collection/domain/useCases/DTOs/CollectionDTO.ts
@@ -1,3 +1,5 @@
+import { CollectionType } from '../../models/CollectionType'
+
export interface CollectionDTO {
alias: string
name: string
@@ -10,18 +12,6 @@ export interface CollectionDTO {
inputLevels?: CollectionInputLevelDTO[]
}
-export enum CollectionType {
- RESEARCHERS = 'RESEARCHERS',
- RESEARCH_PROJECTS = 'RESEARCH_PROJECTS',
- JOURNALS = 'JOURNALS',
- ORGANIZATIONS_INSTITUTIONS = 'ORGANIZATIONS_INSTITUTIONS',
- TEACHING_COURSES = 'TEACHING_COURSES',
- UNCATEGORIZED = 'UNCATEGORIZED',
- LABORATORY = 'LABORATORY',
- RESEARCH_GROUP = 'RESEARCH_GROUP',
- DEPARTMENT = 'DEPARTMENT'
-}
-
export interface CollectionInputLevelDTO {
datasetFieldName: string
include: boolean
diff --git a/src/collection/domain/useCases/editCollection.ts b/src/collection/domain/useCases/editCollection.ts
new file mode 100644
index 000000000..30385ba87
--- /dev/null
+++ b/src/collection/domain/useCases/editCollection.ts
@@ -0,0 +1,13 @@
+import { WriteError } from '@iqss/dataverse-client-javascript'
+import { CollectionRepository } from '../repositories/CollectionRepository'
+import { CollectionDTO } from './DTOs/CollectionDTO'
+
+export async function editCollection(
+ collectionRepository: CollectionRepository,
+ updatedCollection: CollectionDTO,
+ collectionId: string
+): Promise {
+ return collectionRepository.edit(collectionId, updatedCollection).catch((error: WriteError) => {
+ throw error
+ })
+}
diff --git a/src/collection/infrastructure/mappers/JSCollectionMapper.ts b/src/collection/infrastructure/mappers/JSCollectionMapper.ts
index c7cf1008d..1d7312c0c 100644
--- a/src/collection/infrastructure/mappers/JSCollectionMapper.ts
+++ b/src/collection/infrastructure/mappers/JSCollectionMapper.ts
@@ -22,7 +22,11 @@ export class JSCollectionMapper {
jsCollection.alias,
jsCollection.isPartOf
),
- inputLevels: jsCollection.inputLevels
+ inputLevels: jsCollection.inputLevels,
+ type: jsCollection.type,
+ contacts: jsCollection.contacts ?? [],
+ isMetadataBlockRoot: jsCollection.isMetadataBlockRoot,
+ isFacetRoot: jsCollection.isFacetRoot
}
}
diff --git a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts
index 996b6750a..c7851292f 100644
--- a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts
+++ b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts
@@ -6,7 +6,8 @@ import {
getCollectionFacets,
getCollectionUserPermissions,
getCollectionItems,
- publishCollection
+ publishCollection,
+ updateCollection
} from '@iqss/dataverse-client-javascript'
import { JSCollectionMapper } from '../mappers/JSCollectionMapper'
import { CollectionDTO } from '../../domain/useCases/DTOs/CollectionDTO'
@@ -61,4 +62,8 @@ export class CollectionJSDataverseRepository implements CollectionRepository {
publish(collectionIdOrAlias: number | string): Promise {
return publishCollection.execute(collectionIdOrAlias)
}
+
+ edit(collectionIdOrAlias: string, updatedCollection: CollectionDTO): Promise {
+ return updateCollection.execute(collectionIdOrAlias, updatedCollection)
+ }
}
diff --git a/src/files/domain/models/FilePreview.ts b/src/files/domain/models/FilePreview.ts
index 19e8426d9..40d89d45b 100644
--- a/src/files/domain/models/FilePreview.ts
+++ b/src/files/domain/models/FilePreview.ts
@@ -15,7 +15,7 @@ export interface FilePreview {
ingest: FileIngest
metadata: FileMetadata
permissions: FilePermissions
- datasetVersionNumber?: DatasetVersionNumber
+ datasetVersionNumber: DatasetVersionNumber
releaseOrCreateDate?: Date
someDatasetVersionHasBeenReleased?: boolean
datasetPersistentId?: string
diff --git a/src/router/routes.tsx b/src/router/routes.tsx
index 462fe480c..7fd657d29 100644
--- a/src/router/routes.tsx
+++ b/src/router/routes.tsx
@@ -38,6 +38,12 @@ const CreateCollectionPage = lazy(() =>
)
)
+const EditCollectionPage = lazy(() =>
+ import('../sections/edit-collection/EditCollectionFactory').then(({ EditCollectionFactory }) => ({
+ default: () => EditCollectionFactory.create()
+ }))
+)
+
const CreateDatasetPage = lazy(() =>
import('../sections/create-dataset/CreateDatasetFactory').then(({ CreateDatasetFactory }) => ({
default: () => CreateDatasetFactory.create()
@@ -130,6 +136,15 @@ export const routes: RouteObject[] = [
),
errorElement:
},
+ {
+ path: Route.EDIT_COLLECTION,
+ element: (
+ }>
+
+
+ ),
+ errorElement:
+ },
{
path: Route.CREATE_DATASET,
element: (
diff --git a/src/sections/Route.enum.ts b/src/sections/Route.enum.ts
index 93a88ee49..742fd12f9 100644
--- a/src/sections/Route.enum.ts
+++ b/src/sections/Route.enum.ts
@@ -10,15 +10,17 @@ export enum Route {
FILES = '/files',
COLLECTIONS_BASE = '/collections',
COLLECTIONS = '/collections/:collectionId',
- CREATE_COLLECTION = '/collections/:ownerCollectionId/create',
+ CREATE_COLLECTION = '/collections/:parentCollectionId/create',
+ EDIT_COLLECTION = '/collections/:collectionId/edit',
ACCOUNT = '/account'
}
export const RouteWithParams = {
COLLECTIONS: (collectionId?: string) =>
collectionId ? `/collections/${collectionId}` : Route.COLLECTIONS_BASE,
- CREATE_COLLECTION: (ownerCollectionId: string) => `/collections/${ownerCollectionId}/create`,
- CREATE_DATASET: (collectionId: string) => `/datasets/${collectionId}/create`
+ CREATE_COLLECTION: (parentCollectionId: string) => `/collections/${parentCollectionId}/create`,
+ CREATE_DATASET: (collectionId: string) => `/datasets/${collectionId}/create`,
+ EDIT_COLLECTION: (collectionId: string) => `/collections/${collectionId}/edit`
}
export enum QueryParamKey {
diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx
index f90964f0b..4d566f19b 100644
--- a/src/sections/collection/Collection.tsx
+++ b/src/sections/collection/Collection.tsx
@@ -1,9 +1,9 @@
import { useTranslation } from 'react-i18next'
-import { Alert, Col, Row } from '@iqss/dataverse-design-system'
+import { Alert, ButtonGroup, Col, Row } from '@iqss/dataverse-design-system'
+
import { CollectionRepository } from '../../collection/domain/repositories/CollectionRepository'
import { useCollection } from './useCollection'
import { useScrollTop } from '../../shared/hooks/useScrollTop'
-import { useSession } from '../session/SessionContext'
import { useGetCollectionUserPermissions } from '../../shared/hooks/useGetCollectionUserPermissions'
import { type UseCollectionQueryParamsReturnType } from './useGetCollectionQueryParams'
import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator'
@@ -14,6 +14,7 @@ import { CollectionSkeleton } from './CollectionSkeleton'
import { PageNotFound } from '../page-not-found/PageNotFound'
import { CreatedAlert } from './CreatedAlert'
import { PublishCollectionButton } from './publish-collection/PublishCollectionButton'
+import { EditCollectionDropdown } from './edit-collection-dropdown/EditCollectionDropdown'
import styles from './Collection.module.scss'
interface CollectionProps {
@@ -21,6 +22,7 @@ interface CollectionProps {
collectionIdFromParams: string | undefined
created: boolean
published: boolean
+ edited?: boolean
collectionQueryParams: UseCollectionQueryParamsReturnType
infiniteScrollEnabled?: boolean
}
@@ -30,11 +32,12 @@ export function Collection({
collectionRepository,
created,
published,
+ edited,
collectionQueryParams
}: CollectionProps) {
useTranslation('collection')
+ const { t } = useTranslation('collection')
useScrollTop()
- const { user } = useSession()
const { collection, isLoading } = useCollection(
collectionRepository,
collectionIdFromParams,
@@ -46,11 +49,13 @@ export function Collection({
})
const canUserAddCollection = Boolean(collectionUserPermissions?.canAddCollection)
+ const canUserEditCollection = Boolean(collectionUserPermissions?.canEditCollection)
const canUserAddDataset = Boolean(collectionUserPermissions?.canAddDataset)
- const canUserPublishCollection = user && Boolean(collectionUserPermissions?.canPublishCollection)
+ const canUserPublishCollection = Boolean(collectionUserPermissions?.canPublishCollection)
- const showAddDataActions = Boolean(user && (canUserAddCollection || canUserAddDataset))
- const { t } = useTranslation('collection')
+ const showAddDataActions = canUserAddCollection || canUserAddDataset
+ const showPublishButton = !collection?.isReleased && canUserPublishCollection
+ const showEditButton = canUserEditCollection
if (!isLoading && !collection) {
return
@@ -66,17 +71,27 @@ export function Collection({
{created && }
+ {edited && (
+
+ {t('editedAlert')}
+
+ )}
{published && (
{t('publishedAlert')}
)}
- {!collection.isReleased && canUserPublishCollection && (
+ {(showPublishButton || showEditButton) && (
-
+
+ {showPublishButton && (
+
+ )}
+ {showEditButton && }
+
)}
diff --git a/src/sections/collection/CollectionFactory.tsx b/src/sections/collection/CollectionFactory.tsx
index d06056eb9..eaf28195b 100644
--- a/src/sections/collection/CollectionFactory.tsx
+++ b/src/sections/collection/CollectionFactory.tsx
@@ -16,9 +16,12 @@ function CollectionWithSearchParams() {
const collectionQueryParams = useGetCollectionQueryParams()
const { collectionId } = useParams<{ collectionId: string }>()
const location = useLocation()
- const state = location.state as { published: boolean; created: boolean } | undefined
+ const state = location.state as
+ | { published: boolean; created: boolean; edited: boolean }
+ | undefined
const created = state?.created ?? false
const published = state?.published ?? false
+ const edited = state?.edited ?? false
return (
)
diff --git a/src/sections/collection/CollectionHelper.ts b/src/sections/collection/CollectionHelper.ts
index d16273f05..3727e9d61 100644
--- a/src/sections/collection/CollectionHelper.ts
+++ b/src/sections/collection/CollectionHelper.ts
@@ -1,5 +1,7 @@
-import { CollectionItemType } from '../../collection/domain/models/CollectionItemType'
+import { Collection } from '@/collection/domain/models/Collection'
+import { CollectionItemType } from '@/collection/domain/models/CollectionItemType'
import { QueryParamKey } from '../Route.enum'
+import { UpwardHierarchyNode } from '@/shared/hierarchy/domain/models/UpwardHierarchyNode'
export class CollectionHelper {
static defineCollectionQueryParams(searchParams: URLSearchParams) {
@@ -22,4 +24,14 @@ export class CollectionHelper {
return { pageQuery, searchQuery, typesQuery }
}
+
+ static isRootCollection(collectionHierarchy: Collection['hierarchy']) {
+ return !collectionHierarchy.parent
+ }
+
+ static getParentCollection(
+ collectionHierarchy: Collection['hierarchy']
+ ): UpwardHierarchyNode | undefined {
+ return collectionHierarchy.parent
+ }
}
diff --git a/src/sections/collection/edit-collection-dropdown/EditCollectionDropdown.module.scss b/src/sections/collection/edit-collection-dropdown/EditCollectionDropdown.module.scss
new file mode 100644
index 000000000..64eef7e61
--- /dev/null
+++ b/src/sections/collection/edit-collection-dropdown/EditCollectionDropdown.module.scss
@@ -0,0 +1,50 @@
+@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
+
+.dropdown-icon {
+ margin-right: 0.3rem;
+ margin-bottom: 0.2rem;
+}
+
+.dropdown-header {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ min-width: 250px;
+ color: unset;
+ white-space: wrap;
+
+ @media screen and (min-width: 768px) {
+ min-width: 350px;
+ white-space: nowrap;
+ }
+
+ .collection-alias,
+ .collection-name {
+ margin: 0;
+ font-size: 14px;
+ }
+
+ .collection-name {
+ font-weight: 600;
+
+ span {
+ color: $dv-subtext-color;
+ font-weight: 400;
+ }
+ }
+
+ .collection-alias {
+ color: $dv-subtext-color;
+ }
+
+ .collection-icon {
+ height: fit-content;
+ color: $dv-collection-border-color;
+
+ > span {
+ margin-right: 0;
+ font-size: 40px;
+ line-height: 1;
+ }
+ }
+}
diff --git a/src/sections/collection/edit-collection-dropdown/EditCollectionDropdown.tsx b/src/sections/collection/edit-collection-dropdown/EditCollectionDropdown.tsx
new file mode 100644
index 000000000..414918c34
--- /dev/null
+++ b/src/sections/collection/edit-collection-dropdown/EditCollectionDropdown.tsx
@@ -0,0 +1,53 @@
+import { useNavigate } from 'react-router-dom'
+import { useTranslation } from 'react-i18next'
+import {
+ DropdownButton,
+ DropdownButtonItem,
+ DropdownHeader,
+ DropdownSeparator,
+ Icon,
+ IconName
+} from '@iqss/dataverse-design-system'
+import { PencilFill } from 'react-bootstrap-icons'
+import { Collection } from '@/collection/domain/models/Collection'
+import { RouteWithParams } from '@/sections/Route.enum'
+import styles from './EditCollectionDropdown.module.scss'
+
+interface EditCollectionDropdownProps {
+ collection: Collection
+}
+
+export const EditCollectionDropdown = ({ collection }: EditCollectionDropdownProps) => {
+ const { t } = useTranslation('collection')
+ const navigate = useNavigate()
+
+ const onClickEditGeneralInformation = () => {
+ navigate(RouteWithParams.EDIT_COLLECTION(collection.id))
+ }
+
+ return (
+ }>
+
+
+
+
+
+
+ {collection.name}{' '}
+ {collection.affiliation ? ({collection.affiliation}) : null}
+
+
{collection.id}
+
+
+
+
+ {t('editCollection.generalInfo')}
+
+
+ )
+}
diff --git a/src/sections/collection/publish-collection/PublishCollectionModal.tsx b/src/sections/collection/publish-collection/PublishCollectionModal.tsx
index 260ea6b22..dfbff87d2 100644
--- a/src/sections/collection/publish-collection/PublishCollectionModal.tsx
+++ b/src/sections/collection/publish-collection/PublishCollectionModal.tsx
@@ -47,9 +47,7 @@ export function PublishCollectionModal({
{tCollection('publish.question')}
{submissionStatus === SubmissionStatus.Errored && (
-
- `${tCollection('publish.error')} ${publishError ? publishError : ''}`
-
+ {`${tCollection('publish.error')} ${publishError}`}
)}
diff --git a/src/sections/collection/publish-collection/usePublishCollection.tsx b/src/sections/collection/publish-collection/usePublishCollection.tsx
index 2a58b7a39..a514e9983 100644
--- a/src/sections/collection/publish-collection/usePublishCollection.tsx
+++ b/src/sections/collection/publish-collection/usePublishCollection.tsx
@@ -40,7 +40,10 @@ export function usePublishCollection(
return
})
.catch((err) => {
- const errorMessage = err instanceof Error && err.message ? err.message : 'Unknown Error' // TODO: i18n
+ const errorMessage =
+ err instanceof Error && err.message
+ ? err.message
+ : 'Something went wrong while trying to publish the collection. Please try again later.'
setPublishError(errorMessage)
setSubmissionStatus(SubmissionStatus.Errored)
diff --git a/src/sections/create-collection/CreateCollection.tsx b/src/sections/create-collection/CreateCollection.tsx
index 341343ef1..d70256ded 100644
--- a/src/sections/create-collection/CreateCollection.tsx
+++ b/src/sections/create-collection/CreateCollection.tsx
@@ -1,45 +1,28 @@
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { Alert } from '@iqss/dataverse-design-system'
-import { useDeepCompareMemo } from 'use-deep-compare'
-import { useCollection } from '../collection/useCollection'
-import { useGetCollectionMetadataBlocksInfo } from './useGetCollectionMetadataBlocksInfo'
-import { useGetAllMetadataBlocksInfo } from './useGetAllMetadataBlocksInfo'
-import { CollectionRepository } from '../../collection/domain/repositories/CollectionRepository'
-import { MetadataBlockInfoRepository } from '../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { useCollection } from '@/sections/collection/useCollection'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
import { useLoading } from '../loading/LoadingContext'
import { useSession } from '../session/SessionContext'
-import { CollectionFormHelper } from './collection-form/CollectionFormHelper'
-import {
- CollectionForm,
- CollectionFormData,
- CollectionFormFacet,
- CollectionFormMetadataBlocks,
- FACET_IDS_FIELD,
- FormattedCollectionInputLevels,
- FormattedCollectionInputLevelsWithoutParentBlockName,
- INPUT_LEVELS_GROUPER,
- METADATA_BLOCKS_NAMES_GROUPER,
- USE_FACETS_FROM_PARENT,
- USE_FIELDS_FROM_PARENT
-} from './collection-form/CollectionForm'
+import { useGetCollectionUserPermissions } from '@/shared/hooks/useGetCollectionUserPermissions'
+import { User } from '@/users/domain/models/User'
import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator'
import { SeparationLine } from '../shared/layout/SeparationLine/SeparationLine'
import { RequiredFieldText } from '../shared/form/RequiredFieldText/RequiredFieldText'
import { PageNotFound } from '../page-not-found/PageNotFound'
import { CreateCollectionSkeleton } from './CreateCollectionSkeleton'
-import { useGetCollectionFacets } from './useGetCollectionFacets'
-import { useGetAllFacetableMetadataFields } from './useGetAllFacetableMetadataFields'
-import { useGetCollectionUserPermissions } from '../../shared/hooks/useGetCollectionUserPermissions'
+import { EditCreateCollectionForm } from '../shared/form/EditCreateCollectionForm/EditCreateCollectionForm'
interface CreateCollectionProps {
- ownerCollectionId: string
+ parentCollectionId: string
collectionRepository: CollectionRepository
metadataBlockInfoRepository: MetadataBlockInfoRepository
}
export function CreateCollection({
- ownerCollectionId,
+ parentCollectionId,
collectionRepository,
metadataBlockInfoRepository
}: CreateCollectionProps) {
@@ -49,7 +32,7 @@ export function CreateCollection({
const { collection, isLoading: isLoadingCollection } = useCollection(
collectionRepository,
- ownerCollectionId
+ parentCollectionId
)
const {
@@ -57,100 +40,13 @@ export function CreateCollection({
isLoading: isLoadingCollectionUserPermissions,
error: collectionPermissionsError
} = useGetCollectionUserPermissions({
- collectionIdOrAlias: ownerCollectionId,
+ collectionIdOrAlias: parentCollectionId,
collectionRepository: collectionRepository
})
const canUserAddCollection = Boolean(collectionUserPermissions?.canAddCollection)
- // TODO:ME In edit mode, collection id should not be from the collection owner but from the collection being edited, but this can perhaps be differentiated by page.
- const {
- metadataBlocksInfo,
- isLoading: isLoadingMetadataBlocksInfo,
- error: metadataBlockInfoError
- } = useGetCollectionMetadataBlocksInfo({
- collectionId: ownerCollectionId,
- metadataBlockInfoRepository
- })
-
- const {
- allMetadataBlocksInfo,
- isLoading: isLoadingAllMetadataBlocksInfo,
- error: allMetadataBlocksInfoError
- } = useGetAllMetadataBlocksInfo({ metadataBlockInfoRepository })
-
- const {
- collectionFacets,
- isLoading: isLoadingCollectionFacets,
- error: collectionFacetsError
- } = useGetCollectionFacets({
- collectionId: ownerCollectionId,
- collectionRepository
- })
-
- const {
- facetableMetadataFields,
- isLoading: isLoadingFacetableMetadataFields,
- error: facetableMetadataFieldsError
- } = useGetAllFacetableMetadataFields({
- metadataBlockInfoRepository
- })
-
- const baseInputLevels: FormattedCollectionInputLevels = useDeepCompareMemo(() => {
- return CollectionFormHelper.defineBaseInputLevels(allMetadataBlocksInfo)
- }, [allMetadataBlocksInfo])
-
- const formattedCollectionInputLevels: FormattedCollectionInputLevelsWithoutParentBlockName =
- useDeepCompareMemo(() => {
- return CollectionFormHelper.formatCollectiontInputLevels(collection?.inputLevels)
- }, [collection?.inputLevels])
-
- const mergedInputLevels = useDeepCompareMemo(() => {
- return CollectionFormHelper.mergeBaseAndDefaultInputLevels(
- baseInputLevels,
- formattedCollectionInputLevels
- )
- }, [baseInputLevels, formattedCollectionInputLevels])
-
- const baseBlockNames = useDeepCompareMemo(() => {
- return allMetadataBlocksInfo.reduce((acc, block) => {
- acc[block.name as keyof CollectionFormMetadataBlocks] = false
- return acc
- }, {} as CollectionFormMetadataBlocks)
- }, [allMetadataBlocksInfo])
-
- const defaultBlocksNames = useDeepCompareMemo(
- () =>
- metadataBlocksInfo
- .map((block) => block.name)
- .reduce((acc, blockName) => {
- acc[blockName as keyof CollectionFormMetadataBlocks] = true
- return acc
- }, baseBlockNames),
- [metadataBlocksInfo, baseBlockNames]
- )
-
- const defaultCollectionFacets: CollectionFormFacet[] = collectionFacets.map((facet) => ({
- id: facet.name,
- value: facet.name,
- label: facet.displayName
- }))
-
- const isLoadingData =
- isLoadingCollection ||
- isLoadingMetadataBlocksInfo ||
- isLoadingAllMetadataBlocksInfo ||
- isLoadingCollectionFacets ||
- isLoadingFacetableMetadataFields ||
- isLoadingCollectionUserPermissions
-
- const dataLoadingErrors = [
- collectionPermissionsError,
- metadataBlockInfoError,
- allMetadataBlocksInfoError,
- collectionFacetsError,
- facetableMetadataFieldsError
- ]
+ const isLoadingData = isLoadingCollection || isLoadingCollectionUserPermissions
useEffect(() => {
if (!isLoadingData) {
@@ -168,7 +64,7 @@ export function CreateCollection({
if (collectionUserPermissions && !canUserAddCollection) {
return (
-
+
{t('notAllowedToCreateCollection')}
@@ -176,36 +72,14 @@ export function CreateCollection({
)
}
- if (dataLoadingErrors.some((error) => error !== null)) {
+ if (collectionPermissionsError) {
return (
- <>
- {dataLoadingErrors
- .filter((err) => err !== null)
- .map((error, index) => (
-
- {error}
-
- ))}
- >
+
+ {collectionPermissionsError}
+
)
}
- const formDefaultValues: CollectionFormData = {
- hostCollection: collection.name,
- name: user?.displayName ? `${user?.displayName} Collection` : '',
- alias: '',
- type: '',
- contacts: [{ value: user?.email ?? '' }],
- affiliation: user?.affiliation ?? '',
- storage: 'S3',
- description: '',
- [USE_FIELDS_FROM_PARENT]: true,
- [METADATA_BLOCKS_NAMES_GROUPER]: defaultBlocksNames,
- [INPUT_LEVELS_GROUPER]: mergedInputLevels,
- [USE_FACETS_FROM_PARENT]: true,
- [FACET_IDS_FIELD]: defaultCollectionFacets
- }
-
return (
)
diff --git a/src/sections/create-collection/CreateCollectionFactory.tsx b/src/sections/create-collection/CreateCollectionFactory.tsx
index af55ee266..0ff76a5c0 100644
--- a/src/sections/create-collection/CreateCollectionFactory.tsx
+++ b/src/sections/create-collection/CreateCollectionFactory.tsx
@@ -14,15 +14,15 @@ export class CreateCollectionFactory {
}
function CreateCollectionWithParams() {
- const { ownerCollectionId } = useParams<{ ownerCollectionId: string }>() as {
- ownerCollectionId: string
+ const { parentCollectionId } = useParams<{ parentCollectionId: string }>() as {
+ parentCollectionId: string
}
return (
)
}
diff --git a/src/sections/create-collection/CreateCollectionSkeleton.tsx b/src/sections/create-collection/CreateCollectionSkeleton.tsx
index b3fd3ef4a..60094ca6c 100644
--- a/src/sections/create-collection/CreateCollectionSkeleton.tsx
+++ b/src/sections/create-collection/CreateCollectionSkeleton.tsx
@@ -1,8 +1,7 @@
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'
-import { Col, Row, Stack } from '@iqss/dataverse-design-system'
-import { BreadcrumbsSkeleton } from '../shared/hierarchy/BreadcrumbsSkeleton'
-import 'react-loading-skeleton/dist/skeleton.css'
-import { SeparationLine } from '../shared/layout/SeparationLine/SeparationLine'
+import { BreadcrumbsSkeleton } from '@/sections/shared/hierarchy/BreadcrumbsSkeleton'
+import { SeparationLine } from '@/sections/shared/layout/SeparationLine/SeparationLine'
+import { EditCreateCollectionFormSkeleton } from '@/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionFormSkeleton'
export const CreateCollectionSkeleton = () => (
@@ -14,68 +13,7 @@ export const CreateCollectionSkeleton = () => (
- {/* Top fields section skeleton */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
)
diff --git a/src/sections/create-collection/collection-form/useSubmitCollection.ts b/src/sections/create-collection/collection-form/useSubmitCollection.ts
deleted file mode 100644
index 7a8fff79c..000000000
--- a/src/sections/create-collection/collection-form/useSubmitCollection.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { useState } from 'react'
-import { useNavigate } from 'react-router-dom'
-import { WriteError } from '@iqss/dataverse-client-javascript'
-import { createCollection } from '../../../collection/domain/useCases/createCollection'
-import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository'
-import { CollectionDTO } from '../../../collection/domain/useCases/DTOs/CollectionDTO'
-import {
- CollectionFormData,
- CollectionFormValuesOnSubmit,
- INPUT_LEVELS_GROUPER,
- METADATA_BLOCKS_NAMES_GROUPER,
- USE_FIELDS_FROM_PARENT
-} from './CollectionForm'
-import { RouteWithParams } from '../../Route.enum'
-import { JSDataverseWriteErrorHandler } from '../../../shared/helpers/JSDataverseWriteErrorHandler'
-import { CollectionFormHelper } from './CollectionFormHelper'
-
-export enum SubmissionStatus {
- NotSubmitted = 'NotSubmitted',
- IsSubmitting = 'IsSubmitting',
- SubmitComplete = 'SubmitComplete',
- Errored = 'Errored'
-}
-
-type UseSubmitCollectionReturnType =
- | {
- submissionStatus:
- | SubmissionStatus.NotSubmitted
- | SubmissionStatus.IsSubmitting
- | SubmissionStatus.SubmitComplete
- submitForm: (formData: CollectionFormData) => void
- submitError: null
- }
- | {
- submissionStatus: SubmissionStatus.Errored
- submitForm: (formData: CollectionFormData) => void
- submitError: string
- }
-
-export function useSubmitCollection(
- collectionRepository: CollectionRepository,
- ownerCollectionId: string,
- onSubmitErrorCallback: () => void
-): UseSubmitCollectionReturnType {
- const navigate = useNavigate()
-
- const [submissionStatus, setSubmissionStatus] = useState
(
- SubmissionStatus.NotSubmitted
- )
- const [submitError, setSubmitError] = useState(null)
-
- const submitForm = (formData: CollectionFormValuesOnSubmit): void => {
- setSubmissionStatus(SubmissionStatus.IsSubmitting)
-
- const contactsDTO = formData.contacts.map((contact) => contact.value)
-
- const metadataBlockNamesDTO =
- CollectionFormHelper.formatFormMetadataBlockNamesToMetadataBlockNamesDTO(
- formData[METADATA_BLOCKS_NAMES_GROUPER]
- )
-
- const inputLevelsDTO = CollectionFormHelper.formatFormInputLevelsToInputLevelsDTO(
- metadataBlockNamesDTO,
- formData[INPUT_LEVELS_GROUPER]
- )
-
- const facetIdsDTO = formData.facetIds.map((facet) => facet.value)
-
- const useFieldsFromParentChecked = formData[USE_FIELDS_FROM_PARENT]
-
- const newCollection: CollectionDTO = {
- name: formData.name,
- alias: formData.alias,
- type: formData.type,
- affiliation: formData.affiliation,
- description: formData.description,
- contacts: contactsDTO,
- metadataBlockNames: useFieldsFromParentChecked ? undefined : metadataBlockNamesDTO,
- inputLevels: useFieldsFromParentChecked ? undefined : inputLevelsDTO,
- facetIds: facetIdsDTO
- }
-
- // TODO: We can't send the hostCollection name, but we should send the hostCollection alias
- // So in a next iteration we should get the hostCollection alias from the hostCollection name selected
-
- createCollection(collectionRepository, newCollection, ownerCollectionId)
- .then(() => {
- setSubmitError(null)
- setSubmissionStatus(SubmissionStatus.SubmitComplete)
-
- navigate(RouteWithParams.COLLECTIONS(newCollection.alias), {
- state: { created: true }
- })
- return
- })
- .catch((err: WriteError) => {
- const error = new JSDataverseWriteErrorHandler(err)
- const formattedError = error.getReasonWithoutStatusCode() ?? error.getErrorMessage()
- setSubmitError(formattedError)
- setSubmissionStatus(SubmissionStatus.Errored)
- onSubmitErrorCallback()
- })
- }
-
- return {
- submissionStatus,
- submitForm,
- submitError
- } as UseSubmitCollectionReturnType
-}
diff --git a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/FileInfoCell.tsx b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/FileInfoCell.tsx
index 29ec191ca..494f4c1c6 100644
--- a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/FileInfoCell.tsx
+++ b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/FileInfoCell.tsx
@@ -13,10 +13,6 @@ import { FileDescription } from './file-info-data/FileDescription'
import { FileLabels } from '../../../../../file/file-labels/FileLabels'
export function FileInfoCell({ file }: { file: FilePreview }) {
- if (!file.datasetVersionNumber) {
- console.log('FileInfoCell error: FilePreview object must contain datasetVersionNumber')
- return null
- }
return (
diff --git a/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts b/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts
index fb921b418..5461f9b8b 100644
--- a/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts
+++ b/src/sections/dataset/dataset-files/files-table/row-selection/useFileSelection.ts
@@ -72,10 +72,14 @@ export function useFileSelection(
useEffect(() => {
setFileSelection(updateFileSelection())
+ // TODO: Not a priority as the one 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
}, [currentPageSelectedRowModel])
useEffect(() => {
setCurrentPageRowSelection(computeCurrentPageRowSelection())
+ // TODO: Not a priority as the one 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])
return {
diff --git a/src/sections/dataset/dataset-files/useFiles.tsx b/src/sections/dataset/dataset-files/useFiles.tsx
index afd1fa222..4244c6fdf 100644
--- a/src/sections/dataset/dataset-files/useFiles.tsx
+++ b/src/sections/dataset/dataset-files/useFiles.tsx
@@ -72,6 +72,9 @@ export function useFiles(
console.error('There was an error getting the files')
setIsLoading(false)
})
+
+ // TODO: Not a priority as the one 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
}, [
filesRepository,
datasetPersistentId,
diff --git a/src/sections/edit-collection/EditCollection.tsx b/src/sections/edit-collection/EditCollection.tsx
new file mode 100644
index 000000000..6fc7c593d
--- /dev/null
+++ b/src/sections/edit-collection/EditCollection.tsx
@@ -0,0 +1,121 @@
+import { useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
+import { Alert } from '@iqss/dataverse-design-system'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { useGetCollectionUserPermissions } from '@/shared/hooks/useGetCollectionUserPermissions'
+import { useLoading } from '../loading/LoadingContext'
+import { useSession } from '../session/SessionContext'
+import { useCollection } from '../collection/useCollection'
+import { User } from '@/users/domain/models/User'
+import { CollectionHelper } from '../collection/CollectionHelper'
+import { PageNotFound } from '../page-not-found/PageNotFound'
+import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator'
+import { SeparationLine } from '../shared/layout/SeparationLine/SeparationLine'
+import { RequiredFieldText } from '../shared/form/RequiredFieldText/RequiredFieldText'
+import { EditCreateCollectionForm } from '../shared/form/EditCreateCollectionForm/EditCreateCollectionForm'
+import { EditCollectionSkeleton } from './EditCollectionSkeleton'
+
+interface EditCollectionProps {
+ collectionId: string
+ collectionRepository: CollectionRepository
+ metadataBlockInfoRepository: MetadataBlockInfoRepository
+}
+
+export const EditCollection = ({
+ collectionId,
+ collectionRepository,
+ metadataBlockInfoRepository
+}: EditCollectionProps) => {
+ const { t } = useTranslation('editCollection')
+ const { setIsLoading } = useLoading()
+ const { user } = useSession()
+
+ const { collection, isLoading: isLoadingCollection } = useCollection(
+ collectionRepository,
+ collectionId
+ )
+
+ const {
+ collectionUserPermissions,
+ isLoading: isLoadingCollectionUserPermissions,
+ error: collectionPermissionsError
+ } = useGetCollectionUserPermissions({
+ collectionIdOrAlias: collectionId,
+ collectionRepository: collectionRepository
+ })
+ const canUserEditCollection = Boolean(collectionUserPermissions?.canEditCollection)
+
+ const isLoadingData = isLoadingCollection || isLoadingCollectionUserPermissions
+
+ useEffect(() => {
+ if (!isLoadingData) {
+ setIsLoading(false)
+ }
+ }, [setIsLoading, isLoadingData])
+
+ if (!isLoadingCollection && !collection) {
+ return
+ }
+
+ if (isLoadingData || !collection) {
+ return
+ }
+
+ if (collectionUserPermissions && !canUserEditCollection) {
+ return (
+
+
+ {t('notAllowedToEditCollection')}
+
+
+ )
+ }
+
+ if (collectionPermissionsError) {
+ return (
+
+ {collectionPermissionsError}
+
+ )
+ }
+
+ const parentCollection = CollectionHelper.getParentCollection(collection.hierarchy)
+
+ return (
+
+
+
+
+
+
+
+ {t('infoAlert.text')}
+
+
+
+
+
+
+ )
+}
diff --git a/src/sections/edit-collection/EditCollectionFactory.tsx b/src/sections/edit-collection/EditCollectionFactory.tsx
new file mode 100644
index 000000000..21dc9dc52
--- /dev/null
+++ b/src/sections/edit-collection/EditCollectionFactory.tsx
@@ -0,0 +1,28 @@
+import { ReactElement } from 'react'
+import { useParams } from 'react-router-dom'
+import { CollectionJSDataverseRepository } from '@/collection/infrastructure/repositories/CollectionJSDataverseRepository'
+import { MetadataBlockInfoJSDataverseRepository } from '@/metadata-block-info/infrastructure/repositories/MetadataBlockInfoJSDataverseRepository'
+import { EditCollection } from './EditCollection'
+
+const collectionRepository = new CollectionJSDataverseRepository()
+const metadataBlockInfoRepository = new MetadataBlockInfoJSDataverseRepository()
+
+export class EditCollectionFactory {
+ static create(): ReactElement {
+ return
+ }
+}
+
+function EditCollectionWithParams() {
+ const { collectionId } = useParams<{ collectionId: string }>() as {
+ collectionId: string
+ }
+
+ return (
+
+ )
+}
diff --git a/src/sections/edit-collection/EditCollectionSkeleton.tsx b/src/sections/edit-collection/EditCollectionSkeleton.tsx
new file mode 100644
index 000000000..0e268c7d9
--- /dev/null
+++ b/src/sections/edit-collection/EditCollectionSkeleton.tsx
@@ -0,0 +1,19 @@
+import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'
+import { BreadcrumbsSkeleton } from '@/sections/shared/hierarchy/BreadcrumbsSkeleton'
+import { SeparationLine } from '@/sections/shared/layout/SeparationLine/SeparationLine'
+import { EditCreateCollectionFormSkeleton } from '@/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionFormSkeleton'
+
+export const EditCollectionSkeleton = () => (
+
+
+
+)
diff --git a/src/sections/create-collection/collection-form/CollectionFormHelper.ts b/src/sections/shared/form/EditCreateCollectionForm/CollectionFormHelper.ts
similarity index 71%
rename from src/sections/create-collection/collection-form/CollectionFormHelper.ts
rename to src/sections/shared/form/EditCreateCollectionForm/CollectionFormHelper.ts
index b35815983..37e5ff999 100644
--- a/src/sections/create-collection/collection-form/CollectionFormHelper.ts
+++ b/src/sections/shared/form/EditCreateCollectionForm/CollectionFormHelper.ts
@@ -1,20 +1,22 @@
-import { CollectionInputLevel } from '../../../collection/domain/models/Collection'
-import {
- CollectionDTO,
- CollectionInputLevelDTO
-} from '../../../collection/domain/useCases/DTOs/CollectionDTO'
import {
MetadataBlockInfo,
MetadataBlockName,
MetadataField,
TypeClassMetadataFieldOptions
-} from '../../../metadata-block-info/domain/models/MetadataBlockInfo'
+} from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import { CollectionInputLevel } from '@/collection/domain/models/CollectionInputLevel'
+import {
+ CollectionDTO,
+ CollectionInputLevelDTO
+} from '@/collection/domain/useCases/DTOs/CollectionDTO'
+import { CollectionContact } from '@/collection/domain/models/CollectionContact'
import {
+ CollectionFormContactValue,
CollectionFormMetadataBlocks,
FormattedCollectionInputLevels,
FormattedCollectionInputLevelsWithoutParentBlockName,
MetadataFieldWithParentBlockInfo
-} from './CollectionForm'
+} from './types'
export class CollectionFormHelper {
public static replaceDotWithSlash = (str: string) => str.replace(/\./g, '/')
@@ -216,4 +218,82 @@ export class CollectionFormHelper {
}
})
}
+
+ public static formatCollectionContactsToFormContacts(
+ collectionContacts: CollectionContact[]
+ ): CollectionFormContactValue[] {
+ if (collectionContacts.length === 0) {
+ return [{ value: '' }]
+ }
+
+ return collectionContacts.map((contact) => ({
+ value: contact.email
+ }))
+ }
+
+ public static defineShouldCheckUseFromParent(
+ onEditMode: boolean,
+ isEditingRootCollection: boolean,
+ isMetadataBlockOrFacetRoot?: boolean
+ ): boolean {
+ if (onEditMode) {
+ if (isEditingRootCollection) {
+ return false
+ } else {
+ return !isMetadataBlockOrFacetRoot
+ }
+ } else {
+ return true
+ }
+ }
+
+ public static defineShouldSendMetadataBlockNamesAndInputLevels(
+ useFieldsFromParentChecked: boolean,
+ isEditingRootCollection: boolean,
+ blockNamesHaveChanged: boolean,
+ inputLevelsHaveChanged: boolean,
+ mode: 'edit' | 'create'
+ ): boolean {
+ if (mode === 'edit') {
+ if (isEditingRootCollection) {
+ if (blockNamesHaveChanged || inputLevelsHaveChanged) return true
+
+ return false
+ } else {
+ if (useFieldsFromParentChecked) return false
+
+ // If useFieldsFromParentChecked is not checked we should send the default metadata block names and input levels, the only way for the user to edit them is to uncheck the useFromParent first
+ return true
+ }
+ } else {
+ if (useFieldsFromParentChecked) return false
+
+ // If useFieldsFromParentChecked is not checked we should send the default metadata block names and input levels, the only way for the user to edit them is to uncheck the useFromParent first
+ return true
+ }
+ }
+
+ public static defineShouldSendFacetIds(
+ useFacetsFromParentChecked: boolean,
+ isEditingRootCollection: boolean,
+ facetIdsHaveChanged: boolean,
+ mode: 'edit' | 'create'
+ ): boolean {
+ if (mode === 'edit') {
+ if (isEditingRootCollection) {
+ if (facetIdsHaveChanged) return true
+ return false
+ } else {
+ if (useFacetsFromParentChecked) return false
+
+ // If useFacetsFromParentChecked is not checked we should send the default facetIds, changed or not
+ return true
+ }
+ } else {
+ if (useFacetsFromParentChecked) return false
+
+ // If useFacetsFromParentChecked is not checked we should send the default facetIds, changed or not
+ return true
+ }
+ }
}
diff --git a/src/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionForm.tsx b/src/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionForm.tsx
new file mode 100644
index 000000000..60ee9af42
--- /dev/null
+++ b/src/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionForm.tsx
@@ -0,0 +1,234 @@
+import { useEffect } from 'react'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { useGetCollectionMetadataBlocksInfo } from '@/shared/hooks/useGetCollectionMetadataBlocksInfo'
+import { useGetAllMetadataBlocksInfo } from '@/shared/hooks/useGetAllMetadataBlocksInfo'
+import { useGetCollectionFacets } from '@/shared/hooks/useGetCollectionFacets'
+import { useGetAllFacetableMetadataFields } from '@/shared/hooks/useGetAllFacetableMetadataFields'
+import { useLoading } from '@/sections/loading/LoadingContext'
+import { useDeepCompareMemo } from 'use-deep-compare'
+import { CollectionFormHelper } from './CollectionFormHelper'
+import {
+ CollectionFormData,
+ CollectionFormFacet,
+ CollectionFormMetadataBlocks,
+ FormattedCollectionInputLevels,
+ FormattedCollectionInputLevelsWithoutParentBlockName
+} from './types'
+import { Collection } from '@/collection/domain/models/Collection'
+import { Alert } from '@iqss/dataverse-design-system'
+import { User } from '@/users/domain/models/User'
+import { CollectionForm } from './collection-form/CollectionForm'
+import { EditCreateCollectionFormSkeleton } from './EditCreateCollectionFormSkeleton'
+import { CollectionHelper } from '@/sections/collection/CollectionHelper'
+
+export const METADATA_BLOCKS_NAMES_GROUPER = 'metadataBlockNames'
+export const USE_FIELDS_FROM_PARENT = 'useFieldsFromParent'
+export const INPUT_LEVELS_GROUPER = 'inputLevels'
+export const FACET_IDS_FIELD = 'facetIds'
+export const USE_FACETS_FROM_PARENT = 'useFacetsFromParent'
+
+type EditCreateCollectionFormProps =
+ | {
+ mode: 'create'
+ user: User
+ collection?: never // In create mode, collection is undefined, we only have parentCollection
+ parentCollection: Collection
+ collectionRepository: CollectionRepository
+ metadataBlockInfoRepository: MetadataBlockInfoRepository
+ }
+ | {
+ mode: 'edit'
+ user: User
+ collection: Collection
+ parentCollection?: ParentCollectionDataInEdition // In edit mode, parentCollection could be undefined in case of root collection
+ collectionRepository: CollectionRepository
+ metadataBlockInfoRepository: MetadataBlockInfoRepository
+ }
+
+export type EditCreateCollectionFormMode = 'create' | 'edit'
+
+interface ParentCollectionDataInEdition {
+ id: string
+ name: string
+}
+
+export const EditCreateCollectionForm = ({
+ mode,
+ user,
+ collection,
+ parentCollection,
+ collectionRepository,
+ metadataBlockInfoRepository
+}: EditCreateCollectionFormProps) => {
+ const { setIsLoading } = useLoading()
+
+ const onEditMode = mode === 'edit'
+ const isEditingRootCollection =
+ onEditMode && CollectionHelper.isRootCollection(collection.hierarchy)
+
+ const {
+ metadataBlocksInfo,
+ isLoading: isLoadingMetadataBlocksInfo,
+ error: metadataBlockInfoError
+ } = useGetCollectionMetadataBlocksInfo({
+ collectionId: onEditMode ? collection.id : parentCollection.id,
+ metadataBlockInfoRepository
+ })
+
+ const {
+ allMetadataBlocksInfo,
+ isLoading: isLoadingAllMetadataBlocksInfo,
+ error: allMetadataBlocksInfoError
+ } = useGetAllMetadataBlocksInfo({ metadataBlockInfoRepository })
+
+ const {
+ collectionFacets,
+ isLoading: isLoadingCollectionFacets,
+ error: collectionFacetsError
+ } = useGetCollectionFacets({
+ collectionId: onEditMode ? collection.id : parentCollection.id,
+ collectionRepository
+ })
+
+ const {
+ facetableMetadataFields,
+ isLoading: isLoadingFacetableMetadataFields,
+ error: facetableMetadataFieldsError
+ } = useGetAllFacetableMetadataFields({
+ metadataBlockInfoRepository
+ })
+
+ const baseInputLevels: FormattedCollectionInputLevels = useDeepCompareMemo(() => {
+ return CollectionFormHelper.defineBaseInputLevels(allMetadataBlocksInfo)
+ }, [allMetadataBlocksInfo])
+
+ const collectionInputLevelsToFormat =
+ mode === 'edit' ? collection.inputLevels : parentCollection.inputLevels
+
+ const formattedCollectionInputLevels: FormattedCollectionInputLevelsWithoutParentBlockName =
+ useDeepCompareMemo(() => {
+ return CollectionFormHelper.formatCollectiontInputLevels(collectionInputLevelsToFormat)
+ }, [collectionInputLevelsToFormat])
+
+ const mergedInputLevels = useDeepCompareMemo(() => {
+ return CollectionFormHelper.mergeBaseAndDefaultInputLevels(
+ baseInputLevels,
+ formattedCollectionInputLevels
+ )
+ }, [baseInputLevels, formattedCollectionInputLevels])
+
+ const baseBlockNames = useDeepCompareMemo(() => {
+ return allMetadataBlocksInfo.reduce((acc, block) => {
+ acc[block.name as keyof CollectionFormMetadataBlocks] = false
+ return acc
+ }, {} as CollectionFormMetadataBlocks)
+ }, [allMetadataBlocksInfo])
+
+ const defaultBlocksNames = useDeepCompareMemo(
+ () =>
+ metadataBlocksInfo
+ .map((block) => block.name)
+ .reduce((acc, blockName) => {
+ acc[blockName as keyof CollectionFormMetadataBlocks] = true
+ return acc
+ }, baseBlockNames),
+ [metadataBlocksInfo, baseBlockNames]
+ )
+
+ const defaultCollectionFacets: CollectionFormFacet[] = collectionFacets.map((facet) => ({
+ id: facet.name,
+ value: facet.name,
+ label: facet.displayName
+ }))
+
+ const isLoadingData =
+ isLoadingMetadataBlocksInfo ||
+ isLoadingAllMetadataBlocksInfo ||
+ isLoadingCollectionFacets ||
+ isLoadingFacetableMetadataFields
+
+ const dataLoadingErrors = [
+ metadataBlockInfoError,
+ allMetadataBlocksInfoError,
+ collectionFacetsError,
+ facetableMetadataFieldsError
+ ]
+
+ useEffect(() => {
+ if (!isLoadingData) {
+ setIsLoading(false)
+ }
+ }, [isLoadingData, setIsLoading])
+
+ if (isLoadingData) {
+ return
+ }
+
+ if (dataLoadingErrors.some((error) => error !== null)) {
+ return (
+ <>
+ {dataLoadingErrors
+ .filter((err) => err !== null)
+ .map((error, index) => (
+
+ {error}
+
+ ))}
+ >
+ )
+ }
+
+ const defaultCollectionName = onEditMode
+ ? collection.name
+ : user?.displayName
+ ? `${user?.displayName} Collection`
+ : ''
+
+ const defaultContacts = onEditMode
+ ? CollectionFormHelper.formatCollectionContactsToFormContacts(collection.contacts)
+ : [{ value: user?.email ?? '' }]
+
+ const useFieldsFromParentDefault = CollectionFormHelper.defineShouldCheckUseFromParent(
+ onEditMode,
+ isEditingRootCollection,
+ mode === 'edit' ? collection.isMetadataBlockRoot : undefined
+ )
+
+ const useFacetsFromParentDefault = CollectionFormHelper.defineShouldCheckUseFromParent(
+ onEditMode,
+ isEditingRootCollection,
+ mode === 'edit' ? collection.isFacetRoot : undefined
+ )
+
+ const formDefaultValues: CollectionFormData = {
+ hostCollection: isEditingRootCollection
+ ? null
+ : (parentCollection as ParentCollectionDataInEdition).name,
+ name: defaultCollectionName,
+ alias: onEditMode ? collection.id : '',
+ type: onEditMode ? collection.type : '',
+ contacts: defaultContacts,
+ affiliation: onEditMode ? collection.affiliation ?? '' : user?.affiliation ?? '',
+ storage: 'S3',
+ description: onEditMode ? collection.description ?? '' : '',
+ [USE_FIELDS_FROM_PARENT]: useFieldsFromParentDefault,
+ [METADATA_BLOCKS_NAMES_GROUPER]: defaultBlocksNames,
+ [INPUT_LEVELS_GROUPER]: mergedInputLevels,
+ [USE_FACETS_FROM_PARENT]: useFacetsFromParentDefault,
+ [FACET_IDS_FIELD]: defaultCollectionFacets
+ }
+
+ return (
+
+ )
+}
diff --git a/src/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionFormSkeleton.tsx b/src/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionFormSkeleton.tsx
new file mode 100644
index 000000000..241d48a72
--- /dev/null
+++ b/src/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionFormSkeleton.tsx
@@ -0,0 +1,75 @@
+import Skeleton from 'react-loading-skeleton'
+import { Col, Row, Stack } from '@iqss/dataverse-design-system'
+import { SeparationLine } from '../../layout/SeparationLine/SeparationLine'
+
+export const EditCreateCollectionFormSkeleton = () => {
+ return (
+ <>
+ {/* Top fields section skeleton */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Metadata Fields section */}
+
+
+ {/* Browse/Search Facets section */}
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/src/sections/create-collection/collection-form/CollectionForm.module.scss b/src/sections/shared/form/EditCreateCollectionForm/collection-form/CollectionForm.module.scss
similarity index 100%
rename from src/sections/create-collection/collection-form/CollectionForm.module.scss
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/CollectionForm.module.scss
diff --git a/src/sections/create-collection/collection-form/CollectionForm.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/CollectionForm.tsx
similarity index 52%
rename from src/sections/create-collection/collection-form/CollectionForm.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/CollectionForm.tsx
index 43d9c3b39..04d6a409d 100644
--- a/src/sections/create-collection/collection-form/CollectionForm.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/CollectionForm.tsx
@@ -1,113 +1,48 @@
-import { MouseEvent, useMemo, useRef } from 'react'
+import { useMemo, useRef } from 'react'
import { FormProvider, useForm } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
import { Alert, Button, Card, Stack } from '@iqss/dataverse-design-system'
-import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository'
-import {
- CollectionType,
- CollectionStorage
-} from '../../../collection/domain/useCases/DTOs/CollectionDTO'
-import { SubmissionStatus, useSubmitCollection } from './useSubmitCollection'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { CollectionFormData, CollectionFormFacet } from '../types'
import {
MetadataBlockInfo,
- MetadataBlockName,
MetadataField
-} from '../../../metadata-block-info/domain/models/MetadataBlockInfo'
-import { SeparationLine } from '../../shared/layout/SeparationLine/SeparationLine'
+} from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import { SubmissionStatus, useSubmitCollection } from './useSubmitCollection'
+import styles from './CollectionForm.module.scss'
import { TopFieldsSection } from './top-fields-section/TopFieldsSection'
+import { SeparationLine } from '@/sections/shared/layout/SeparationLine/SeparationLine'
import { MetadataFieldsSection } from './metadata-fields-section/MetadataFieldsSection'
import { BrowseSearchFacetsSection } from './browse-search-facets-section/BrowseSearchFacetsSection'
-import styles from './CollectionForm.module.scss'
-
-export const METADATA_BLOCKS_NAMES_GROUPER = 'metadataBlockNames'
-export const USE_FIELDS_FROM_PARENT = 'useFieldsFromParent'
-export const INPUT_LEVELS_GROUPER = 'inputLevels'
-export const FACET_IDS_FIELD = 'facetIds'
-export const USE_FACETS_FROM_PARENT = 'useFacetsFromParent'
+import { EditCreateCollectionFormMode } from '../EditCreateCollectionForm'
+import { RouteWithParams } from '@/sections/Route.enum'
export interface CollectionFormProps {
+ mode: EditCreateCollectionFormMode
collectionRepository: CollectionRepository
- ownerCollectionId: string
+ collectionIdOrParentCollectionId: string
defaultValues: CollectionFormData
allMetadataBlocksInfo: MetadataBlockInfo[]
allFacetableMetadataFields: MetadataField[]
defaultCollectionFacets: CollectionFormFacet[]
-}
-
-export type CollectionFormData = {
- hostCollection: string
- name: string
- affiliation: string
- alias: string
- storage: CollectionStorage
- type: CollectionType | ''
- description: string
- contacts: { value: string }[]
- [USE_FIELDS_FROM_PARENT]: boolean
- [METADATA_BLOCKS_NAMES_GROUPER]: CollectionFormMetadataBlocks
- [INPUT_LEVELS_GROUPER]: FormattedCollectionInputLevels
- [USE_FACETS_FROM_PARENT]: boolean
- facetIds: CollectionFormFacet[]
-}
-
-export type CollectionFormMetadataBlocks = Record
-
-export type FormattedCollectionInputLevels = {
- [key: string]: {
- include: boolean
- optionalOrRequired: CollectionFormInputLevelValue
- parentBlockName: MetadataBlockName
- }
-}
-
-export type FormattedCollectionInputLevelsWithoutParentBlockName = {
- [K in keyof FormattedCollectionInputLevels]: Omit<
- FormattedCollectionInputLevels[K],
- 'parentBlockName'
- >
-}
-
-export const CollectionFormInputLevelOptions = {
- OPTIONAL: 'optional',
- REQUIRED: 'required'
-} as const
-
-export type CollectionFormInputLevelValue =
- (typeof CollectionFormInputLevelOptions)[keyof typeof CollectionFormInputLevelOptions]
-
-export type CollectionFormFacet = {
- value: string
- label: string
- id: string
-}
-
-export type MetadataFieldWithParentBlockInfo = MetadataField & {
- parentBlockInfo: Pick
-}
-
-// On the submit function callback, type is CollectionType as type field is required and wont never be ""
-export type CollectionFormValuesOnSubmit = Omit & {
- type: CollectionType
+ isEditingRootCollection: boolean
}
export const CollectionForm = ({
+ mode,
collectionRepository,
- ownerCollectionId,
+ collectionIdOrParentCollectionId,
defaultValues,
allMetadataBlocksInfo,
allFacetableMetadataFields,
- defaultCollectionFacets
+ defaultCollectionFacets,
+ isEditingRootCollection
}: CollectionFormProps) => {
const formContainerRef = useRef(null)
- const { t } = useTranslation('createCollection')
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm' })
const navigate = useNavigate()
-
- const { submitForm, submitError, submissionStatus } = useSubmitCollection(
- collectionRepository,
- ownerCollectionId,
- onSubmittedCollectionError
- )
+ const onCreateMode = mode === 'create'
const form = useForm({
mode: 'onChange',
@@ -116,15 +51,14 @@ export const CollectionForm = ({
const { formState } = form
- const preventEnterSubmit = (e: React.KeyboardEvent) => {
- // When pressing Enter, only submit the form if the user is focused on the submit button itself
- if (e.key !== 'Enter') return
-
- const isButton = e.target instanceof HTMLButtonElement
- const isButtonTypeSubmit = isButton ? (e.target as HTMLButtonElement).type === 'submit' : false
-
- if (!isButton && !isButtonTypeSubmit) e.preventDefault()
- }
+ const { submitForm, submitError, submissionStatus } = useSubmitCollection(
+ mode,
+ collectionIdOrParentCollectionId,
+ collectionRepository,
+ isEditingRootCollection,
+ formState.dirtyFields,
+ onSubmittedCollectionError
+ )
function onSubmittedCollectionError() {
if (formContainerRef.current) {
@@ -132,9 +66,8 @@ export const CollectionForm = ({
}
}
- const handleCancel = (event: MouseEvent) => {
- event.preventDefault()
- navigate(-1)
+ const handleCancel = () => {
+ navigate(RouteWithParams.COLLECTIONS(collectionIdOrParentCollectionId))
}
const disableSubmitButton = useMemo(() => {
@@ -153,16 +86,15 @@ export const CollectionForm = ({
)}
{submissionStatus === SubmissionStatus.SubmitComplete && (
- {t('submitStatus.success')}
+ {onCreateMode ? t('submitStatus.createSuccess') : t('submitStatus.editSuccess')}
)}
diff --git a/src/sections/create-collection/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.module.scss b/src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.module.scss
similarity index 52%
rename from src/sections/create-collection/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.module.scss
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.module.scss
index 252f11bf4..5c72cb488 100644
--- a/src/sections/create-collection/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.module.scss
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.module.scss
@@ -1,7 +1,16 @@
+@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
+
.transfer-list-container {
display: flex;
justify-content: center;
+ .error-msg {
+ width: 100%;
+ margin-top: 0.25rem;
+ color: $dv-danger-color;
+ font-size: 0.875em;
+ }
+
@media (min-width: 768px) {
justify-content: flex-start;
}
diff --git a/src/sections/create-collection/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.tsx
similarity index 68%
rename from src/sections/create-collection/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.tsx
index a18655559..3939d5557 100644
--- a/src/sections/create-collection/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.tsx
@@ -10,12 +10,13 @@ import {
TransferListItem,
SelectAdvanced
} from '@iqss/dataverse-design-system'
+import { CollectionFormFacet } from '../../types'
import {
MetadataBlockInfo,
MetadataField
-} from '../../../../metadata-block-info/domain/models/MetadataBlockInfo'
-import { CollectionFormHelper } from '../CollectionFormHelper'
-import { CollectionFormFacet, FACET_IDS_FIELD, USE_FACETS_FROM_PARENT } from '../CollectionForm'
+} from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import { FACET_IDS_FIELD, USE_FACETS_FROM_PARENT } from '../../EditCreateCollectionForm'
+import { CollectionFormHelper } from '../../CollectionFormHelper'
import { FacetsFromParentCheckbox } from './FacetsFromParentCheckbox'
import styles from './BrowseSearchFacetsSection.module.scss'
@@ -23,14 +24,16 @@ interface BrowseSearchFacetsSectionProps {
defaultCollectionFacets: CollectionFormFacet[]
allFacetableMetadataFields: MetadataField[]
allMetadataBlocksInfo: MetadataBlockInfo[]
+ isEditingRootCollection: boolean
}
export const BrowseSearchFacetsSection = ({
defaultCollectionFacets,
allFacetableMetadataFields,
- allMetadataBlocksInfo
+ allMetadataBlocksInfo,
+ isEditingRootCollection
}: BrowseSearchFacetsSectionProps) => {
- const { t } = useTranslation('createCollection')
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm' })
const { control } = useFormContext()
const useBrowseSearchFacetsFromParentCheckedValue = useWatch({
name: USE_FACETS_FROM_PARENT
@@ -101,10 +104,12 @@ export const BrowseSearchFacetsSection = ({
{t('fields.browseSearchFacets.helperText')}
-
+ {!isEditingRootCollection && (
+
+ )}
(
-
handleOnChangeSelectedItems(selectedItems, onChange)}
- availableItems={availableItems}
- defaultSelected={defaultCollectionFacets}
- rightLabel={t('fields.browseSearchFacets.selectedFacets')}
- disabled={useBrowseSearchFacetsFromParentCheckedValue}
- key={resetKey}
- />
+ rules={{
+ validate: (facetsSelected: CollectionFormFacet[]) => {
+ if (facetsSelected.length === 0) {
+ return t('fields.browseSearchFacets.invalid.minLength')
+ }
+ return true
+ }
+ }}
+ render={({ field: { onChange }, fieldState: { invalid, error } }) => (
+
+
+ handleOnChangeSelectedItems(selectedItems, onChange)
+ }
+ availableItems={availableItems}
+ defaultSelected={defaultCollectionFacets}
+ rightLabel={t('fields.browseSearchFacets.selectedFacets')}
+ disabled={useBrowseSearchFacetsFromParentCheckedValue}
+ key={resetKey}
+ />
+ {invalid && {error?.message}
}
+
)}
/>
diff --git a/src/sections/create-collection/collection-form/browse-search-facets-section/FacetsFromParentCheckbox.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/FacetsFromParentCheckbox.tsx
similarity index 84%
rename from src/sections/create-collection/collection-form/browse-search-facets-section/FacetsFromParentCheckbox.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/FacetsFromParentCheckbox.tsx
index 38e234d53..ae58a34e9 100644
--- a/src/sections/create-collection/collection-form/browse-search-facets-section/FacetsFromParentCheckbox.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/FacetsFromParentCheckbox.tsx
@@ -2,7 +2,8 @@ import { ChangeEvent } from 'react'
import { Controller, useFormContext, useWatch } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { Form } from '@iqss/dataverse-design-system'
-import { CollectionFormFacet, FACET_IDS_FIELD, USE_FACETS_FROM_PARENT } from '../CollectionForm'
+import { CollectionFormFacet } from '../../types'
+import { FACET_IDS_FIELD, USE_FACETS_FROM_PARENT } from '../../EditCreateCollectionForm'
interface FacetsFromParentCheckboxProps {
defaultCollectionFacets: CollectionFormFacet[]
@@ -13,7 +14,7 @@ export const FacetsFromParentCheckbox = ({
defaultCollectionFacets,
resetAvailableItems
}: FacetsFromParentCheckboxProps) => {
- const { t } = useTranslation('createCollection')
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm' })
const { control, setValue } = useFormContext()
const hostCollectionFieldValue = useWatch({ name: 'hostCollection' }) as string
@@ -22,7 +23,7 @@ export const FacetsFromParentCheckbox = ({
formOnChange: (...event: unknown[]) => void
) => {
if (e.target.checked) {
- setValue(FACET_IDS_FIELD, defaultCollectionFacets)
+ setValue(FACET_IDS_FIELD, defaultCollectionFacets, { shouldDirty: true })
resetAvailableItems()
}
formOnChange(e)
diff --git a/src/sections/create-collection/collection-form/metadata-fields-section/MetadataFieldsSection.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/MetadataFieldsSection.tsx
similarity index 72%
rename from src/sections/create-collection/collection-form/metadata-fields-section/MetadataFieldsSection.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/MetadataFieldsSection.tsx
index 48470e575..9b9ef6804 100644
--- a/src/sections/create-collection/collection-form/metadata-fields-section/MetadataFieldsSection.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/MetadataFieldsSection.tsx
@@ -6,19 +6,21 @@ import { FieldsFromParentCheckbox } from './fields-from-parent-checkbox/FieldsFr
import {
MetadataBlockInfo,
MetadataBlockName
-} from '../../../../metadata-block-info/domain/models/MetadataBlockInfo'
-import { CollectionFormData } from '../CollectionForm'
+} from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import { CollectionFormData } from '../../types'
interface MetadataFieldsSectionProps {
allMetadataBlocksInfo: MetadataBlockInfo[]
defaultValues: CollectionFormData
+ isEditingRootCollection: boolean
}
export const MetadataFieldsSection = ({
allMetadataBlocksInfo,
- defaultValues
+ defaultValues,
+ isEditingRootCollection
}: MetadataFieldsSectionProps) => {
- const { t } = useTranslation('createCollection', { keyPrefix: 'fields.metadataFields' })
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm.fields.metadataFields' })
return (
@@ -29,7 +31,8 @@ export const MetadataFieldsSection = ({
{t('helperText')}
-
+ {!isEditingRootCollection && }
+
{allMetadataBlocksInfo.map((block) => {
return (
@@ -38,6 +41,8 @@ export const MetadataFieldsSection = ({
blockName={block.name as MetadataBlockName}
blockDisplayName={block.displayName}
metadataBlockInfo={block}
+ isEditingRootCollection={isEditingRootCollection}
+ defaultValues={defaultValues}
/>
)
})}
diff --git a/src/sections/create-collection/collection-form/metadata-fields-section/fields-from-parent-checkbox/ConfirmResetModificationsModal.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/fields-from-parent-checkbox/ConfirmResetModificationsModal.tsx
similarity index 91%
rename from src/sections/create-collection/collection-form/metadata-fields-section/fields-from-parent-checkbox/ConfirmResetModificationsModal.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/fields-from-parent-checkbox/ConfirmResetModificationsModal.tsx
index 9412b8fb9..6f2e9391c 100644
--- a/src/sections/create-collection/collection-form/metadata-fields-section/fields-from-parent-checkbox/ConfirmResetModificationsModal.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/fields-from-parent-checkbox/ConfirmResetModificationsModal.tsx
@@ -1,5 +1,5 @@
-import { Alert, Button, Modal } from '@iqss/dataverse-design-system'
import { useTranslation } from 'react-i18next'
+import { Alert, Button, Modal } from '@iqss/dataverse-design-system'
interface ConfirmResetModificationsModalProps {
showModal: boolean
@@ -12,7 +12,7 @@ export const ConfirmResetModificationsModal = ({
onContinue,
onCancel
}: ConfirmResetModificationsModalProps) => {
- const { t } = useTranslation('createCollection', { keyPrefix: 'confirmResetModal' })
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm.confirmResetModal' })
return (
diff --git a/src/sections/create-collection/collection-form/metadata-fields-section/fields-from-parent-checkbox/FieldsFromParentCheckbox.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/fields-from-parent-checkbox/FieldsFromParentCheckbox.tsx
similarity index 87%
rename from src/sections/create-collection/collection-form/metadata-fields-section/fields-from-parent-checkbox/FieldsFromParentCheckbox.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/fields-from-parent-checkbox/FieldsFromParentCheckbox.tsx
index e04c1ea71..1eb442342 100644
--- a/src/sections/create-collection/collection-form/metadata-fields-section/fields-from-parent-checkbox/FieldsFromParentCheckbox.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/fields-from-parent-checkbox/FieldsFromParentCheckbox.tsx
@@ -3,36 +3,38 @@ import { useTranslation } from 'react-i18next'
import { Controller, UseControllerProps, useFormContext, useWatch } from 'react-hook-form'
import { Form } from '@iqss/dataverse-design-system'
import { ConfirmResetModificationsModal } from './ConfirmResetModificationsModal'
+import { CollectionFormData } from '../../../types'
import {
- CollectionFormData,
INPUT_LEVELS_GROUPER,
METADATA_BLOCKS_NAMES_GROUPER,
USE_FIELDS_FROM_PARENT
-} from '../../CollectionForm'
+} from '../../../EditCreateCollectionForm'
interface FieldsFromParentCheckboxProps {
defaultValues: CollectionFormData
}
export const FieldsFromParentCheckbox = ({ defaultValues }: FieldsFromParentCheckboxProps) => {
- const { t } = useTranslation('createCollection')
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm' })
const checkboxID = useId()
const { control, setValue } = useFormContext()
const [showResetConfirmationModal, setShowResetConfirmationModal] = useState(false)
const hostCollectionFieldValue = useWatch({ name: 'hostCollection' }) as string
const handleContinueWithReset = () => {
- setValue(USE_FIELDS_FROM_PARENT, true)
+ setValue(USE_FIELDS_FROM_PARENT, true, { shouldDirty: true })
const metadataBlockDefaultValues = Object.entries(defaultValues[METADATA_BLOCKS_NAMES_GROUPER])
- // Reset metadata block names checkboxes to the inital value
+ // Reset metadata block names checboxes to the inital value
metadataBlockDefaultValues.forEach(([blockName, blockInitialValue]) => {
- setValue(`${METADATA_BLOCKS_NAMES_GROUPER}.${blockName}`, blockInitialValue)
+ setValue(`${METADATA_BLOCKS_NAMES_GROUPER}.${blockName}`, blockInitialValue, {
+ shouldDirty: true
+ })
})
// Reset input levels to the initial value
- setValue(INPUT_LEVELS_GROUPER, defaultValues[INPUT_LEVELS_GROUPER])
+ setValue(INPUT_LEVELS_GROUPER, defaultValues[INPUT_LEVELS_GROUPER], { shouldDirty: true })
closeModal()
}
diff --git a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/MetadataInputLevelFieldsBlock.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/MetadataInputLevelFieldsBlock.tsx
similarity index 77%
rename from src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/MetadataInputLevelFieldsBlock.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/MetadataInputLevelFieldsBlock.tsx
index 5f4eb8153..5ff1015e1 100644
--- a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/MetadataInputLevelFieldsBlock.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/MetadataInputLevelFieldsBlock.tsx
@@ -1,29 +1,38 @@
import { ChangeEvent, useEffect, useId, useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { Controller, UseControllerProps, useFormContext, useWatch } from 'react-hook-form'
import { Button, Form, Stack, CloseButton } from '@iqss/dataverse-design-system'
import {
MetadataBlockInfo,
MetadataBlockName
-} from '../../../../../metadata-block-info/domain/models/MetadataBlockInfo'
-import { METADATA_BLOCKS_NAMES_GROUPER, USE_FIELDS_FROM_PARENT } from '../../CollectionForm'
+} from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import {
+ INPUT_LEVELS_GROUPER,
+ METADATA_BLOCKS_NAMES_GROUPER,
+ USE_FIELDS_FROM_PARENT
+} from '../../../EditCreateCollectionForm'
import { InputLevelsTable } from './input-levels-table/InputLevelsTable'
-import { useTranslation } from 'react-i18next'
+import { CollectionFormData } from '../../../types'
interface MetadataInputLevelFieldsBlockProps {
blockName: MetadataBlockName
blockDisplayName: string
metadataBlockInfo: MetadataBlockInfo
+ isEditingRootCollection: boolean
+ defaultValues: CollectionFormData
}
export const MetadataInputLevelFieldsBlock = ({
blockName,
blockDisplayName,
- metadataBlockInfo
+ metadataBlockInfo,
+ isEditingRootCollection,
+ defaultValues
}: MetadataInputLevelFieldsBlockProps) => {
const checkboxID = useId()
- const { control } = useFormContext()
- const { t } = useTranslation('createCollection', {
- keyPrefix: 'fields.metadataFields.inputLevelsTable'
+ const { control, setValue } = useFormContext()
+ const { t } = useTranslation('shared', {
+ keyPrefix: 'collectionForm.fields.metadataFields.inputLevelsTable'
})
const [inputLevelsTableStatus, setInputLevelsTableStatus] = useState({
@@ -42,6 +51,12 @@ export const MetadataInputLevelFieldsBlock = ({
const isCitation = blockName === MetadataBlockName.CITATION
+ const disabledBlockCheckbox = isCitation
+ ? true
+ : isEditingRootCollection
+ ? false
+ : useFieldsFromParentCheckedValue
+
const rules: UseControllerProps['rules'] = {}
const handleEditInputLevels = () => {
@@ -78,6 +93,19 @@ export const MetadataInputLevelFieldsBlock = ({
}
formOnChange(e)
+
+ // Apart from changing the checked status of the blockname, if the block is unchecked, make the input levels of that block back to the initial default value
+ if (!e.target.checked) {
+ const blockInputLevelsToSetBackToDefault = Object.entries(
+ defaultValues[INPUT_LEVELS_GROUPER]
+ ).filter(([_key, value]) => value.parentBlockName === blockName)
+
+ blockInputLevelsToSetBackToDefault.forEach(([key, _value]) => {
+ setValue(`${INPUT_LEVELS_GROUPER}.${key}`, defaultValues[INPUT_LEVELS_GROUPER][key], {
+ shouldDirty: true
+ })
+ })
+ }
}
// In order to close the table when use fields from parent change from unchecked to checked
@@ -106,7 +134,7 @@ export const MetadataInputLevelFieldsBlock = ({
checked={value as boolean}
isInvalid={invalid}
invalidFeedback={error?.message}
- disabled={isCitation ? true : useFieldsFromParentCheckedValue}
+ disabled={disabledBlockCheckbox}
ref={ref}
/>
)}
diff --git a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelFieldRow.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelFieldRow.tsx
similarity index 91%
rename from src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelFieldRow.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelFieldRow.tsx
index 3a34826ed..e3066dcb4 100644
--- a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelFieldRow.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelFieldRow.tsx
@@ -1,16 +1,16 @@
import { ChangeEvent, useId } from 'react'
import { Controller, UseControllerProps, useFormContext, useWatch } from 'react-hook-form'
import cn from 'classnames'
+import { useTranslation } from 'react-i18next'
import { Form } from '@iqss/dataverse-design-system'
+import { RequiredOptionalRadios } from './RequiredOptionalRadios'
import {
MetadataField,
TypeClassMetadataFieldOptions
-} from '../../../../../../metadata-block-info/domain/models/MetadataBlockInfo'
-import { INPUT_LEVELS_GROUPER } from '../../../CollectionForm'
+} from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import { INPUT_LEVELS_GROUPER } from '../../../../EditCreateCollectionForm'
+import { CollectionFormHelper } from '../../../../CollectionFormHelper'
import styles from './InputLevelsTable.module.scss'
-import { CollectionFormHelper } from '../../../CollectionFormHelper'
-import { RequiredOptionalRadios } from './RequiredOptionalRadios'
-import { useTranslation } from 'react-i18next'
interface InputLevelFieldRowProps {
metadataField: MetadataField
@@ -18,8 +18,8 @@ interface InputLevelFieldRowProps {
}
export const InputLevelFieldRow = ({ metadataField, disabled }: InputLevelFieldRowProps) => {
- const { t } = useTranslation('createCollection', {
- keyPrefix: 'fields.metadataFields.inputLevelsTable'
+ const { t } = useTranslation('shared', {
+ keyPrefix: 'collectionForm.fields.metadataFields.inputLevelsTable'
})
const uniqueInputLevelRowID = useId()
const { control, setValue } = useFormContext()
@@ -49,13 +49,19 @@ export const InputLevelFieldRow = ({ metadataField, disabled }: InputLevelFieldR
if (e.target.checked === false) {
// If include is set to false, then field and child fields should be set to optional and include false
if (!childMetadataFields) {
- setValue(`${INPUT_LEVELS_GROUPER}.${name}.optionalOrRequired`, 'optional')
+ setValue(`${INPUT_LEVELS_GROUPER}.${name}.optionalOrRequired`, 'optional', {
+ shouldDirty: true
+ })
} else {
- setValue(`${INPUT_LEVELS_GROUPER}.${name}.optionalOrRequired`, 'optional')
+ setValue(`${INPUT_LEVELS_GROUPER}.${name}.optionalOrRequired`, 'optional', {
+ shouldDirty: true
+ })
Object.values(childMetadataFields).forEach(({ name }) => {
setValue(`${INPUT_LEVELS_GROUPER}.${name}.include`, false)
- setValue(`${INPUT_LEVELS_GROUPER}.${name}.optionalOrRequired`, 'optional')
+ setValue(`${INPUT_LEVELS_GROUPER}.${name}.optionalOrRequired`, 'optional', {
+ shouldDirty: true
+ })
})
}
formOnChange(e)
diff --git a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.module.scss b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.module.scss
similarity index 100%
rename from src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.module.scss
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.module.scss
diff --git a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.tsx
similarity index 91%
rename from src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.tsx
index 830937d3a..13168da30 100644
--- a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/InputLevelsTable.tsx
@@ -1,7 +1,7 @@
import { ReactNode } from 'react'
import cn from 'classnames'
import { Table } from '@iqss/dataverse-design-system'
-import { MetadataBlockInfo } from '../../../../../../metadata-block-info/domain/models/MetadataBlockInfo'
+import { MetadataBlockInfo } from '@/metadata-block-info/domain/models/MetadataBlockInfo'
import { InputLevelFieldRow } from './InputLevelFieldRow'
import styles from './InputLevelsTable.module.scss'
diff --git a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/RequiredOptionalRadios.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/RequiredOptionalRadios.tsx
similarity index 88%
rename from src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/RequiredOptionalRadios.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/RequiredOptionalRadios.tsx
index 3bbcb6338..d18eb4fd6 100644
--- a/src/sections/create-collection/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/RequiredOptionalRadios.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/metadata-fields-section/metadata-input-level-fields-block/input-levels-table/RequiredOptionalRadios.tsx
@@ -2,8 +2,9 @@ import { ChangeEvent } from 'react'
import { useTranslation } from 'react-i18next'
import { Form, Stack } from '@iqss/dataverse-design-system'
import { Controller, useFormContext, useWatch } from 'react-hook-form'
-import { MetadataField } from '../../../../../../metadata-block-info/domain/models/MetadataBlockInfo'
-import { CollectionFormInputLevelValue, INPUT_LEVELS_GROUPER } from '../../../CollectionForm'
+import { MetadataField } from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import { INPUT_LEVELS_GROUPER } from '../../../../EditCreateCollectionForm'
+import { CollectionFormInputLevelValue } from '../../../../types'
type RequiredOptionalRadiosProps =
| {
@@ -46,8 +47,8 @@ export const RequiredOptionalRadios = ({
uniqueInputLevelRowID,
isConditionallyRequired
}: RequiredOptionalRadiosProps) => {
- const { t } = useTranslation('createCollection', {
- keyPrefix: 'fields.metadataFields.inputLevelsTable'
+ const { t } = useTranslation('shared', {
+ keyPrefix: 'collectionForm.fields.metadataFields.inputLevelsTable'
})
const { control, setValue } = useFormContext()
@@ -67,7 +68,9 @@ export const RequiredOptionalRadios = ({
if (isForChildField) {
// If parent is required by dataverse, then is already required
if (e.target.value === 'required' && !parentIsRequiredByDataverse) {
- setValue(`${INPUT_LEVELS_GROUPER}.${parentIncludeName}.optionalOrRequired`, 'required')
+ setValue(`${INPUT_LEVELS_GROUPER}.${parentIncludeName}.optionalOrRequired`, 'required', {
+ shouldDirty: true
+ })
}
// If parent is required by dataverse, then is already required and should not be set to optional
@@ -77,9 +80,13 @@ export const RequiredOptionalRadios = ({
).some((value) => value === 'required')
if (!isSomeSiblingRequired) {
- setValue(`${INPUT_LEVELS_GROUPER}.${parentIncludeName}.optionalOrRequired`, 'optional')
+ setValue(`${INPUT_LEVELS_GROUPER}.${parentIncludeName}.optionalOrRequired`, 'optional', {
+ shouldDirty: true
+ })
} else {
- setValue(`${INPUT_LEVELS_GROUPER}.${parentIncludeName}.optionalOrRequired`, 'required')
+ setValue(`${INPUT_LEVELS_GROUPER}.${parentIncludeName}.optionalOrRequired`, 'required', {
+ shouldDirty: true
+ })
}
}
}
diff --git a/src/sections/create-collection/collection-form/top-fields-section/ContactsField.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/ContactsField.tsx
similarity index 94%
rename from src/sections/create-collection/collection-form/top-fields-section/ContactsField.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/ContactsField.tsx
index ade98385b..9c149b46b 100644
--- a/src/sections/create-collection/collection-form/top-fields-section/ContactsField.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/ContactsField.tsx
@@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react'
import { Col, Form, Row } from '@iqss/dataverse-design-system'
import { Controller, UseControllerProps, useFieldArray, useFormContext } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
-import { DynamicFieldsButtons } from '../../../shared/form/DynamicFieldsButtons/DynamicFieldsButtons'
+import { DynamicFieldsButtons } from '@/sections/shared/form/DynamicFieldsButtons/DynamicFieldsButtons'
import cn from 'classnames'
import styles from './TopFieldsSection.module.scss'
@@ -11,7 +11,7 @@ interface ContactsFieldProps {
}
export const ContactsField = ({ rules }: ContactsFieldProps) => {
- const { t } = useTranslation('createCollection')
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm' })
const { control } = useFormContext()
const {
diff --git a/src/sections/create-collection/collection-form/top-fields-section/DescriptionField.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/DescriptionField.tsx
similarity index 96%
rename from src/sections/create-collection/collection-form/top-fields-section/DescriptionField.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/DescriptionField.tsx
index 4e07a4702..92e6a3b56 100644
--- a/src/sections/create-collection/collection-form/top-fields-section/DescriptionField.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/DescriptionField.tsx
@@ -5,7 +5,7 @@ import { Col, Form, Stack, Tooltip } from '@iqss/dataverse-design-system'
import styles from './TopFieldsSection.module.scss'
export const DescriptionField = () => {
- const { t } = useTranslation('createCollection')
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm' })
const { control } = useFormContext()
const HtmlWordTooltip = ({ children }: { children: ReactNode }) => (
diff --git a/src/sections/create-collection/collection-form/top-fields-section/IdentifierField.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/IdentifierField.tsx
similarity index 97%
rename from src/sections/create-collection/collection-form/top-fields-section/IdentifierField.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/IdentifierField.tsx
index 12c6ebc40..3e22d0657 100644
--- a/src/sections/create-collection/collection-form/top-fields-section/IdentifierField.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/IdentifierField.tsx
@@ -22,7 +22,7 @@ interface IdentifierFieldProps {
}
export const IdentifierField = ({ rules }: IdentifierFieldProps) => {
- const { t } = useTranslation('createCollection')
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm' })
const { control, setValue } = useFormContext()
const nameFieldValue = useWatch({ name: 'name' }) as string
diff --git a/src/sections/create-collection/collection-form/top-fields-section/TopFieldsSection.module.scss b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/TopFieldsSection.module.scss
similarity index 100%
rename from src/sections/create-collection/collection-form/top-fields-section/TopFieldsSection.module.scss
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/TopFieldsSection.module.scss
diff --git a/src/sections/create-collection/collection-form/top-fields-section/TopFieldsSection.tsx b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/TopFieldsSection.tsx
similarity index 79%
rename from src/sections/create-collection/collection-form/top-fields-section/TopFieldsSection.tsx
rename to src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/TopFieldsSection.tsx
index 9ab451257..8de181dcc 100644
--- a/src/sections/create-collection/collection-form/top-fields-section/TopFieldsSection.tsx
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/TopFieldsSection.tsx
@@ -1,17 +1,21 @@
import { Controller, UseControllerProps, useFormContext } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import { Col, Form, Row } from '@iqss/dataverse-design-system'
+import { Validator } from '@/shared/helpers/Validator'
import {
- collectionTypeOptions,
- collectionStorageOptions
-} from '../../../../collection/domain/useCases/DTOs/CollectionDTO'
-import { Validator } from '../../../../shared/helpers/Validator'
+ collectionStorageOptions,
+ collectionTypeOptions
+} from '@/collection/domain/useCases/DTOs/CollectionDTO'
import { ContactsField } from './ContactsField'
import { IdentifierField } from './IdentifierField'
import { DescriptionField } from './DescriptionField'
-export const TopFieldsSection = () => {
- const { t } = useTranslation('createCollection')
+interface TopFieldsSectionProps {
+ isEditingRootCollection: boolean
+}
+
+export const TopFieldsSection = ({ isEditingRootCollection }: TopFieldsSectionProps) => {
+ const { t } = useTranslation('shared', { keyPrefix: 'collectionForm' })
const { control } = useFormContext()
const hostCollectionRules: UseControllerProps['rules'] = {
@@ -50,33 +54,36 @@ export const TopFieldsSection = () => {
return (
- {/* Host Collection */}
-
-
-
- {t('fields.hostCollection.label')}
-
- (
-
-
- {error?.message}
-
- )}
- />
-
-
+ {/* Host Collection - Not shown if editing root collection */}
+
+ {!isEditingRootCollection && (
+
+
+
+ {t('fields.hostCollection.label')}
+
+ (
+
+
+ {error?.message}
+
+ )}
+ />
+
+
+ )}
{/* Name & Affiliation */}
diff --git a/src/sections/shared/form/EditCreateCollectionForm/collection-form/useSubmitCollection.ts b/src/sections/shared/form/EditCreateCollectionForm/collection-form/useSubmitCollection.ts
new file mode 100644
index 000000000..7c3fc80a0
--- /dev/null
+++ b/src/sections/shared/form/EditCreateCollectionForm/collection-form/useSubmitCollection.ts
@@ -0,0 +1,166 @@
+import { useState } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { WriteError } from '@iqss/dataverse-client-javascript'
+import { CollectionFormData, CollectionFormValuesOnSubmit } from '../types'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import {
+ EditCreateCollectionFormMode,
+ FACET_IDS_FIELD,
+ INPUT_LEVELS_GROUPER,
+ METADATA_BLOCKS_NAMES_GROUPER,
+ USE_FACETS_FROM_PARENT,
+ USE_FIELDS_FROM_PARENT
+} from '../EditCreateCollectionForm'
+import { CollectionDTO } from '@/collection/domain/useCases/DTOs/CollectionDTO'
+import { createCollection } from '@/collection/domain/useCases/createCollection'
+import { editCollection } from '@/collection/domain/useCases/editCollection'
+import { RouteWithParams } from '@/sections/Route.enum'
+import { JSDataverseWriteErrorHandler } from '@/shared/helpers/JSDataverseWriteErrorHandler'
+import { CollectionFormHelper } from '../CollectionFormHelper'
+import { FormState } from 'react-hook-form'
+
+export enum SubmissionStatus {
+ NotSubmitted = 'NotSubmitted',
+ IsSubmitting = 'IsSubmitting',
+ SubmitComplete = 'SubmitComplete',
+ Errored = 'Errored'
+}
+
+type UseSubmitCollectionReturnType =
+ | {
+ submissionStatus:
+ | SubmissionStatus.NotSubmitted
+ | SubmissionStatus.IsSubmitting
+ | SubmissionStatus.SubmitComplete
+ submitForm: (formData: CollectionFormData) => void
+ submitError: null
+ }
+ | {
+ submissionStatus: SubmissionStatus.Errored
+ submitForm: (formData: CollectionFormData) => void
+ submitError: string
+ }
+
+export function useSubmitCollection(
+ mode: EditCreateCollectionFormMode,
+ collectionIdOrParentCollectionId: string,
+ collectionRepository: CollectionRepository,
+ isEditingRootCollection: boolean,
+ formDirtyFields: FormState['dirtyFields'],
+ onSubmitErrorCallback: () => void
+): UseSubmitCollectionReturnType {
+ const navigate = useNavigate()
+
+ const [submissionStatus, setSubmissionStatus] = useState(
+ SubmissionStatus.NotSubmitted
+ )
+ const [submitError, setSubmitError] = useState(null)
+
+ const submitForm = (formData: CollectionFormValuesOnSubmit): void => {
+ // setSubmissionStatus(SubmissionStatus.IsSubmitting)
+
+ const contactsDTO = formData.contacts.map((contact) => contact.value)
+
+ const metadataBlockNamesDTO =
+ CollectionFormHelper.formatFormMetadataBlockNamesToMetadataBlockNamesDTO(
+ formData[METADATA_BLOCKS_NAMES_GROUPER]
+ )
+
+ const inputLevelsDTO = CollectionFormHelper.formatFormInputLevelsToInputLevelsDTO(
+ metadataBlockNamesDTO,
+ formData[INPUT_LEVELS_GROUPER]
+ )
+
+ const facetIdsDTO = formData.facetIds.map((facet) => facet.value)
+
+ const useFieldsFromParentChecked = formData[USE_FIELDS_FROM_PARENT]
+
+ const useFacetsFromParentChecked = formData[USE_FACETS_FROM_PARENT]
+
+ const hasMetadataBlockNamesChangedFromDefaultValue =
+ formDirtyFields[METADATA_BLOCKS_NAMES_GROUPER] !== undefined
+
+ const hasInputLevelsChangedFromDefaultValue =
+ formDirtyFields[INPUT_LEVELS_GROUPER] !== undefined
+
+ const hasFacetIdsChangedFromDefaultValue = formDirtyFields[FACET_IDS_FIELD] !== undefined
+
+ const shouldSendMetadataBlockNamesAndInputLevels =
+ CollectionFormHelper.defineShouldSendMetadataBlockNamesAndInputLevels(
+ useFieldsFromParentChecked,
+ isEditingRootCollection,
+ hasMetadataBlockNamesChangedFromDefaultValue,
+ hasInputLevelsChangedFromDefaultValue,
+ mode
+ )
+
+ const shouldSendFacetIds = CollectionFormHelper.defineShouldSendFacetIds(
+ useFacetsFromParentChecked,
+ isEditingRootCollection,
+ hasFacetIdsChangedFromDefaultValue,
+ mode
+ )
+
+ const newOrUpdatedCollection: CollectionDTO = {
+ name: formData.name,
+ alias: formData.alias,
+ type: formData.type,
+ affiliation: formData.affiliation,
+ description: formData.description,
+ contacts: contactsDTO,
+ metadataBlockNames: shouldSendMetadataBlockNamesAndInputLevels
+ ? metadataBlockNamesDTO
+ : undefined,
+ inputLevels: shouldSendMetadataBlockNamesAndInputLevels ? inputLevelsDTO : undefined,
+ facetIds: shouldSendFacetIds ? facetIdsDTO : undefined
+ }
+
+ if (mode === 'create') {
+ createCollection(
+ collectionRepository,
+ newOrUpdatedCollection,
+ collectionIdOrParentCollectionId
+ )
+ .then(() => {
+ setSubmitError(null)
+ setSubmissionStatus(SubmissionStatus.SubmitComplete)
+
+ navigate(RouteWithParams.COLLECTIONS(newOrUpdatedCollection.alias), {
+ state: { created: true }
+ })
+ return
+ })
+ .catch((err: WriteError) => {
+ const error = new JSDataverseWriteErrorHandler(err)
+ const formattedError = error.getReasonWithoutStatusCode() ?? error.getErrorMessage()
+ setSubmitError(formattedError)
+ setSubmissionStatus(SubmissionStatus.Errored)
+ onSubmitErrorCallback()
+ })
+ } else {
+ editCollection(collectionRepository, newOrUpdatedCollection, collectionIdOrParentCollectionId)
+ .then(() => {
+ setSubmitError(null)
+ setSubmissionStatus(SubmissionStatus.SubmitComplete)
+
+ navigate(RouteWithParams.COLLECTIONS(newOrUpdatedCollection.alias), {
+ state: { edited: true }
+ })
+ return
+ })
+ .catch((err: WriteError) => {
+ const error = new JSDataverseWriteErrorHandler(err)
+ const formattedError = error.getReasonWithoutStatusCode() ?? error.getErrorMessage()
+ setSubmitError(formattedError)
+ setSubmissionStatus(SubmissionStatus.Errored)
+ onSubmitErrorCallback()
+ })
+ }
+ }
+
+ return {
+ submissionStatus,
+ submitForm,
+ submitError
+ } as UseSubmitCollectionReturnType
+}
diff --git a/src/sections/shared/form/EditCreateCollectionForm/types.ts b/src/sections/shared/form/EditCreateCollectionForm/types.ts
new file mode 100644
index 000000000..27970720e
--- /dev/null
+++ b/src/sections/shared/form/EditCreateCollectionForm/types.ts
@@ -0,0 +1,73 @@
+import { CollectionStorage } from '@/collection/domain/useCases/DTOs/CollectionDTO'
+import {
+ MetadataBlockInfo,
+ MetadataBlockName,
+ MetadataField
+} from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import {
+ INPUT_LEVELS_GROUPER,
+ METADATA_BLOCKS_NAMES_GROUPER,
+ USE_FACETS_FROM_PARENT,
+ USE_FIELDS_FROM_PARENT
+} from './EditCreateCollectionForm'
+import { CollectionType } from '@/collection/domain/models/CollectionType'
+
+export type CollectionFormData = {
+ hostCollection: string | null
+ name: string
+ affiliation: string
+ alias: string
+ storage: CollectionStorage
+ type: CollectionType | ''
+ description: string
+ contacts: { value: string }[]
+ [USE_FIELDS_FROM_PARENT]: boolean
+ [METADATA_BLOCKS_NAMES_GROUPER]: CollectionFormMetadataBlocks
+ [INPUT_LEVELS_GROUPER]: FormattedCollectionInputLevels
+ [USE_FACETS_FROM_PARENT]: boolean
+ facetIds: CollectionFormFacet[]
+}
+
+export type CollectionFormMetadataBlocks = Record
+
+export type FormattedCollectionInputLevels = {
+ [key: string]: {
+ include: boolean
+ optionalOrRequired: CollectionFormInputLevelValue
+ parentBlockName: MetadataBlockName
+ }
+}
+
+export type FormattedCollectionInputLevelsWithoutParentBlockName = {
+ [K in keyof FormattedCollectionInputLevels]: Omit<
+ FormattedCollectionInputLevels[K],
+ 'parentBlockName'
+ >
+}
+
+export const CollectionFormInputLevelOptions = {
+ OPTIONAL: 'optional',
+ REQUIRED: 'required'
+} as const
+
+export type CollectionFormInputLevelValue =
+ (typeof CollectionFormInputLevelOptions)[keyof typeof CollectionFormInputLevelOptions]
+
+export type CollectionFormFacet = {
+ value: string
+ label: string
+ id: string
+}
+
+export type MetadataFieldWithParentBlockInfo = MetadataField & {
+ parentBlockInfo: Pick
+}
+
+// On the submit function callback, type is CollectionType as type field is required and wont never be ""
+export type CollectionFormValuesOnSubmit = Omit & {
+ type: CollectionType
+}
+
+export interface CollectionFormContactValue {
+ value: string
+}
diff --git a/src/sections/shared/form/RequiredFieldText/RequiredFieldText.tsx b/src/sections/shared/form/RequiredFieldText/RequiredFieldText.tsx
index 9c218aca6..67d1498f8 100644
--- a/src/sections/shared/form/RequiredFieldText/RequiredFieldText.tsx
+++ b/src/sections/shared/form/RequiredFieldText/RequiredFieldText.tsx
@@ -1,13 +1,13 @@
-import { RequiredInputSymbol } from '@iqss/dataverse-design-system'
+import { Form, RequiredInputSymbol } from '@iqss/dataverse-design-system'
import { useTranslation } from 'react-i18next'
export function RequiredFieldText() {
const { t } = useTranslation('shared')
return (
-
+
{t('asterisksIndicateRequiredFields')}
-
+
)
}
diff --git a/src/sections/shared/pagination/PaginationControls.tsx b/src/sections/shared/pagination/PaginationControls.tsx
index dcbd57da3..82f56ee8e 100644
--- a/src/sections/shared/pagination/PaginationControls.tsx
+++ b/src/sections/shared/pagination/PaginationControls.tsx
@@ -42,6 +42,8 @@ export function PaginationControls({
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(() => {
@@ -52,10 +54,14 @@ export function PaginationControls({
setSearchParams(searchParams)
}
}
+ // 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])
useEffect(() => {
@@ -67,6 +73,8 @@ export function PaginationControls({
goToPage(page)
}
}
+ // 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
}, [searchParams])
if (paginationInfo.totalPages < MINIMUM_NUMBER_OF_PAGES_TO_DISPLAY_PAGINATION) {
diff --git a/src/sections/create-collection/useGetAllFacetableMetadataFields.ts b/src/shared/hooks/useGetAllFacetableMetadataFields.ts
similarity index 81%
rename from src/sections/create-collection/useGetAllFacetableMetadataFields.ts
rename to src/shared/hooks/useGetAllFacetableMetadataFields.ts
index 30bb02b31..591cd949c 100644
--- a/src/sections/create-collection/useGetAllFacetableMetadataFields.ts
+++ b/src/shared/hooks/useGetAllFacetableMetadataFields.ts
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'
-import { MetadataBlockInfoRepository } from '../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
-import { MetadataField } from '../../metadata-block-info/domain/models/MetadataBlockInfo'
-import { getAllFacetableMetadataFields } from '../../metadata-block-info/domain/useCases/getAllFacetableMetadataFields'
+import { MetadataField } from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { getAllFacetableMetadataFields } from '@/metadata-block-info/domain/useCases/getAllFacetableMetadataFields'
interface Props {
metadataBlockInfoRepository: MetadataBlockInfoRepository
diff --git a/src/sections/create-collection/useGetAllMetadataBlocksInfo.tsx b/src/shared/hooks/useGetAllMetadataBlocksInfo.tsx
similarity index 77%
rename from src/sections/create-collection/useGetAllMetadataBlocksInfo.tsx
rename to src/shared/hooks/useGetAllMetadataBlocksInfo.tsx
index c6545a429..1910ea98d 100644
--- a/src/sections/create-collection/useGetAllMetadataBlocksInfo.tsx
+++ b/src/shared/hooks/useGetAllMetadataBlocksInfo.tsx
@@ -1,8 +1,8 @@
import { useEffect, useState } from 'react'
-import { MetadataBlockInfoRepository } from '../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
-import { MetadataBlockInfo } from '../../metadata-block-info/domain/models/MetadataBlockInfo'
-import { getAllMetadataBlocksInfo } from '../../metadata-block-info/domain/useCases/getAllMetadataBlocksInfo'
-import { MetadataFieldsHelper } from '../shared/form/DatasetMetadataForm/MetadataFieldsHelper'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { getAllMetadataBlocksInfo } from '@/metadata-block-info/domain/useCases/getAllMetadataBlocksInfo'
+import { MetadataFieldsHelper } from '@/sections/shared/form/DatasetMetadataForm/MetadataFieldsHelper'
+import { MetadataBlockInfo } from '@/metadata-block-info/domain/models/MetadataBlockInfo'
interface Props {
metadataBlockInfoRepository: MetadataBlockInfoRepository
diff --git a/src/sections/create-collection/useGetCollectionFacets.tsx b/src/shared/hooks/useGetCollectionFacets.tsx
similarity index 83%
rename from src/sections/create-collection/useGetCollectionFacets.tsx
rename to src/shared/hooks/useGetCollectionFacets.tsx
index 38535ee3c..960759bd9 100644
--- a/src/sections/create-collection/useGetCollectionFacets.tsx
+++ b/src/shared/hooks/useGetCollectionFacets.tsx
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'
-import { CollectionFacet } from '../../collection/domain/models/CollectionFacet'
-import { CollectionRepository } from '../../collection/domain/repositories/CollectionRepository'
-import { getCollectionFacets } from '../../collection/domain/useCases/getCollectionFacets'
+import { CollectionFacet } from '@/collection/domain/models/CollectionFacet'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { getCollectionFacets } from '@/collection/domain/useCases/getCollectionFacets'
interface Props {
collectionId: string
diff --git a/src/sections/create-collection/useGetCollectionMetadataBlocksInfo.tsx b/src/shared/hooks/useGetCollectionMetadataBlocksInfo.tsx
similarity index 80%
rename from src/sections/create-collection/useGetCollectionMetadataBlocksInfo.tsx
rename to src/shared/hooks/useGetCollectionMetadataBlocksInfo.tsx
index d196d96bc..9e260df89 100644
--- a/src/sections/create-collection/useGetCollectionMetadataBlocksInfo.tsx
+++ b/src/shared/hooks/useGetCollectionMetadataBlocksInfo.tsx
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'
-import { MetadataBlockInfoRepository } from '../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
-import { getMetadataBlockInfoByCollectionId } from '../../metadata-block-info/domain/useCases/getMetadataBlockInfoByCollectionId'
-import { MetadataBlockInfo } from '../../metadata-block-info/domain/models/MetadataBlockInfo'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { getMetadataBlockInfoByCollectionId } from '@/metadata-block-info/domain/useCases/getMetadataBlockInfoByCollectionId'
+import { MetadataBlockInfo } from '@/metadata-block-info/domain/models/MetadataBlockInfo'
interface Props {
collectionId: string
diff --git a/src/stories/collection/Collection.stories.tsx b/src/stories/collection/Collection.stories.tsx
index f48b6235d..66a82c636 100644
--- a/src/stories/collection/Collection.stories.tsx
+++ b/src/stories/collection/Collection.stories.tsx
@@ -97,3 +97,17 @@ export const Published: Story = {
/>
)
}
+
+export const Edited: Story = {
+ decorators: [WithLoggedInUser],
+ render: () => (
+
+ )
+}
diff --git a/src/stories/collection/CollectionLoadingMockRepository.ts b/src/stories/collection/CollectionLoadingMockRepository.ts
index 71e63e09e..5bda0750b 100644
--- a/src/stories/collection/CollectionLoadingMockRepository.ts
+++ b/src/stories/collection/CollectionLoadingMockRepository.ts
@@ -1,10 +1,11 @@
-import { CollectionDTO, CollectionUserPermissions } from '@iqss/dataverse-client-javascript'
+import { CollectionUserPermissions } from '@iqss/dataverse-client-javascript'
import { Collection } from '../../collection/domain/models/Collection'
import { CollectionMockRepository } from './CollectionMockRepository'
import { CollectionFacet } from '../../collection/domain/models/CollectionFacet'
import { CollectionItemsPaginationInfo } from '@/collection/domain/models/CollectionItemsPaginationInfo'
import { CollectionItemSubset } from '@/collection/domain/models/CollectionItemSubset'
import { CollectionSearchCriteria } from '@/collection/domain/models/CollectionSearchCriteria'
+import { CollectionDTO } from '@/collection/domain/useCases/DTOs/CollectionDTO'
export class CollectionLoadingMockRepository extends CollectionMockRepository {
getById(_id?: string): Promise {
@@ -26,4 +27,8 @@ export class CollectionLoadingMockRepository extends CollectionMockRepository {
): Promise {
return new Promise(() => {})
}
+
+ edit(_collectionIdOrAlias: string, _updatedCollection: CollectionDTO): Promise {
+ return new Promise(() => {})
+ }
}
diff --git a/src/stories/collection/CollectionMockRepository.ts b/src/stories/collection/CollectionMockRepository.ts
index efb2e116a..1b45235e3 100644
--- a/src/stories/collection/CollectionMockRepository.ts
+++ b/src/stories/collection/CollectionMockRepository.ts
@@ -84,4 +84,12 @@ export class CollectionMockRepository implements CollectionRepository {
}, FakerHelper.loadingTimout())
})
}
+
+ edit(_collectionIdOrAlias: string, _updatedCollection: CollectionDTO): Promise {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve()
+ }, FakerHelper.loadingTimout())
+ })
+ }
}
diff --git a/src/stories/create-collection/CreateCollection.stories.tsx b/src/stories/create-collection/CreateCollection.stories.tsx
index f868a305d..053f8cba4 100644
--- a/src/stories/create-collection/CreateCollection.stories.tsx
+++ b/src/stories/create-collection/CreateCollection.stories.tsx
@@ -30,7 +30,7 @@ export const Default: Story = {
render: () => (
)
@@ -39,18 +39,18 @@ export const Loading: Story = {
render: () => (
)
}
-export const OwnerCollectionNotFound: Story = {
+export const ParentCollectionNotFound: Story = {
render: () => (
)
}
@@ -73,7 +73,7 @@ export const NotAllowedToAddCollection: Story = {
)
}
diff --git a/src/stories/edit-collection/EditCollection.stories.tsx b/src/stories/edit-collection/EditCollection.stories.tsx
new file mode 100644
index 000000000..44938f9ad
--- /dev/null
+++ b/src/stories/edit-collection/EditCollection.stories.tsx
@@ -0,0 +1,117 @@
+import { Meta, StoryObj } from '@storybook/react'
+import { EditCollection } from '@/sections/edit-collection/EditCollection'
+import { WithI18next } from '../WithI18next'
+import { WithLayout } from '../WithLayout'
+import { WithLoggedInUser } from '../WithLoggedInUser'
+import { CollectionMockRepository } from '../collection/CollectionMockRepository'
+import { CollectionLoadingMockRepository } from '../collection/CollectionLoadingMockRepository'
+import { NoCollectionMockRepository } from '../collection/NoCollectionMockRepository'
+import { CollectionMother } from '@tests/component/collection/domain/models/CollectionMother'
+import { FakerHelper } from '@tests/component/shared/FakerHelper'
+import { ROOT_COLLECTION_ALIAS } from '@tests/e2e-integration/shared/collection/ROOT_COLLECTION_ALIAS'
+import { UpwardHierarchyNodeMother } from '@tests/component/shared/hierarchy/domain/models/UpwardHierarchyNodeMother'
+import { MetadataBlockInfoMockRepository } from '../shared-mock-repositories/metadata-block-info/MetadataBlockInfoMockRepository'
+import { MetadataBlockInfoMockLoadingRepository } from '../shared-mock-repositories/metadata-block-info/MetadataBlockInfoMockLoadingRepository'
+import { MetadataBlockInfoMockErrorRepository } from '../shared-mock-repositories/metadata-block-info/MetadataBlockInfoMockErrorRepository'
+
+const meta: Meta = {
+ title: 'Pages/Edit Collection',
+ component: EditCollection,
+ decorators: [WithI18next, WithLayout, WithLoggedInUser],
+ parameters: {
+ // Sets the delay for all stories.
+ chromatic: { delay: 15000, pauseAnimationAtEnd: true }
+ }
+}
+export default meta
+type Story = StoryObj
+
+export const Default: Story = {
+ render: () => {
+ const collectionRepo = new CollectionMockRepository()
+ collectionRepo.getById = () => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ CollectionMother.create({
+ id: 'science',
+ isReleased: true,
+ name: 'Collection Name',
+ description: 'We do all the science.',
+ affiliation: 'Scientific Research University',
+ hierarchy: UpwardHierarchyNodeMother.createCollection({
+ id: 'science',
+ name: 'Collection Name',
+ parent: UpwardHierarchyNodeMother.createCollection({
+ id: ROOT_COLLECTION_ALIAS,
+ name: 'Root'
+ })
+ })
+ })
+ )
+ }, FakerHelper.loadingTimout())
+ })
+ }
+
+ return (
+
+ )
+ }
+}
+
+export const EditingRoot: Story = {
+ render: () => (
+
+ )
+}
+
+export const Loading: Story = {
+ render: () => (
+
+ )
+}
+
+export const CollectionNotFound: Story = {
+ render: () => (
+
+ )
+}
+
+const collectionRepositoryWithoutPermissionsToCreateCollection = new CollectionMockRepository()
+collectionRepositoryWithoutPermissionsToCreateCollection.getUserPermissions = () => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ CollectionMother.createUserPermissions({
+ canEditCollection: false
+ })
+ )
+ }, FakerHelper.loadingTimout())
+ })
+}
+
+export const NotAllowedToEditCollection: Story = {
+ render: () => (
+
+ )
+}
diff --git a/tests/component/collection/domain/models/CollectionMother.ts b/tests/component/collection/domain/models/CollectionMother.ts
index d539bd432..069927809 100644
--- a/tests/component/collection/domain/models/CollectionMother.ts
+++ b/tests/component/collection/domain/models/CollectionMother.ts
@@ -3,6 +3,7 @@ import { faker } from '@faker-js/faker'
import { FakerHelper } from '../../../shared/FakerHelper'
import { UpwardHierarchyNodeMother } from '../../../shared/hierarchy/domain/models/UpwardHierarchyNodeMother'
import { CollectionUserPermissions } from '../../../../../src/collection/domain/models/CollectionUserPermissions'
+import { CollectionType } from '@/collection/domain/models/CollectionType'
export class CollectionMother {
static create(props?: Partial): Collection {
@@ -16,6 +17,10 @@ export class CollectionMother {
affiliation: faker.datatype.boolean() ? faker.lorem.words(3) : undefined,
hierarchy: UpwardHierarchyNodeMother.createCollection(),
inputLevels: [],
+ contacts: [],
+ type: CollectionType.UNCATEGORIZED,
+ isMetadataBlockRoot: true,
+ isFacetRoot: true,
...props
}
}
diff --git a/tests/component/sections/collection/Collection.spec.tsx b/tests/component/sections/collection/Collection.spec.tsx
index fdb80ac37..34ee54a02 100644
--- a/tests/component/sections/collection/Collection.spec.tsx
+++ b/tests/component/sections/collection/Collection.spec.tsx
@@ -182,7 +182,7 @@ describe('Collection page', () => {
cy.mountAuthenticated(
{
expect(collectionQueryParams.typesQuery).to.deep.equal(undefined)
expect(collectionQueryParams.pageQuery).to.equal(1)
})
+
+ describe('isRootCollection', () => {
+ it('returns true when collection is root collection', () => {
+ const collectionHierarchy: UpwardHierarchyNode = new UpwardHierarchyNode(
+ 'Root',
+ DvObjectType.COLLECTION,
+ 'root'
+ )
+ expect(CollectionHelper.isRootCollection(collectionHierarchy)).to.be.true
+ })
+
+ it('returns false when collection is not root collection', () => {
+ const collectionHierarchy: UpwardHierarchyNode = new UpwardHierarchyNode(
+ 'Subcollection',
+ DvObjectType.COLLECTION,
+ 'subcollection',
+ undefined,
+ undefined,
+ undefined,
+ new UpwardHierarchyNode('Root', DvObjectType.COLLECTION, 'root')
+ )
+ expect(CollectionHelper.isRootCollection(collectionHierarchy)).to.be.false
+ })
+ })
+
+ describe('getParentCollection', () => {
+ it('returns parent collection when collection has parent', () => {
+ const parentCollection: UpwardHierarchyNode = new UpwardHierarchyNode(
+ 'Root',
+ DvObjectType.COLLECTION,
+ 'root'
+ )
+ const collectionHierarchy: UpwardHierarchyNode = new UpwardHierarchyNode(
+ 'Subcollection',
+ DvObjectType.COLLECTION,
+ 'subcollection',
+ undefined,
+ undefined,
+ undefined,
+ parentCollection
+ )
+ expect(CollectionHelper.getParentCollection(collectionHierarchy)).to.deep.equal(
+ parentCollection
+ )
+ })
+
+ it('returns undefined when collection does not have parent', () => {
+ const collectionHierarchy: UpwardHierarchyNode = new UpwardHierarchyNode(
+ 'Root',
+ DvObjectType.COLLECTION,
+ 'root'
+ )
+ expect(CollectionHelper.getParentCollection(collectionHierarchy)).to.be.undefined
+ })
+ })
})
diff --git a/tests/component/sections/collection/collection-publish/PublishCollectionModal.spec.tsx b/tests/component/sections/collection/collection-publish/PublishCollectionModal.spec.tsx
index f6051500b..1a68515ba 100644
--- a/tests/component/sections/collection/collection-publish/PublishCollectionModal.spec.tsx
+++ b/tests/component/sections/collection/collection-publish/PublishCollectionModal.spec.tsx
@@ -23,6 +23,30 @@ describe('PublishCollectionModal', () => {
// Check if the error message is displayed
cy.contains(errorMessage).should('exist')
})
+
+ it('displays the fallback error message when publishCollection fails without an error message', () => {
+ const handleClose = cy.stub()
+ const repository = {} as CollectionRepository // Mock the repository as needed
+ repository.publish = cy.stub().as('repositoryPublish').rejects('Unknown error')
+
+ cy.mountAuthenticated(
+
+ )
+
+ // Trigger the Publish action
+ cy.findByRole('button', { name: 'Continue' }).click()
+
+ // Check if the error message is displayed
+ cy.contains(
+ 'Something went wrong while trying to publish the collection. Please try again later.'
+ ).should('exist')
+ })
+
it('renders the PublishDatasetModal and triggers submitPublish on button click', () => {
const handleClose = cy.stub()
const repository = {} as CollectionRepository // Mock the repository as needed
diff --git a/tests/component/sections/collection/edit-collection-dropdown/EditCollectionDropdown.spec.tsx b/tests/component/sections/collection/edit-collection-dropdown/EditCollectionDropdown.spec.tsx
new file mode 100644
index 000000000..7e5089ba1
--- /dev/null
+++ b/tests/component/sections/collection/edit-collection-dropdown/EditCollectionDropdown.spec.tsx
@@ -0,0 +1,66 @@
+import { EditCollectionDropdown } from '@/sections/collection/edit-collection-dropdown/EditCollectionDropdown'
+import { CollectionMother } from '@tests/component/collection/domain/models/CollectionMother'
+import { UpwardHierarchyNodeMother } from '@tests/component/shared/hierarchy/domain/models/UpwardHierarchyNodeMother'
+
+const PARENT_COLLECTION_ID = 'root'
+const PARENT_COLLECTION_NAME = 'Root'
+const PARENT_COLLECTION_CONTACT_EMAIL = 'root@test.com'
+
+const rootCollection = CollectionMother.create({
+ id: PARENT_COLLECTION_ID,
+ name: PARENT_COLLECTION_NAME,
+ affiliation: undefined,
+ contacts: [{ email: PARENT_COLLECTION_CONTACT_EMAIL, displayOrder: 0 }],
+ hierarchy: UpwardHierarchyNodeMother.createCollection({
+ id: PARENT_COLLECTION_ID,
+ name: PARENT_COLLECTION_NAME
+ }),
+ isFacetRoot: true,
+ isMetadataBlockRoot: true
+})
+
+const openDropdown = () => cy.findByRole('button', { name: /Edit/i }).click()
+
+describe('EditCollectionDropdown', () => {
+ describe('dropdown header', () => {
+ it('shows the collection name and id, but not affiliaton if not present', () => {
+ cy.mountAuthenticated()
+
+ openDropdown()
+
+ cy.findByText(rootCollection.name).should('exist')
+ cy.findByText(rootCollection.id).should('exist')
+ })
+
+ it('shows the collection name, id, and also affiliation if present', () => {
+ const TEST_AFFILIATION = 'Test Affiliation'
+ const rootCollectionWithAffiliation = CollectionMother.create({
+ id: PARENT_COLLECTION_ID,
+ name: PARENT_COLLECTION_NAME,
+ contacts: [{ email: PARENT_COLLECTION_CONTACT_EMAIL, displayOrder: 0 }],
+ affiliation: TEST_AFFILIATION,
+ hierarchy: UpwardHierarchyNodeMother.createCollection({
+ id: PARENT_COLLECTION_ID,
+ name: PARENT_COLLECTION_NAME
+ }),
+ isFacetRoot: true,
+ isMetadataBlockRoot: true
+ })
+ cy.mountAuthenticated()
+
+ openDropdown()
+
+ cy.findByText(rootCollectionWithAffiliation.name).should('exist')
+ cy.findByText(`(${TEST_AFFILIATION})`).should('exist')
+ cy.findByText(rootCollectionWithAffiliation.id).should('exist')
+ })
+ })
+
+ it('clicks the general info button', () => {
+ cy.mountAuthenticated()
+
+ openDropdown()
+
+ cy.findByRole('button', { name: /General Information/i }).click()
+ })
+})
diff --git a/tests/component/sections/create-collection/CollectionForm.spec.tsx b/tests/component/sections/create-collection/CollectionForm.spec.tsx
deleted file mode 100644
index 0dd7f9f78..000000000
--- a/tests/component/sections/create-collection/CollectionForm.spec.tsx
+++ /dev/null
@@ -1,1072 +0,0 @@
-import {
- CollectionForm,
- CollectionFormData,
- CollectionFormFacet,
- FormattedCollectionInputLevels,
- FormattedCollectionInputLevelsWithoutParentBlockName,
- INPUT_LEVELS_GROUPER,
- METADATA_BLOCKS_NAMES_GROUPER,
- USE_FACETS_FROM_PARENT,
- USE_FIELDS_FROM_PARENT
-} from '../../../../src/sections/create-collection/collection-form/CollectionForm'
-import { CollectionRepository } from '../../../../src/collection/domain/repositories/CollectionRepository'
-import { UserRepository } from '../../../../src/users/domain/repositories/UserRepository'
-import { CollectionMother } from '../../collection/domain/models/CollectionMother'
-import { UserMother } from '../../users/domain/models/UserMother'
-import { collectionNameToAlias } from '../../../../src/sections/create-collection/collection-form/top-fields-section/IdentifierField'
-import { MetadataBlockInfoMother } from '../../metadata-block-info/domain/models/MetadataBlockInfoMother'
-import { CollectionFormHelper } from '../../../../src/sections/create-collection/collection-form/CollectionFormHelper'
-import { MetadataBlockName } from '../../../../src/metadata-block-info/domain/models/MetadataBlockInfo'
-import { CollectionFacetMother } from '../../collection/domain/models/CollectionFacetMother'
-import { CollectionDTO } from '@/collection/domain/useCases/DTOs/CollectionDTO'
-
-const collectionRepository: CollectionRepository = {} as CollectionRepository
-
-const OWNER_COLLECTION_ID = 'root'
-
-const COLLECTION_NAME = 'Collection Name'
-const collection = CollectionMother.create({ name: COLLECTION_NAME })
-
-const VIEW_AND_EDIT_FIELDS_LABEL = '[+] View fields + set as hidden, required, or optional'
-const VIEW_FIELDS_LABEL = '[+] View fields'
-
-const allMetadataBlocksMock = [
- MetadataBlockInfoMother.getCitationBlock(),
- MetadataBlockInfoMother.getGeospatialBlock(),
- MetadataBlockInfoMother.getAstrophysicsBlock(),
- MetadataBlockInfoMother.getBiomedicalBlock(),
- MetadataBlockInfoMother.getJournalBlock(),
- MetadataBlockInfoMother.getSocialScienceBlock()
-]
-
-const testUser = UserMother.create()
-const userRepository: UserRepository = {} as UserRepository
-
-const defaultCollectionName = `${testUser.displayName} Collection`
-
-const baseInputLevels: FormattedCollectionInputLevels =
- CollectionFormHelper.defineBaseInputLevels(allMetadataBlocksMock)
-
-const formattedCollectionInputLevels: FormattedCollectionInputLevelsWithoutParentBlockName =
- CollectionFormHelper.formatCollectiontInputLevels(collection?.inputLevels)
-
-const mergedInputLevels = CollectionFormHelper.mergeBaseAndDefaultInputLevels(
- baseInputLevels,
- formattedCollectionInputLevels
-)
-
-const defaultBlocksNames = {
- [MetadataBlockName.CITATION]: true,
- [MetadataBlockName.GEOSPATIAL]: false,
- [MetadataBlockName.SOCIAL_SCIENCE]: false,
- [MetadataBlockName.ASTROPHYSICS]: false,
- [MetadataBlockName.BIOMEDICAL]: false,
- [MetadataBlockName.JOURNAL]: false,
- [MetadataBlockName.COMPUTATIONAL_WORKFLOW]: false,
- [MetadataBlockName.CODE_META]: false
-}
-
-const defaultCollectionFacetsMock: CollectionFormFacet[] = CollectionFacetMother.createFacets().map(
- (facet) => ({
- id: facet.name,
- value: facet.name,
- label: facet.displayName
- })
-)
-
-const allFacetableMetadataFields = MetadataBlockInfoMother.getAllFacetableMetadataFields()
-
-const formDefaultValues: CollectionFormData = {
- hostCollection: collection.name,
- name: defaultCollectionName,
- alias: '',
- type: '',
- contacts: [{ value: testUser.email }],
- affiliation: testUser.affiliation ?? '',
- storage: 'S3',
- description: '',
- [USE_FIELDS_FROM_PARENT]: true,
- [METADATA_BLOCKS_NAMES_GROUPER]: defaultBlocksNames,
- [INPUT_LEVELS_GROUPER]: mergedInputLevels,
- [USE_FACETS_FROM_PARENT]: true,
- facetIds: defaultCollectionFacetsMock
-}
-
-describe('CollectionForm', () => {
- beforeEach(() => {
- collectionRepository.create = cy.stub().resolves(1)
- collectionRepository.getById = cy.stub().resolves(collection)
- userRepository.getAuthenticated = cy.stub().resolves(testUser)
- })
-
- it('should render the form', () => {
- cy.mountAuthenticated(
-
- )
-
- cy.findByTestId('collection-form').should('exist')
- })
- it('prefills the Host Collection field with current owner collection', () => {
- cy.mountAuthenticated(
-
- )
-
- cy.findByLabelText(/^Host Collection/i).should('have.value', COLLECTION_NAME)
- })
-
- it('pre-fills specific form fields with user data', () => {
- cy.mountAuthenticated(
-
- )
-
- cy.findByLabelText(/^Collection Name/i).should('have.value', defaultCollectionName)
-
- cy.findByLabelText(/^Affiliation/i).should('have.value', testUser.affiliation)
-
- cy.findByLabelText(/^Email/i).should('have.value', testUser.email)
- })
-
- it('submit button should be disabled when form has not been touched', () => {
- cy.customMount(
-
- )
-
- cy.findByRole('button', { name: 'Create Collection' }).should('be.disabled')
- })
-
- it('submit button should not be disabled when form has been touched', () => {
- cy.customMount(
-
- )
-
- cy.findByLabelText(/^Collection Name/i)
- .clear()
- .type('New Collection Name')
-
- cy.findByRole('button', { name: 'Create Collection' }).should('not.be.disabled')
- })
-
- it('shows error message when form is submitted with empty required fields', () => {
- cy.customMount(
-
- )
-
- // Change collection name so submit button is no longer disabled
- cy.findByLabelText(/^Collection Name/i)
- .clear()
- .type('New Collection Name')
-
- cy.findByRole('button', { name: 'Create Collection' }).click()
-
- cy.findByText('Category is required').should('exist')
- cy.findByText('Identifier is required').should('exist')
- })
-
- it('shows error message when form is submitted with invalid email', () => {
- cy.customMount(
-
- )
-
- cy.findByLabelText(/^Email/i)
- .clear()
- .type('invalid-email')
-
- cy.findByRole('button', { name: 'Create Collection' }).click()
-
- cy.findByText('Email is not a valid email').should('exist')
- })
- it('shows error message when form is submitted with invalid identifier', () => {
- cy.customMount(
-
- )
-
- cy.findByLabelText(/^Identifier/i).type('invalid identifier')
-
- cy.findByRole('button', { name: 'Create Collection' }).click()
-
- cy.findByText(/Identifier is not valid./).should('exist')
- })
-
- it('should not submit the form when pressing enter key if submit button is not focused', () => {
- cy.customMount(
-
- )
-
- // Select a Category option so submit button is not disabled
- cy.findByLabelText(/^Category/i).select(1)
-
- // Focus on the Identifier field that is empty and is required and press enter key
- cy.findByLabelText(/^Identifier/i)
- .focus()
- .type('{enter}')
-
- // Validation error shouldn't be shown as form wasn't submitted by pressing enter key on Identifier field
- cy.findByText('Identifier is required').should('not.exist')
- })
- it('should submit the form when pressing enter key if submit button is indeed focused', () => {
- cy.customMount(
-
- )
-
- // Select a Category option so submit button is not disabled
- cy.findByLabelText(/^Category/i).select(1)
-
- // To wait until button becomes enabled
- cy.wait(100)
-
- cy.findByRole('button', { name: 'Create Collection' }).focus().type('{enter}')
-
- // Validation error should be shown as form was submitted by pressing enter key on Identifier field
- cy.findByText('Identifier is required').should('exist')
- })
-
- it('submits a valid form and succeed', () => {
- cy.customMount(
-
- )
- // Accept suggestion
- cy.findByRole('button', { name: 'Apply suggestion' }).click()
- // Select a Category option
- cy.findByLabelText(/^Category/i).select(1)
-
- cy.findByRole('button', { name: 'Create Collection' }).click()
-
- cy.findByText('Error').should('not.exist')
- cy.findByText('Success!').should('exist')
- })
-
- it('submits a valid form and fails', () => {
- collectionRepository.create = cy.stub().rejects(new Error('Error creating collection'))
-
- cy.customMount(
-
- )
-
- // Accept suggestion
- cy.findByRole('button', { name: 'Apply suggestion' }).click()
- // Select a Category option
- cy.findByLabelText(/^Category/i).select(1)
-
- cy.findByRole('button', { name: 'Create Collection' }).click()
-
- cy.findByText('Error').should('exist')
- cy.findByText(/Error creating collection/).should('exist')
- cy.findByText('Success!').should('not.exist')
- })
-
- it('cancel button is clickable', () => {
- cy.customMount(
-
- )
-
- cy.findByText('Cancel').click()
- })
-
- describe('IdentifierField suggestion functionality', () => {
- it('should show to apply an identifier suggestion', () => {
- cy.customMount(
-
- )
-
- const aliasSuggestion = collectionNameToAlias(defaultCollectionName)
-
- cy.findByText(/Psst... try this/).should('exist')
- cy.findByText(/Psst... try this/).should('include.text', aliasSuggestion)
-
- cy.findByRole('button', { name: 'Apply suggestion' }).should('exist')
- })
-
- it('should apply suggestion when clicking the button and hide suggestion', () => {
- cy.customMount(
-
- )
-
- const aliasSuggestion = collectionNameToAlias(defaultCollectionName)
-
- cy.findByRole('button', { name: 'Apply suggestion' }).click()
-
- cy.findByLabelText(/^Identifier/i).should('have.value', aliasSuggestion)
-
- cy.findByText(/Psst... try this/).should('not.exist')
- })
-
- it('should not show suggestion when identifier is already the suggestion', () => {
- cy.customMount(
-
- )
-
- cy.findByText(/Psst... try this/).should('not.exist')
- })
-
- it('should not show suggestion if Collection Name is empty', () => {
- cy.customMount(
-
- )
-
- cy.findByText(/Psst... try this/).should('not.exist')
- })
- })
-
- describe('ContactsField functionality', () => {
- it('should add a new contact field when clicking the add button', () => {
- cy.customMount(
-
- )
-
- cy.findByLabelText('Add Email').click()
-
- cy.findAllByLabelText('Add Email').should('exist').should('have.length', 2)
- cy.findByLabelText('Remove Email').should('exist')
- })
-
- it('should remove a contact field when clicking the remove button', () => {
- cy.customMount(
-
- )
- cy.findAllByLabelText('Add Email').should('exist').should('have.length', 2)
- cy.findByLabelText('Remove Email').should('exist')
-
- cy.findByLabelText('Remove Email').click()
-
- cy.findByLabelText('Add Email').should('exist')
- cy.findByLabelText('Remove Email').should('not.exist')
- })
- })
-
- describe('MetadataFieldsSection functionality', () => {
- beforeEach(() => {
- cy.mountAuthenticated(
-
- )
-
- cy.get('[data-testid="use-fields-from-parent-checkbox"]').as('useFieldsFromParentCheckbox')
- })
-
- describe('Use fields from parent checkbox', () => {
- it('should be checked by default', () => {
- cy.get('@useFieldsFromParentCheckbox').should('be.checked')
- })
-
- it('should open the reset confirmation modal when unchecking and then checking the checkbox again', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- cy.get('@useFieldsFromParentCheckbox').check({ force: true })
-
- cy.findByText(/Reset Modifications/).should('exist')
- })
-
- it('should close the reset confirmation modal if cancelling without finally checking the checkbox', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- cy.get('@useFieldsFromParentCheckbox').check({ force: true })
-
- cy.findByText(/Reset Modifications/).should('exist')
-
- cy.findByTestId('confirm-reset-modal-cancel').click()
-
- cy.findByText(/Reset Modifications/).should('not.exist')
-
- cy.get('@useFieldsFromParentCheckbox').should('not.be.checked')
- })
-
- it('should reset the metadata fields when confirming the reset', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck()
-
- // Modify a field in citation block
- cy.findByRole('button', {
- name: VIEW_AND_EDIT_FIELDS_LABEL
- })
- .should('exist')
- .click()
-
- cy.findByLabelText('Subtitle').uncheck({ force: true })
-
- cy.findByLabelText('Subtitle').should('not.be.checked')
-
- cy.get('@useFieldsFromParentCheckbox').check({ force: true })
-
- cy.findByText(/Reset Modifications/).should('exist')
-
- cy.findByTestId('confirm-reset-modal-continue').click()
-
- cy.findByText(/Reset Modifications/).should('not.exist')
-
- cy.get('@useFieldsFromParentCheckbox').should('be.checked')
-
- // Check if field is back to its original state
- cy.findAllByRole('button', {
- name: VIEW_FIELDS_LABEL
- })
- .first()
- .click()
-
- cy.findByLabelText('Subtitle').should('be.checked')
- })
-
- it('should send metadataBlockNames and inputLevels as undefined if use fields from parent is checked', () => {
- const collectionRepository = {} as CollectionRepository
- collectionRepository.create = cy.stub().as('createCollection').resolves()
-
- cy.customMount(
-
- )
- // Accept suggestion
- cy.findByRole('button', { name: 'Apply suggestion' }).click()
- // Select a Category option
- cy.findByLabelText(/^Category/i).select(1)
-
- cy.findByRole('button', { name: 'Create Collection' }).click()
-
- cy.get('@createCollection').should((spy) => {
- const createCollectionSpy = spy as unknown as Cypress.Agent
- const collectionDTO = createCollectionSpy.getCall(0).args[0] as CollectionDTO
-
- const inputLevels = collectionDTO.inputLevels
- const metadataBlockNames = collectionDTO.metadataBlockNames
-
- expect(inputLevels).to.be.undefined
- expect(metadataBlockNames).to.be.undefined
- })
- })
-
- it('should not send metadataBlockNames and inputLevels as undefined if use fields from parent is unchecked', () => {
- const collectionRepository = {} as CollectionRepository
- collectionRepository.create = cy.stub().as('createCollection').resolves()
-
- cy.customMount(
-
- )
-
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- // Accept suggestion
- cy.findByRole('button', { name: 'Apply suggestion' }).click()
- // Select a Category option
- cy.findByLabelText(/^Category/i).select(1)
-
- cy.findByRole('button', { name: 'Create Collection' }).click()
-
- cy.get('@createCollection').should((spy) => {
- const createCollectionSpy = spy as unknown as Cypress.Agent
- const collectionDTO = createCollectionSpy.getCall(0).args[0] as CollectionDTO
-
- const inputLevels = collectionDTO.inputLevels
- const metadataBlockNames = collectionDTO.metadataBlockNames
-
- expect(inputLevels).to.not.be.undefined
- expect(metadataBlockNames).to.not.be.undefined
- })
- })
- })
-
- describe('InputLevelFieldRow', () => {
- it('On not composed fields - should unclude and include the field when checking the checkbox', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- // Open citation input levels
- cy.findByRole('button', {
- name: VIEW_AND_EDIT_FIELDS_LABEL
- })
- .should('exist')
- .click({ force: true })
-
- // First unclude the subtitle field
- cy.findByLabelText('Subtitle').uncheck({ force: true })
-
- cy.findByLabelText('Subtitle').should('not.be.checked')
-
- // Go to parent td of Subtitle field and find sibling td to check if Hidden checkbox is there
- cy.findByLabelText('Subtitle')
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Hidden').should('exist').should('be.checked')
- })
-
- // Second include the subtitle field
- cy.findByLabelText('Subtitle').check({ force: true })
- cy.findByLabelText('Subtitle').should('be.checked')
-
- // Go to parent td of Subtitle field and find sibling td to check if Hidden checkbox is not there anymore
- cy.findByLabelText('Subtitle')
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Hidden').should('not.exist')
- })
- })
-
- it('On composed fields - should unclude and include the child field when checking the checkbox', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- // Open citation input levels
- cy.findByRole('button', {
- name: VIEW_AND_EDIT_FIELDS_LABEL
- })
- .should('exist')
- .click()
-
- // First unclude the other identifier composed field
- cy.findByLabelText('Other Identifier').uncheck({ force: true })
-
- cy.findByLabelText('Other Identifier').should('not.be.checked')
-
- // Go to child fields rows and check if Hidden checkbox is there
- cy.findByText('Other Identifier Agency', { exact: true })
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Hidden').should('exist').should('be.checked')
- })
-
- cy.findByText('Other Identifier Identifier', { exact: true })
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Hidden').should('exist').should('be.checked')
- })
-
- // Second include the other identifier composed fields
- cy.findByLabelText('Other Identifier').check({ force: true })
- cy.findByLabelText('Other Identifier').should('be.checked')
-
- // Go to child fields rows and check if Hidden checkbox is not there anymore and they optional checkbox is checked
- cy.findByText('Other Identifier Agency', { exact: true })
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Hidden').should('not.exist')
- cy.findByLabelText('Optional').should('be.checked')
- })
-
- cy.findByText('Other Identifier Identifier', { exact: true })
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Hidden').should('not.exist')
- cy.findByLabelText('Optional').should('be.checked')
- })
- })
-
- it('On not composed fields - should select correctly the Required or Optional radios', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- // Open citation input levels
- cy.findByRole('button', {
- name: VIEW_AND_EDIT_FIELDS_LABEL
- })
- .should('exist')
- .click()
-
- // Go to parent td of Subtitle field and find sibling td to select the Required radio
- cy.findByLabelText('Subtitle')
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Required').should('exist').should('not.be.checked')
- cy.findByLabelText('Optional').should('exist').should('be.checked')
-
- cy.findByLabelText('Required').check({ force: true })
- cy.findByLabelText('Required').should('be.checked')
- cy.findByLabelText('Optional').should('not.be.checked')
-
- cy.findByLabelText('Optional').check({ force: true })
- cy.findByLabelText('Optional').should('be.checked')
- cy.findByLabelText('Required').should('not.be.checked')
- })
-
- // Second set the subtitle field as optional
- // cy.findByLabelText('Subtitle').uncheck({ force: true })
-
- // // Go to parent td of Subtitle field and find sibling td to check if Optional radio is there
- // cy.findByLabelText('Subtitle')
- // .closest('td')
- // .next()
- // .next()
- // .within(() => {
- // cy.findByLabelText('Optional').should('exist').should('be.checked')
- // })
- })
-
- it('On composed fields - should select correctly the Required or Optional radios of child fields', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- // Open citation input levels
- cy.findByRole('button', {
- name: VIEW_AND_EDIT_FIELDS_LABEL
- })
- .should('exist')
- .click()
-
- // Go to child fields row and check if Required radio is there and perform the check/uncheck
- cy.findByText('Other Identifier Identifier', { exact: true })
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Required').should('exist').should('not.be.checked')
- cy.findByLabelText('Optional').should('exist').should('be.checked')
-
- cy.findByLabelText('Required').check({ force: true })
- cy.findByLabelText('Required').should('be.checked')
- cy.findByLabelText('Optional').should('not.be.checked')
- })
-
- cy.findByText('Other Identifier Agency', { exact: true })
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Required').should('exist').should('not.be.checked')
- cy.findByLabelText('Optional').should('exist').should('be.checked')
-
- cy.findByLabelText('Required').check({ force: true })
- cy.findByLabelText('Required').should('be.checked')
- cy.findByLabelText('Optional').should('not.be.checked')
-
- cy.findByLabelText('Optional').check({ force: true })
- cy.findByLabelText('Optional').should('be.checked')
- cy.findByLabelText('Required').should('not.be.checked')
- })
-
- cy.findByText('Other Identifier Identifier', { exact: true })
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Optional').check({ force: true })
- })
-
- cy.findByText('Other Identifier Agency', { exact: true })
- .closest('td')
- .next()
- .within(() => {
- cy.findByLabelText('Required').should('exist').should('not.be.checked')
- cy.findByLabelText('Optional').should('exist').should('be.checked')
-
- cy.findByLabelText('Required').check({ force: true })
- cy.findByLabelText('Required').should('be.checked')
- cy.findByLabelText('Optional').should('not.be.checked')
-
- cy.findByLabelText('Optional').check({ force: true })
- cy.findByLabelText('Optional').should('be.checked')
- cy.findByLabelText('Required').should('not.be.checked')
- })
- })
- })
-
- describe('Opens input levels table correctly on different states', () => {
- it('should open the Citation input levels on view mode', () => {
- // cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- cy.findAllByRole('button', {
- name: VIEW_FIELDS_LABEL
- })
- .first()
- .should('exist')
- .click()
-
- cy.findByRole('table').should('exist').should('be.visible')
-
- cy.findByRole('table').within(() => {
- cy.findByText('Title').should('be.visible')
- cy.findByLabelText('Subtitle').should('be.visible').should('be.disabled')
- })
-
- // Close the table
- cy.findAllByLabelText('Hide input levels table').first().click()
-
- cy.findByRole('table').should('not.exist')
- })
-
- it('should open the Citation input levels on edit mode', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- cy.findByRole('button', {
- name: VIEW_AND_EDIT_FIELDS_LABEL
- })
- .should('exist')
- .click()
-
- cy.findByRole('table').should('exist').should('be.visible')
-
- cy.findByRole('table').within(() => {
- cy.findByText('Title').should('be.visible')
- cy.findByLabelText('Subtitle').should('be.visible').should('not.be.disabled')
- })
-
- // Close the table
- cy.findAllByLabelText('Hide input levels table').first().click()
-
- cy.findByRole('table').should('not.exist')
- })
-
- it('should enable fields when opening an input levels table on view mode and checking the block name checkbox', () => {
- cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
-
- cy.findAllByRole('button', {
- name: VIEW_FIELDS_LABEL
- })
- .first()
- .should('exist')
- .click()
-
- cy.findByRole('table').should('exist').should('be.visible')
-
- cy.findByRole('table').within(() => {
- cy.findByLabelText('Geographic Unit').should('exist').should('be.disabled')
- })
-
- // Now check the Geospatial block name checkbox
- cy.findByLabelText('Geospatial Metadata').check({ force: true })
-
- cy.findByRole('table').should('exist').should('be.visible')
-
- cy.findByRole('table').within(() => {
- cy.findByLabelText('Geographic Unit').should('exist').should('not.be.disabled')
- })
- })
- })
- })
-
- describe('BrowseSearchFacetsSection functionality', () => {
- beforeEach(() => {
- cy.mountAuthenticated(
-
- )
-
- cy.get('[data-testid="use-facets-from-parent-checkbox"]').as('useFacetsFromParentCheckbox')
- cy.get('[data-testid="transfer-list-container"]').as('transferListContainer')
- cy.findByTestId('left-list-group').as('leftList')
- cy.findByTestId('actions-column').as('actionsColumn')
- cy.findByTestId('right-list-group').as('rightList')
- })
-
- it('should populate the right list with the default facets', () => {
- cy.get('@rightList').children().should('have.length', 4)
-
- cy.get('@rightList').within(() => {
- cy.findByLabelText('Author Name').should('exist')
- cy.findByLabelText('Subject').should('exist')
- cy.findByLabelText('Keyword Term').should('exist')
- cy.findByLabelText('Deposit Date').should('exist')
- })
- })
-
- it('should reset the newly selected facets when checking the checkbox', () => {
- cy.get('@useFacetsFromParentCheckbox').should('be.checked')
- cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
- cy.get('@useFacetsFromParentCheckbox').should('not.be.checked')
-
- cy.get('@rightList').should('exist')
- cy.get('@rightList').children().should('have.length', 4)
- cy.get('@rightList').within(() => {
- cy.findByLabelText('Author Name').should('exist')
- cy.findByLabelText('Subject').should('exist')
- cy.findByLabelText('Keyword Term').should('exist')
- cy.findByLabelText('Deposit Date').should('exist')
- })
-
- cy.get('@transferListContainer').within(() => {
- cy.findByLabelText('Topic Classification Term').check({ force: true })
-
- cy.findByLabelText('Topic Classification Term').should('be.checked')
- })
-
- cy.get('@actionsColumn').within(() => {
- cy.findByLabelText('move selected to right').click()
- })
-
- cy.get('@leftList').within(() => {
- cy.findByLabelText('Topic Classification Term').should('not.exist')
- })
-
- cy.get('@rightList').children().should('have.length', 5)
-
- cy.get('@rightList').within(() => {
- cy.findByLabelText('Author Name').should('exist')
- cy.findByLabelText('Subject').should('exist')
- cy.findByLabelText('Keyword Term').should('exist')
- cy.findByLabelText('Deposit Date').should('exist')
- cy.findByLabelText('Topic Classification Term').should('exist')
- })
-
- cy.get('@useFacetsFromParentCheckbox').check({ force: true })
-
- cy.get('@rightList').children().should('have.length', 4)
-
- cy.get('@rightList').within(() => {
- cy.findByLabelText('Author Name').should('exist')
- cy.findByLabelText('Subject').should('exist')
- cy.findByLabelText('Keyword Term').should('exist')
- cy.findByLabelText('Deposit Date').should('exist')
- cy.findByLabelText('Topic Classification Term').should('not.exist')
- })
-
- cy.get('@leftList').within(() => {
- cy.findByLabelText('Topic Classification Term').should('exist')
- })
- })
-
- it('should populate the select to filter facets by blocks correctly', () => {
- cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
-
- cy.findByTestId('select-facets-by-block').within(() => {
- cy.findByLabelText('Toggle options menu').click()
-
- cy.findAllByText('All Metadata Fields').should('have.length', 2)
-
- cy.findByText('Citation Metadata').should('have.length', 1)
- cy.findByText('Geospatial Metadata').should('have.length', 1)
- cy.findByText('Astronomy and Astrophysics Metadata').should('have.length', 1)
- cy.findByText('Life Sciences Metadata').should('have.length', 1)
- cy.findByText('Journal Metadata').should('have.length', 1)
- cy.findByText('Social Science and Humanities Metadata').should('have.length', 1)
- })
- })
-
- it('should filter the facets by blocks correctly', () => {
- cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
-
- cy.findByTestId('select-facets-by-block').within(() => {
- cy.findByLabelText('Toggle options menu').click()
-
- cy.findByText('Journal Metadata').click()
- })
-
- cy.get('@leftList').children().should('have.length', 4)
-
- cy.get('@leftList').within(() => {
- cy.findByLabelText('Journal Volume').should('exist')
- cy.findByLabelText('Journal Issue').should('exist')
- cy.findByLabelText('Journal Publication Date').should('exist')
- cy.findByLabelText('Type of Article').should('exist')
- })
-
- cy.findByTestId('select-facets-by-block').within(() => {
- cy.findByLabelText('Toggle options menu').click()
-
- cy.findByText('Social Science and Humanities Metadata').click()
- })
-
- cy.get('@leftList').children().should('have.length', 5)
-
- cy.get('@leftList').within(() => {
- cy.findByLabelText('Unit of Analysis').should('exist')
- cy.findByLabelText('Universe').should('exist')
- cy.findByLabelText('Time Method').should('exist')
- cy.findByLabelText('Frequency').should('exist')
- cy.findByLabelText('Response Rate').should('exist')
- })
-
- cy.findByTestId('select-facets-by-block').within(() => {
- cy.findByLabelText('Toggle options menu').click()
-
- cy.findByText('All Metadata Fields').click()
- })
-
- cy.get('@leftList')
- .children()
- .should(
- 'have.length',
- allFacetableMetadataFields.length - defaultCollectionFacetsMock.length
- )
- })
-
- it('should reset the select to filter by facets when checking the checkbox', () => {
- cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
-
- cy.findByTestId('select-facets-by-block').within(() => {
- cy.findByLabelText('Toggle options menu').click()
-
- cy.findByText('Journal Metadata').click()
- })
-
- cy.get('@leftList').children().should('have.length', 4)
-
- cy.get('@leftList').within(() => {
- cy.findByLabelText('Journal Volume').should('exist')
- cy.findByLabelText('Journal Issue').should('exist')
- cy.findByLabelText('Journal Publication Date').should('exist')
- cy.findByLabelText('Type of Article').should('exist')
- })
-
- cy.get('@useFacetsFromParentCheckbox').check({ force: true })
-
- cy.get('@leftList')
- .children()
- .should(
- 'have.length',
- allFacetableMetadataFields.length - defaultCollectionFacetsMock.length
- )
-
- cy.findByTestId('select-facets-by-block').within(() => {
- cy.findByText('All Metadata Fields').should('exist')
- })
- })
- })
-})
diff --git a/tests/component/sections/create-collection/CreateCollection.spec.tsx b/tests/component/sections/create-collection/CreateCollection.spec.tsx
index 40c4dbc8e..fdb9682e1 100644
--- a/tests/component/sections/create-collection/CreateCollection.spec.tsx
+++ b/tests/component/sections/create-collection/CreateCollection.spec.tsx
@@ -1,11 +1,11 @@
-import { CollectionRepository } from '../../../../src/collection/domain/repositories/CollectionRepository'
-import { MetadataBlockInfoRepository } from '../../../../src/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
-import { CreateCollection } from '../../../../src/sections/create-collection/CreateCollection'
-import { UserRepository } from '../../../../src/users/domain/repositories/UserRepository'
-import { CollectionFacetMother } from '../../collection/domain/models/CollectionFacetMother'
-import { CollectionMother } from '../../collection/domain/models/CollectionMother'
-import { MetadataBlockInfoMother } from '../../metadata-block-info/domain/models/MetadataBlockInfoMother'
-import { UserMother } from '../../users/domain/models/UserMother'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { CreateCollection } from '@/sections/create-collection/CreateCollection'
+import { UserRepository } from '@/users/domain/repositories/UserRepository'
+import { CollectionFacetMother } from '@tests/component/collection/domain/models/CollectionFacetMother'
+import { CollectionMother } from '@tests/component/collection/domain/models/CollectionMother'
+import { MetadataBlockInfoMother } from '@tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother'
+import { UserMother } from '@tests/component/users/domain/models/UserMother'
const collectionRepository: CollectionRepository = {} as CollectionRepository
const metadataBlockInfoRepository: MetadataBlockInfoRepository = {} as MetadataBlockInfoRepository
@@ -57,7 +57,7 @@ describe('CreateCollection', () => {
)
cy.clock()
@@ -76,7 +76,7 @@ describe('CreateCollection', () => {
)
@@ -95,87 +95,70 @@ describe('CreateCollection', () => {
)
cy.findByText('Page Not Found').should('exist')
})
- it('pre-fills specific form fields with user data', () => {
+ it('should show alert error message when user is not allowed to create collection', () => {
+ const DELAYED_TIME = 200
+ collectionRepository.getUserPermissions = cy.stub().callsFake(() => {
+ return Cypress.Promise.delay(DELAYED_TIME).then(() =>
+ CollectionMother.createUserPermissions({
+ canAddCollection: false
+ })
+ )
+ })
+
cy.mountAuthenticated(
)
- cy.findByLabelText(/^Collection Name/i).should(
- 'have.value',
- `${testUser.displayName} Collection`
- )
-
- cy.findByLabelText(/^Affiliation/i).should('have.value', testUser.affiliation)
+ cy.wait(DELAYED_TIME * 2)
- cy.findByLabelText(/^Email/i).should('have.value', testUser.email)
+ cy.findByText(
+ /You do not have permissions to create a collection within this collection./
+ ).should('exist')
})
- it('should show alert error message when user is not allowed to create collection', () => {
- collectionRepository.getUserPermissions = cy.stub().resolves(
- CollectionMother.createUserPermissions({
- canAddCollection: false
- })
- )
+ it('should not show alert error message when user is allowed to create collection', () => {
+ const DELAYED_TIME = 200
+ collectionRepository.getUserPermissions = cy.stub().callsFake(() => {
+ return Cypress.Promise.delay(DELAYED_TIME).then(() => userPermissionsMock)
+ })
cy.mountAuthenticated(
)
- cy.findAllByTestId('not-allowed-to-create-collection-alert').should('exist')
- })
- it('should not show alert error message when user is allowed to create collection', () => {
- cy.mountAuthenticated(
-
- )
- cy.findAllByTestId('not-allowed-to-create-collection-alert').should('not.exist')
+ cy.wait(DELAYED_TIME * 2)
+
+ cy.findByText(
+ /You do not have permissions to create a collection within this collection./
+ ).should('not.exist')
})
- it('should display an alert error message for each error in loading the required data', () => {
- collectionRepository.getUserPermissions = cy
- .stub()
- .rejects(new Error('Error getting user permissions'))
- collectionRepository.getFacets = cy.stub().rejects(new Error('Error getting collection facets'))
- metadataBlockInfoRepository.getByCollectionId = cy
- .stub()
- .rejects(new Error('Error getting metadata blocks info'))
- metadataBlockInfoRepository.getAll = cy
- .stub()
- .rejects(new Error('Error getting all metadata blocks info'))
- metadataBlockInfoRepository.getAllFacetableMetadataFields = cy
- .stub()
- .rejects(new Error('Error getting all facetable metadata fields'))
+ it('should show alert error message when getting the user permissions fails', () => {
+ collectionRepository.getUserPermissions = cy.stub().rejects('Error')
cy.mountAuthenticated(
)
- cy.findByText(/Error getting user permissions/).should('exist')
- cy.findByText(/Error getting collection facets/).should('exist')
- cy.findByText(/Error getting metadata blocks info/).should('exist')
- cy.findByText(/Error getting all metadata blocks info/).should('exist')
- cy.findByText(/Error getting all facetable metadata fields/).should('exist')
+ cy.findByText('Error').should('exist')
})
})
diff --git a/tests/component/sections/dataset/Dataset.spec.tsx b/tests/component/sections/dataset/Dataset.spec.tsx
index c06134a39..c77b5e2b5 100644
--- a/tests/component/sections/dataset/Dataset.spec.tsx
+++ b/tests/component/sections/dataset/Dataset.spec.tsx
@@ -16,11 +16,13 @@ import { FileAccessOption, FileTag } from '../../../../src/files/domain/models/F
import { AlertProvider } from '../../../../src/sections/alerts/AlertProvider'
import { MetadataBlockInfoRepository } from '../../../../src/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
import { MetadataBlockInfoMother } from '../../metadata-block-info/domain/models/MetadataBlockInfoMother'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
const setAnonymizedView = () => {}
const fileRepository: FileRepository = {} as FileRepository
const datasetRepository: DatasetRepository = {} as DatasetRepository
const metadataBlockInfoRepository: MetadataBlockInfoRepository = {} as MetadataBlockInfoRepository
+const collectionRepository: CollectionRepository = {} as CollectionRepository
const TOTAL_FILES_COUNT = 200
const allFiles = FilePreviewMother.createMany(TOTAL_FILES_COUNT)
@@ -93,6 +95,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
testDataset
)
@@ -109,6 +112,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
emptyDataset
)
@@ -124,6 +128,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
dataset
)
@@ -140,6 +145,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
dataset
)
@@ -155,6 +161,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
testDataset
)
@@ -171,6 +178,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
testDataset
)
@@ -190,6 +198,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
testDataset
)
@@ -212,6 +221,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
testDatasetAnonymized
)
@@ -229,6 +239,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
/>,
testDataset
)
@@ -245,6 +256,7 @@ describe('Dataset', () => {
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
filesTabInfiniteScrollEnabled={true}
+ collectionRepository={collectionRepository}
/>,
testDataset
)
@@ -262,6 +274,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
created={true}
/>
,
@@ -281,6 +294,7 @@ describe('Dataset', () => {
datasetRepository={datasetRepository}
fileRepository={fileRepository}
metadataBlockInfoRepository={metadataBlockInfoRepository}
+ collectionRepository={collectionRepository}
metadataUpdated={true}
/>
,
diff --git a/tests/component/sections/dataset/dataset-files/dataset-upload-files-button/DatasetUploadFilesButton.spec.tsx b/tests/component/sections/dataset/dataset-files/dataset-upload-files-button/DatasetUploadFilesButton.spec.tsx
index 24c3ff587..c5198a9e5 100644
--- a/tests/component/sections/dataset/dataset-files/dataset-upload-files-button/DatasetUploadFilesButton.spec.tsx
+++ b/tests/component/sections/dataset/dataset-files/dataset-upload-files-button/DatasetUploadFilesButton.spec.tsx
@@ -56,7 +56,9 @@ describe('DatasetUploadFilesButton', () => {
cy.findByRole('button', { name: 'Upload Files' }).should('exist').should('be.disabled')
})
- it.skip('calls upload files use case when button is clicked', () => {
- // TODO - Implement upload files
+ it('test click', () => {
+ cy.mountAuthenticated(withDataset(, datasetWithUpdatePermissions))
+
+ cy.findByRole('button', { name: 'Upload Files' }).click()
})
})
diff --git a/tests/component/sections/edit-collection/EditCollection.spec.tsx b/tests/component/sections/edit-collection/EditCollection.spec.tsx
new file mode 100644
index 000000000..384aa1f85
--- /dev/null
+++ b/tests/component/sections/edit-collection/EditCollection.spec.tsx
@@ -0,0 +1,164 @@
+import { EditCollection } from '@/sections/edit-collection/EditCollection'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { UserRepository } from '@/users/domain/repositories/UserRepository'
+import { CollectionFacetMother } from '@tests/component/collection/domain/models/CollectionFacetMother'
+import { CollectionMother } from '@tests/component/collection/domain/models/CollectionMother'
+import { MetadataBlockInfoMother } from '@tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother'
+import { UserMother } from '@tests/component/users/domain/models/UserMother'
+
+const collectionRepository: CollectionRepository = {} as CollectionRepository
+const metadataBlockInfoRepository: MetadataBlockInfoRepository = {} as MetadataBlockInfoRepository
+
+const COLLECTION_NAME = 'Collection Name'
+const collection = CollectionMother.create({ name: COLLECTION_NAME })
+const userPermissionsMock = CollectionMother.createUserPermissions()
+
+const collectionMetadataBlocksInfo =
+ MetadataBlockInfoMother.getByCollectionIdDisplayedOnCreateFalse()
+
+const collectionFacetsMock = CollectionFacetMother.createFacets()
+
+const allFacetableMetadataFieldsMock = MetadataBlockInfoMother.createFacetableMetadataFields()
+
+const allMetadataBlocksMock = [
+ MetadataBlockInfoMother.getCitationBlock(),
+ MetadataBlockInfoMother.getGeospatialBlock(),
+ MetadataBlockInfoMother.getAstrophysicsBlock(),
+ MetadataBlockInfoMother.getBiomedicalBlock(),
+ MetadataBlockInfoMother.getJournalBlock(),
+ MetadataBlockInfoMother.getSocialScienceBlock()
+]
+
+const testUser = UserMother.create()
+const userRepository: UserRepository = {} as UserRepository
+
+describe('EditCollection', () => {
+ beforeEach(() => {
+ collectionRepository.create = cy.stub().resolves(1)
+ collectionRepository.getById = cy.stub().resolves(collection)
+ collectionRepository.getUserPermissions = cy.stub().resolves(userPermissionsMock)
+ collectionRepository.getFacets = cy.stub().resolves(collectionFacetsMock)
+ metadataBlockInfoRepository.getByCollectionId = cy.stub().resolves(collectionMetadataBlocksInfo)
+ metadataBlockInfoRepository.getAll = cy.stub().resolves(allMetadataBlocksMock)
+ metadataBlockInfoRepository.getAllFacetableMetadataFields = cy
+ .stub()
+ .resolves(allFacetableMetadataFieldsMock)
+ userRepository.getAuthenticated = cy.stub().resolves(testUser)
+ })
+
+ it('should show loading skeleton while loading the owner collection', () => {
+ const DELAYED_TIME = 200
+ collectionRepository.getById = cy.stub().callsFake(() => {
+ return Cypress.Promise.delay(DELAYED_TIME).then(() => collection)
+ })
+
+ cy.customMount(
+
+ )
+ cy.clock()
+
+ cy.findByTestId('edit-collection-skeleton').should('exist')
+
+ cy.tick(DELAYED_TIME)
+
+ cy.findByTestId('edit-collection-skeleton').should('not.exist')
+
+ cy.clock().then((clock) => clock.restore())
+ })
+
+ it('should render the correct breadcrumbs', () => {
+ cy.customMount(
+
+ )
+
+ cy.findByRole('link', { name: 'Root' }).should('exist')
+
+ cy.get('li[aria-current="page"]')
+ .should('exist')
+ .should('have.text', 'Edit Collection')
+ .should('have.class', 'active')
+ })
+
+ it('should show page not found when collection does not exist', () => {
+ collectionRepository.getById = cy.stub().resolves(null)
+
+ cy.customMount(
+
+ )
+
+ cy.findByText('Page Not Found').should('exist')
+ })
+
+ it('should show alert error message when user is not allowed to edit the collection', () => {
+ const DELAYED_TIME = 200
+ collectionRepository.getUserPermissions = cy.stub().callsFake(() => {
+ return Cypress.Promise.delay(DELAYED_TIME).then(() =>
+ CollectionMother.createUserPermissions({
+ canEditCollection: false
+ })
+ )
+ })
+
+ cy.mountAuthenticated(
+
+ )
+
+ cy.wait(DELAYED_TIME * 2)
+
+ cy.findByText(/You do not have permissions to edit this collection./, { timeout: 0 }).should(
+ 'exist'
+ )
+ })
+
+ it('should not show alert error message when user is allowed to edit the collection', () => {
+ const DELAYED_TIME = 200
+ collectionRepository.getUserPermissions = cy.stub().callsFake(() => {
+ return Cypress.Promise.delay(DELAYED_TIME).then(() => userPermissionsMock)
+ })
+
+ cy.mountAuthenticated(
+
+ )
+
+ cy.wait(DELAYED_TIME * 2)
+
+ cy.findByText(/You do not have permissions to edit this collection./, { timeout: 0 }).should(
+ 'not.exist'
+ )
+ })
+
+ it('should show alert error message when getting the user permissions fails', () => {
+ collectionRepository.getUserPermissions = cy.stub().rejects('Error')
+
+ cy.mountAuthenticated(
+
+ )
+
+ cy.findByText('Error').should('exist')
+ })
+})
diff --git a/tests/component/sections/route-enum/Route.spec.ts b/tests/component/sections/route-enum/Route.spec.ts
new file mode 100644
index 000000000..5c1b69ed5
--- /dev/null
+++ b/tests/component/sections/route-enum/Route.spec.ts
@@ -0,0 +1,22 @@
+import { Route, RouteWithParams } from '@/sections/Route.enum'
+
+describe('Route.enum.ts', () => {
+ describe('RouteWithParams', () => {
+ it('should return the correct route for COLLECTIONS', () => {
+ expect(RouteWithParams.COLLECTIONS()).to.be.equal(Route.COLLECTIONS_BASE)
+ expect(RouteWithParams.COLLECTIONS('123')).to.be.equal(`${Route.COLLECTIONS_BASE}/123`)
+ })
+
+ it('should return the correct route for CREATE_COLLECTION', () => {
+ expect(RouteWithParams.CREATE_COLLECTION('123')).to.be.equal(`/collections/123/create`)
+ })
+
+ it('should return the correct route for EDIT_COLLECTION', () => {
+ expect(RouteWithParams.EDIT_COLLECTION('123')).to.be.equal(`/collections/123/edit`)
+ })
+
+ it('should return the correct route for CREATE_DATASET', () => {
+ expect(RouteWithParams.CREATE_DATASET('123')).to.be.equal(`/datasets/123/create`)
+ })
+ })
+})
diff --git a/tests/component/sections/create-collection/CollectionFormHelper.spec.ts b/tests/component/sections/shared/edit-create-collection-form/CollectionFormHelper.spec.ts
similarity index 69%
rename from tests/component/sections/create-collection/CollectionFormHelper.spec.ts
rename to tests/component/sections/shared/edit-create-collection-form/CollectionFormHelper.spec.ts
index 6c60b85f6..ff83551bd 100644
--- a/tests/component/sections/create-collection/CollectionFormHelper.spec.ts
+++ b/tests/component/sections/shared/edit-create-collection-form/CollectionFormHelper.spec.ts
@@ -1,16 +1,18 @@
-import { CollectionInputLevel } from '../../../../src/collection/domain/models/Collection'
-import { CollectionInputLevelDTO } from '../../../../src/collection/domain/useCases/DTOs/CollectionDTO'
+import { CollectionInputLevel } from '@/collection/domain/models/CollectionInputLevel'
+import { CollectionContact } from '@/collection/domain/models/CollectionContact'
+import { CollectionInputLevelDTO } from '@/collection/domain/useCases/DTOs/CollectionDTO'
import {
MetadataBlockInfo,
MetadataBlockName,
MetadataField
-} from '../../../../src/metadata-block-info/domain/models/MetadataBlockInfo'
+} from '@/metadata-block-info/domain/models/MetadataBlockInfo'
+import { CollectionFormHelper } from '@/sections/shared/form/EditCreateCollectionForm/CollectionFormHelper'
import {
+ CollectionFormContactValue,
FormattedCollectionInputLevels,
FormattedCollectionInputLevelsWithoutParentBlockName,
MetadataFieldWithParentBlockInfo
-} from '../../../../src/sections/create-collection/collection-form/CollectionForm'
-import { CollectionFormHelper } from '../../../../src/sections/create-collection/collection-form/CollectionFormHelper'
+} from '@/sections/shared/form/EditCreateCollectionForm/types'
const allMetadataBlocksInfoMock: MetadataBlockInfo[] = [
{
@@ -551,4 +553,187 @@ describe('CollectionFormHelper', () => {
expect(result).to.equal('coverage.Spectral.MinimumWavelength')
})
+
+ it('formats collection contacts to form contacts values', () => {
+ const collectionContacts: CollectionContact[] = [
+ {
+ email: 'test1@mail.com',
+ displayOrder: 0
+ },
+ {
+ email: 'test2@mail.com',
+ displayOrder: 1
+ }
+ ]
+ const expected: CollectionFormContactValue[] = [
+ {
+ value: 'test1@mail.com'
+ },
+ {
+ value: 'test2@mail.com'
+ }
+ ]
+
+ const result = CollectionFormHelper.formatCollectionContactsToFormContacts(collectionContacts)
+
+ expect(result).to.deep.equal(expected)
+ })
+
+ it('formats empty collection contacts to empty form contacts values ', () => {
+ const collectionContacts: CollectionContact[] = []
+
+ const result = CollectionFormHelper.formatCollectionContactsToFormContacts(collectionContacts)
+
+ expect(result).to.deep.equal([{ value: '' }])
+ })
+
+ describe('defineShouldCheckUseFromParent', () => {
+ it('returns false if is on edit mode and is editing the root collection', () => {
+ const result = CollectionFormHelper.defineShouldCheckUseFromParent(true, true, true)
+
+ expect(result).to.equal(false)
+ })
+
+ it('returns false if is on edit mode and is not editing the root collection and isMetadataBlockOrFacetRoot is true', () => {
+ const result = CollectionFormHelper.defineShouldCheckUseFromParent(true, false, true)
+
+ expect(result).to.equal(false)
+ })
+
+ it('returns true if is on edit mode and is not editing the root collection and isMetadataBlockOrFacetRoot is false', () => {
+ const result = CollectionFormHelper.defineShouldCheckUseFromParent(true, false, false)
+
+ expect(result).to.equal(true)
+ })
+
+ it('should return true if is not on edit mode', () => {
+ const result = CollectionFormHelper.defineShouldCheckUseFromParent(false, false, true)
+
+ expect(result).to.equal(true)
+ })
+ })
+
+ describe('defineShouldSendMetadataBlockNamesAndInputLevels', () => {
+ it('for edit mode - returns true if is editing root collection and blockNamesHaveChanged is true', () => {
+ const result = CollectionFormHelper.defineShouldSendMetadataBlockNamesAndInputLevels(
+ false,
+ true,
+ true,
+ false,
+ 'edit'
+ )
+
+ expect(result).to.equal(true)
+ })
+
+ it('for edit mode - returns true if is editing root collection and inputLevelsHaveChanged is true', () => {
+ const result = CollectionFormHelper.defineShouldSendMetadataBlockNamesAndInputLevels(
+ false,
+ true,
+ false,
+ true,
+ 'edit'
+ )
+
+ expect(result).to.equal(true)
+ })
+
+ it('for edit mode - returns false if is editing root collection and blockNamesHaveChanged and inputLevelsHaveChanged are false', () => {
+ const result = CollectionFormHelper.defineShouldSendMetadataBlockNamesAndInputLevels(
+ false,
+ true,
+ false,
+ false,
+ 'edit'
+ )
+
+ expect(result).to.equal(false)
+ })
+
+ it('for edit mode - returns false if is not editing root collection and useFieldsFromParentChecked is true', () => {
+ const result = CollectionFormHelper.defineShouldSendMetadataBlockNamesAndInputLevels(
+ true,
+ false,
+ true,
+ true,
+ 'edit'
+ )
+
+ expect(result).to.equal(false)
+ })
+
+ it('for edit mode - returns true if is not editing root collection and useFieldsFromParentChecked is false', () => {
+ const result = CollectionFormHelper.defineShouldSendMetadataBlockNamesAndInputLevels(
+ false,
+ false,
+ true,
+ true,
+ 'edit'
+ )
+
+ expect(result).to.equal(true)
+ })
+
+ it('for create mode - returns false if useFieldsFromParentChecked is true', () => {
+ const result = CollectionFormHelper.defineShouldSendMetadataBlockNamesAndInputLevels(
+ true,
+ false,
+ true,
+ true,
+ 'create'
+ )
+
+ expect(result).to.equal(false)
+ })
+
+ it('for create mode - returns true if useFieldsFromParentChecked is false', () => {
+ const result = CollectionFormHelper.defineShouldSendMetadataBlockNamesAndInputLevels(
+ false,
+ false,
+ true,
+ true,
+ 'create'
+ )
+
+ expect(result).to.equal(true)
+ })
+ })
+
+ describe('defineShouldSendFacetIds', () => {
+ it('for edit mode - returns true if is editing root collection and facetIdsHaveChanged is true', () => {
+ const result = CollectionFormHelper.defineShouldSendFacetIds(false, true, true, 'edit')
+
+ expect(result).to.equal(true)
+ })
+
+ it('for edit mode - returns false if is editing root collection and facetIdsHaveChanged is false', () => {
+ const result = CollectionFormHelper.defineShouldSendFacetIds(false, true, false, 'edit')
+
+ expect(result).to.equal(false)
+ })
+
+ it('for edit mode - returns false if is not editing root collection and useFacetsFromParentChecked is true', () => {
+ const result = CollectionFormHelper.defineShouldSendFacetIds(true, false, true, 'edit')
+
+ expect(result).to.equal(false)
+ })
+
+ it('for edit mode - returns true if is not editing root collection and useFacetsFromParentChecked is false', () => {
+ const result = CollectionFormHelper.defineShouldSendFacetIds(false, false, true, 'edit')
+
+ expect(result).to.equal(true)
+ })
+
+ it('for create mode - returns false if useFacetsFromParentChecked is true', () => {
+ const result = CollectionFormHelper.defineShouldSendFacetIds(true, false, true, 'create')
+
+ expect(result).to.equal(false)
+ })
+
+ it('for create mode - returns true if useFacetsFromParentChecked is false', () => {
+ const result = CollectionFormHelper.defineShouldSendFacetIds(false, false, true, 'create')
+
+ expect(result).to.equal(true)
+ })
+ })
})
diff --git a/tests/component/sections/shared/edit-create-collection-form/EditCreateCollectionForm.spec.tsx b/tests/component/sections/shared/edit-create-collection-form/EditCreateCollectionForm.spec.tsx
new file mode 100644
index 000000000..12359113a
--- /dev/null
+++ b/tests/component/sections/shared/edit-create-collection-form/EditCreateCollectionForm.spec.tsx
@@ -0,0 +1,1310 @@
+import { CollectionType } from '@/collection/domain/models/CollectionType'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { CollectionDTO } from '@/collection/domain/useCases/DTOs/CollectionDTO'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { collectionNameToAlias } from '@/sections/shared/form/EditCreateCollectionForm/collection-form/top-fields-section/IdentifierField'
+import { EditCreateCollectionForm } from '@/sections/shared/form/EditCreateCollectionForm/EditCreateCollectionForm'
+import { UserRepository } from '@/users/domain/repositories/UserRepository'
+import { CollectionFacetMother } from '@tests/component/collection/domain/models/CollectionFacetMother'
+import { CollectionMother } from '@tests/component/collection/domain/models/CollectionMother'
+import { MetadataBlockInfoMother } from '@tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother'
+import { UpwardHierarchyNodeMother } from '@tests/component/shared/hierarchy/domain/models/UpwardHierarchyNodeMother'
+import { UserMother } from '@tests/component/users/domain/models/UserMother'
+
+const collectionRepository: CollectionRepository = {} as CollectionRepository
+const metadataBlockInfoRepository = {} as MetadataBlockInfoRepository
+const userRepository: UserRepository = {} as UserRepository
+
+const testUser = UserMother.create()
+
+const PARENT_COLLECTION_ID = 'root'
+const PARENT_COLLECTION_NAME = 'Root'
+const PARENT_COLLECTION_CONTACT_EMAIL = 'root@test.com'
+
+const COLLECTION_BEING_EDITED_ID = 'collection-being-edited-id'
+const COLLECTION_BEING_EDITED_NAME = 'Collection In Edition'
+const COLLECTION_BEING_EDITED_AFFILIATION = 'Collection In Edition Affiliation'
+const COLLECTION_BEING_EDITED_DESCRIPTION = 'Collection In Edition Description'
+const COLLECTION_BEING_EDITED_CONTACT_EMAIL = 'collectionEdited@test.com'
+
+const defaultCollectionNameInCreateMode = `${testUser.displayName} Collection`
+
+const collectionBeingEdited = CollectionMother.create({
+ id: COLLECTION_BEING_EDITED_ID,
+ name: COLLECTION_BEING_EDITED_NAME,
+ description: COLLECTION_BEING_EDITED_DESCRIPTION,
+ affiliation: COLLECTION_BEING_EDITED_AFFILIATION,
+ type: CollectionType.JOURNALS,
+ contacts: [{ email: COLLECTION_BEING_EDITED_CONTACT_EMAIL, displayOrder: 0 }],
+ hierarchy: UpwardHierarchyNodeMother.createCollection({
+ id: COLLECTION_BEING_EDITED_ID,
+ name: COLLECTION_BEING_EDITED_NAME,
+ parent: UpwardHierarchyNodeMother.createCollection({
+ id: PARENT_COLLECTION_ID,
+ name: PARENT_COLLECTION_NAME
+ })
+ }),
+ isFacetRoot: false,
+ isMetadataBlockRoot: false
+})
+
+const rootCollection = CollectionMother.create({
+ id: PARENT_COLLECTION_ID,
+ name: PARENT_COLLECTION_NAME,
+ contacts: [{ email: PARENT_COLLECTION_CONTACT_EMAIL, displayOrder: 0 }],
+ hierarchy: UpwardHierarchyNodeMother.createCollection({
+ id: PARENT_COLLECTION_ID,
+ name: PARENT_COLLECTION_NAME
+ }),
+ isFacetRoot: true,
+ isMetadataBlockRoot: true
+})
+
+const VIEW_AND_EDIT_FIELDS_LABEL = '[+] View fields + set as hidden, required, or optional'
+const VIEW_FIELDS_LABEL = '[+] View fields'
+
+const colllectionMetadataBlocks = [MetadataBlockInfoMother.getCitationBlock()]
+
+const allMetadataBlocksMock = [
+ MetadataBlockInfoMother.getCitationBlock(),
+ MetadataBlockInfoMother.getGeospatialBlock(),
+ MetadataBlockInfoMother.getAstrophysicsBlock(),
+ MetadataBlockInfoMother.getBiomedicalBlock(),
+ MetadataBlockInfoMother.getJournalBlock(),
+ MetadataBlockInfoMother.getSocialScienceBlock()
+]
+
+const collectionFacets = CollectionFacetMother.createFacets()
+
+const allFacetableMetadataFields = MetadataBlockInfoMother.getAllFacetableMetadataFields()
+
+describe('EditCreateCollectionForm', () => {
+ beforeEach(() => {
+ collectionRepository.create = cy.stub().resolves(1)
+ collectionRepository.edit = cy.stub().resolves({})
+ collectionRepository.getFacets = cy.stub().resolves(collectionFacets)
+ userRepository.getAuthenticated = cy.stub().resolves(testUser)
+ metadataBlockInfoRepository.getByCollectionId = cy.stub().resolves(colllectionMetadataBlocks)
+ metadataBlockInfoRepository.getAll = cy.stub().resolves(allMetadataBlocksMock)
+ metadataBlockInfoRepository.getAllFacetableMetadataFields = cy
+ .stub()
+ .resolves(allFacetableMetadataFields)
+ })
+
+ describe('on create mode', () => {
+ it('should render the form', () => {
+ cy.mountAuthenticated(
+
+ )
+ cy.findByTestId('collection-form').should('exist')
+ })
+
+ it('prefills the Host Collection field with the parent collection name', () => {
+ cy.mountAuthenticated(
+
+ )
+
+ cy.findByLabelText(/^Host Collection/i).should('have.value', PARENT_COLLECTION_NAME)
+ })
+
+ it('pre-fills specific form fields with user data', () => {
+ cy.mountAuthenticated(
+
+ )
+
+ cy.findByLabelText(/^Collection Name/i).should(
+ 'have.value',
+ defaultCollectionNameInCreateMode
+ )
+
+ cy.findByLabelText(/^Affiliation/i).should('have.value', testUser.affiliation)
+
+ cy.findByLabelText(/^Email/i).should('have.value', testUser.email)
+ })
+
+ it('submit button should be disabled when form has not been touched', () => {
+ cy.customMount(
+
+ )
+
+ cy.findByRole('button', { name: 'Create Collection' }).should('be.disabled')
+ })
+
+ it('submit button should not be disabled when form has been touched', () => {
+ cy.customMount(
+
+ )
+
+ cy.findByLabelText(/^Collection Name/i)
+ .clear()
+ .type('New Collection Name')
+
+ cy.findByRole('button', { name: 'Create Collection' }).should('not.be.disabled')
+ })
+
+ it('shows error message when form is submitted with empty required fields', () => {
+ cy.customMount(
+
+ )
+
+ // Change collection name so submit button is no longer disabled
+ cy.findByLabelText(/^Collection Name/i)
+ .clear()
+ .type('New Collection Name')
+
+ cy.findByRole('button', { name: 'Create Collection' }).click()
+
+ cy.findByText('Category is required').should('exist')
+ cy.findByText('Identifier is required').should('exist')
+ })
+
+ it('shows error message when form is submitted with invalid email', () => {
+ cy.customMount(
+
+ )
+
+ cy.findByLabelText(/^Email/i)
+ .clear()
+ .type('invalid-email')
+
+ cy.findByRole('button', { name: 'Create Collection' }).click()
+
+ cy.findByText('Email is not a valid email').should('exist')
+ })
+
+ it('shows error message when form is submitted with invalid identifier', () => {
+ cy.customMount(
+
+ )
+
+ cy.findByLabelText(/^Identifier/i).type('invalid identifier')
+
+ cy.findByRole('button', { name: 'Create Collection' }).click()
+
+ cy.findByText(/Identifier is not valid./).should('exist')
+ })
+
+ it('submits a valid form and succeed', () => {
+ cy.customMount(
+
+ )
+ // Accept suggestion
+ cy.findByRole('button', { name: 'Apply suggestion' }).click()
+ // Select a Category option
+ cy.findByLabelText(/^Category/i).select(1)
+
+ cy.findByRole('button', { name: 'Create Collection' }).click()
+
+ cy.findByText('Error').should('not.exist')
+ cy.findByText('Success!').should('exist')
+ })
+
+ it('submits a valid form and fails', () => {
+ collectionRepository.create = cy.stub().rejects(new Error('Error creating collection'))
+
+ cy.customMount(
+
+ )
+
+ // Accept suggestion
+ cy.findByRole('button', { name: 'Apply suggestion' }).click()
+ // Select a Category option
+ cy.findByLabelText(/^Category/i).select(1)
+
+ cy.findByRole('button', { name: 'Create Collection' }).click()
+
+ cy.findByText('Error').should('exist')
+ cy.findByText(/Error creating collection/).should('exist')
+ cy.findByText('Success!').should('not.exist')
+ })
+
+ it('cancel button is clickable', () => {
+ cy.customMount(
+
+ )
+
+ cy.findByText('Cancel').click()
+ })
+
+ it('should display an alert error message for each error in loading the required data', () => {
+ collectionRepository.getFacets = cy
+ .stub()
+ .rejects(new Error('Error getting collection facets'))
+ metadataBlockInfoRepository.getByCollectionId = cy
+ .stub()
+ .rejects(new Error('Error getting metadata blocks info'))
+ metadataBlockInfoRepository.getAll = cy
+ .stub()
+ .rejects(new Error('Error getting all metadata blocks info'))
+ metadataBlockInfoRepository.getAllFacetableMetadataFields = cy
+ .stub()
+ .rejects(new Error('Error getting all facetable metadata fields'))
+
+ cy.mountAuthenticated(
+
+ )
+
+ cy.findByText(/Error getting collection facets/).should('exist')
+ cy.findByText(/Error getting metadata blocks info/).should('exist')
+ cy.findByText(/Error getting all metadata blocks info/).should('exist')
+ cy.findByText(/Error getting all facetable metadata fields/).should('exist')
+ })
+
+ describe('IdentifierField suggestion functionality', () => {
+ it('should show to apply an identifier suggestion', () => {
+ cy.customMount(
+
+ )
+
+ const aliasSuggestion = collectionNameToAlias(defaultCollectionNameInCreateMode)
+
+ cy.findByText(/Psst... try this/).should('exist')
+ cy.findByText(/Psst... try this/).should('include.text', aliasSuggestion)
+
+ cy.findByRole('button', { name: 'Apply suggestion' }).should('exist')
+ })
+
+ it('should apply suggestion when clicking the button and hide suggestion', () => {
+ cy.customMount(
+
+ )
+
+ const aliasSuggestion = collectionNameToAlias(defaultCollectionNameInCreateMode)
+
+ cy.findByRole('button', { name: 'Apply suggestion' }).click()
+
+ cy.findByLabelText(/^Identifier/i).should('have.value', aliasSuggestion)
+
+ cy.findByText(/Psst... try this/).should('not.exist')
+ })
+ })
+
+ describe('ContactsField functionality', () => {
+ it('should add a new contact field when clicking the add button', () => {
+ cy.customMount(
+
+ )
+
+ cy.findByLabelText('Add Email').click()
+
+ cy.findAllByLabelText('Add Email').should('exist').should('have.length', 2)
+ cy.findByLabelText('Remove Email').should('exist')
+ })
+
+ it('should add and then remove a contact field', () => {
+ cy.customMount(
+
+ )
+
+ cy.findByLabelText('Add Email').click()
+ cy.findAllByLabelText('Add Email').should('exist').should('have.length', 2)
+
+ cy.get('[id="contacts.1.value"]').type('test@test.com')
+
+ cy.findByLabelText('Remove Email').should('exist')
+
+ cy.findByLabelText('Remove Email').click()
+
+ cy.findByLabelText('Add Email').should('exist')
+ cy.findByLabelText('Remove Email').should('not.exist')
+
+ cy.get('[id="contacts.1.value"]').should('not.exist')
+ })
+ })
+
+ describe('MetadataFieldsSection functionality', () => {
+ beforeEach(() => {
+ cy.mountAuthenticated(
+
+ )
+
+ cy.get('[data-testid="use-fields-from-parent-checkbox"]').as('useFieldsFromParentCheckbox')
+ })
+
+ describe('Use fields from parent checkbox', () => {
+ it('should be checked by default', () => {
+ cy.get('@useFieldsFromParentCheckbox').should('be.checked')
+ })
+
+ it('should open the reset confirmation modal when unchecking and then checking the checkbox again', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ cy.get('@useFieldsFromParentCheckbox').check({ force: true })
+
+ cy.findByText(/Reset Modifications/).should('exist')
+ })
+
+ it('should close the reset confirmation modal if cancelling without finally checking the checkbox', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ cy.get('@useFieldsFromParentCheckbox').check({ force: true })
+
+ cy.findByText(/Reset Modifications/).should('exist')
+
+ cy.findByTestId('confirm-reset-modal-cancel').click()
+
+ cy.findByText(/Reset Modifications/).should('not.exist')
+
+ cy.get('@useFieldsFromParentCheckbox').should('not.be.checked')
+ })
+
+ it('should reset the metadata fields when confirming the reset', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck()
+
+ // Modify a field in citation block
+ cy.findByRole('button', {
+ name: VIEW_AND_EDIT_FIELDS_LABEL
+ })
+ .should('exist')
+ .click()
+
+ cy.findByLabelText('Subtitle').uncheck({ force: true })
+
+ cy.findByLabelText('Subtitle').should('not.be.checked')
+
+ cy.get('@useFieldsFromParentCheckbox').check({ force: true })
+
+ cy.findByText(/Reset Modifications/).should('exist')
+
+ cy.findByTestId('confirm-reset-modal-continue').click()
+
+ cy.findByText(/Reset Modifications/).should('not.exist')
+
+ cy.get('@useFieldsFromParentCheckbox').should('be.checked')
+
+ // Check if field is back to its original state
+ cy.findAllByRole('button', {
+ name: VIEW_FIELDS_LABEL
+ })
+ .first()
+ .click()
+
+ cy.findByLabelText('Subtitle').should('be.checked')
+ })
+
+ it('should send metadataBlockNames and inputLevels as undefined if use fields from parent is checked', () => {
+ const collectionRepository = {} as CollectionRepository
+ collectionRepository.create = cy.stub().as('createCollection').resolves()
+ collectionRepository.getFacets = cy.stub().resolves(CollectionFacetMother.createFacets())
+
+ cy.mountAuthenticated(
+
+ )
+
+ // Accept suggestion
+ cy.findByRole('button', { name: 'Apply suggestion' }).click()
+ // Select a Category option
+ cy.findByLabelText(/^Category/i).select(1)
+
+ cy.findByRole('button', { name: 'Create Collection' }).click()
+
+ cy.get('@createCollection').should((spy) => {
+ const createCollectionSpy = spy as unknown as Cypress.Agent
+ const collectionDTO = createCollectionSpy.getCall(0).args[0] as CollectionDTO
+
+ const inputLevels = collectionDTO.inputLevels
+ const metadataBlockNames = collectionDTO.metadataBlockNames
+
+ expect(inputLevels).to.be.undefined
+ expect(metadataBlockNames).to.be.undefined
+ })
+ })
+
+ it('should not send metadataBlockNames and inputLevels as undefined if use fields from parent is unchecked', () => {
+ const collectionRepository = {} as CollectionRepository
+ collectionRepository.create = cy.stub().as('createCollection').resolves()
+ collectionRepository.getFacets = cy.stub().resolves(CollectionFacetMother.createFacets())
+
+ cy.mountAuthenticated(
+
+ )
+
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ // Accept suggestion
+ cy.findByRole('button', { name: 'Apply suggestion' }).click()
+ // Select a Category option
+ cy.findByLabelText(/^Category/i).select(1)
+
+ cy.findByRole('button', { name: 'Create Collection' }).click()
+
+ cy.get('@createCollection').should((spy) => {
+ const createCollectionSpy = spy as unknown as Cypress.Agent
+ const collectionDTO = createCollectionSpy.getCall(0).args[0] as CollectionDTO
+
+ const inputLevels = collectionDTO.inputLevels
+ const metadataBlockNames = collectionDTO.metadataBlockNames
+
+ expect(inputLevels).to.not.be.undefined
+ expect(metadataBlockNames).to.not.be.undefined
+ })
+ })
+ })
+
+ describe('InputLevelFieldRow', () => {
+ it('On not composed fields - should unclude and include the field when checking the checkbox', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ // Open citation input levels
+ cy.findByRole('button', {
+ name: VIEW_AND_EDIT_FIELDS_LABEL
+ })
+ .should('exist')
+ .click({ force: true })
+
+ // First unclude the subtitle field
+ cy.findByLabelText('Subtitle').uncheck({ force: true })
+
+ cy.findByLabelText('Subtitle').should('not.be.checked')
+
+ // Go to parent td of Subtitle field and find sibling td to check if Hidden checkbox is there
+ cy.findByLabelText('Subtitle')
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Hidden').should('exist').should('be.checked')
+ })
+
+ // Second include the subtitle field
+ cy.findByLabelText('Subtitle').check({ force: true })
+ cy.findByLabelText('Subtitle').should('be.checked')
+
+ // Go to parent td of Subtitle field and find sibling td to check if Hidden checkbox is not there anymore
+ cy.findByLabelText('Subtitle')
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Hidden').should('not.exist')
+ })
+ })
+
+ it('On composed fields - should unclude and include the child field when checking the checkbox', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ // Open citation input levels
+ cy.findByRole('button', {
+ name: VIEW_AND_EDIT_FIELDS_LABEL
+ })
+ .should('exist')
+ .click()
+
+ // First unclude the other identifier composed field
+ cy.findByLabelText('Other Identifier').uncheck({ force: true })
+
+ cy.findByLabelText('Other Identifier').should('not.be.checked')
+
+ // Go to child fields rows and check if Hidden checkbox is there
+ cy.findByText('Other Identifier Agency', { exact: true })
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Hidden').should('exist').should('be.checked')
+ })
+
+ cy.findByText('Other Identifier Identifier', { exact: true })
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Hidden').should('exist').should('be.checked')
+ })
+
+ // Second include the other identifier composed fields
+ cy.findByLabelText('Other Identifier').check({ force: true })
+ cy.findByLabelText('Other Identifier').should('be.checked')
+
+ // Go to child fields rows and check if Hidden checkbox is not there anymore and they optional checkbox is checked
+ cy.findByText('Other Identifier Agency', { exact: true })
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Hidden').should('not.exist')
+ cy.findByLabelText('Optional').should('be.checked')
+ })
+
+ cy.findByText('Other Identifier Identifier', { exact: true })
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Hidden').should('not.exist')
+ cy.findByLabelText('Optional').should('be.checked')
+ })
+ })
+
+ it('On not composed fields - should select correctly the Required or Optional radios', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ // Open citation input levels
+ cy.findByRole('button', {
+ name: VIEW_AND_EDIT_FIELDS_LABEL
+ })
+ .should('exist')
+ .click()
+
+ // Go to parent td of Subtitle field and find sibling td to select the Required radio
+ cy.findByLabelText('Subtitle')
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Required').should('exist').should('not.be.checked')
+ cy.findByLabelText('Optional').should('exist').should('be.checked')
+
+ cy.findByLabelText('Required').check({ force: true })
+ cy.findByLabelText('Required').should('be.checked')
+ cy.findByLabelText('Optional').should('not.be.checked')
+
+ cy.findByLabelText('Optional').check({ force: true })
+ cy.findByLabelText('Optional').should('be.checked')
+ cy.findByLabelText('Required').should('not.be.checked')
+ })
+
+ // Second set the subtitle field as optional
+ // cy.findByLabelText('Subtitle').uncheck({ force: true })
+
+ // // Go to parent td of Subtitle field and find sibling td to check if Optional radio is there
+ // cy.findByLabelText('Subtitle')
+ // .closest('td')
+ // .next()
+ // .next()
+ // .within(() => {
+ // cy.findByLabelText('Optional').should('exist').should('be.checked')
+ // })
+ })
+
+ it('On composed fields - should select correctly the Required or Optional radios of child fields', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ // Open citation input levels
+ cy.findByRole('button', {
+ name: VIEW_AND_EDIT_FIELDS_LABEL
+ })
+ .should('exist')
+ .click()
+
+ // Go to child fields row and check if Required radio is there and perform the check/uncheck
+ cy.findByText('Other Identifier Identifier', { exact: true })
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Required').should('exist').should('not.be.checked')
+ cy.findByLabelText('Optional').should('exist').should('be.checked')
+
+ cy.findByLabelText('Required').check({ force: true })
+ cy.findByLabelText('Required').should('be.checked')
+ cy.findByLabelText('Optional').should('not.be.checked')
+ })
+
+ cy.findByText('Other Identifier Agency', { exact: true })
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Required').should('exist').should('not.be.checked')
+ cy.findByLabelText('Optional').should('exist').should('be.checked')
+
+ cy.findByLabelText('Required').check({ force: true })
+ cy.findByLabelText('Required').should('be.checked')
+ cy.findByLabelText('Optional').should('not.be.checked')
+
+ cy.findByLabelText('Optional').check({ force: true })
+ cy.findByLabelText('Optional').should('be.checked')
+ cy.findByLabelText('Required').should('not.be.checked')
+ })
+
+ cy.findByText('Other Identifier Identifier', { exact: true })
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Optional').check({ force: true })
+ })
+
+ cy.findByText('Other Identifier Agency', { exact: true })
+ .closest('td')
+ .next()
+ .within(() => {
+ cy.findByLabelText('Required').should('exist').should('not.be.checked')
+ cy.findByLabelText('Optional').should('exist').should('be.checked')
+
+ cy.findByLabelText('Required').check({ force: true })
+ cy.findByLabelText('Required').should('be.checked')
+ cy.findByLabelText('Optional').should('not.be.checked')
+
+ cy.findByLabelText('Optional').check({ force: true })
+ cy.findByLabelText('Optional').should('be.checked')
+ cy.findByLabelText('Required').should('not.be.checked')
+ })
+ })
+ })
+
+ describe('Opens input levels table correctly on different states', () => {
+ it('should open the Citation input levels on view mode', () => {
+ cy.findAllByRole('button', {
+ name: VIEW_FIELDS_LABEL
+ })
+ .first()
+ .should('exist')
+ .click()
+
+ cy.findByRole('table').should('exist').should('be.visible')
+
+ cy.findByRole('table').within(() => {
+ cy.findByText('Title').should('be.visible')
+ cy.findByLabelText('Subtitle').should('be.visible').should('be.disabled')
+ })
+
+ // Close the table
+ cy.findAllByLabelText('Hide input levels table').first().click()
+
+ cy.findByRole('table').should('not.exist')
+ })
+
+ it('should open the Citation input levels on edit mode', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ cy.findByRole('button', {
+ name: VIEW_AND_EDIT_FIELDS_LABEL
+ })
+ .should('exist')
+ .click()
+
+ cy.findByRole('table').should('exist').should('be.visible')
+
+ cy.findByRole('table').within(() => {
+ cy.findByText('Title').should('be.visible')
+ cy.findByLabelText('Subtitle').should('be.visible').should('not.be.disabled')
+ })
+
+ // Close the table
+ cy.findAllByLabelText('Hide input levels table').first().click()
+
+ cy.findByRole('table').should('not.exist')
+ })
+
+ it('should enable fields when opening an input levels table on view mode and checking the block name checkbox', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ cy.findAllByRole('button', {
+ name: VIEW_FIELDS_LABEL
+ })
+ .first()
+ .should('exist')
+ .click()
+
+ cy.findByRole('table').should('exist').should('be.visible')
+
+ cy.findByRole('table').within(() => {
+ cy.findByLabelText('Geographic Unit').should('exist').should('be.disabled')
+ })
+
+ // Now check the Geospatial block name checkbox
+ cy.findByLabelText('Geospatial Metadata').check({ force: true })
+
+ cy.findByRole('table').should('exist').should('be.visible')
+
+ cy.findByRole('table').within(() => {
+ cy.findByLabelText('Geographic Unit').should('exist').should('not.be.disabled')
+ })
+ })
+ })
+
+ it('when unchecking a block name checkbox, should reset the input levels as they were by default', () => {
+ cy.get('@useFieldsFromParentCheckbox').uncheck({ force: true })
+
+ // Check geospatial block name checkbox
+ cy.findByLabelText('Geospatial Metadata').check({ force: true })
+
+ // Open geospatial input levels
+ cy.findAllByRole('button', {
+ name: VIEW_AND_EDIT_FIELDS_LABEL
+ })
+ .last()
+ .should('exist')
+ .click()
+
+ // Set coverage city as required
+ cy.findByLabelText('Geographic Coverage').uncheck({ force: true })
+
+ cy.findByLabelText('Geographic Coverage').should('not.be.checked')
+
+ // Uncheck geospatial block name checkbox
+ cy.findByLabelText('Geospatial Metadata').uncheck({ force: true })
+
+ cy.findByLabelText('Geographic Coverage').should('be.checked')
+ })
+ })
+
+ describe('BrowseSearchFacetsSection functionality', () => {
+ beforeEach(() => {
+ cy.mountAuthenticated(
+
+ )
+
+ cy.get('[data-testid="use-facets-from-parent-checkbox"]').as('useFacetsFromParentCheckbox')
+ cy.get('[data-testid="transfer-list-container"]').as('transferListContainer')
+ cy.findByTestId('left-list-group').as('leftList')
+ cy.findByTestId('actions-column').as('actionsColumn')
+ cy.findByTestId('right-list-group').as('rightList')
+ })
+
+ it('should populate the right list with the default facets', () => {
+ cy.get('@rightList').children().should('have.length', 4)
+
+ cy.get('@rightList').within(() => {
+ cy.findByLabelText('Author Name').should('exist')
+ cy.findByLabelText('Subject').should('exist')
+ cy.findByLabelText('Keyword Term').should('exist')
+ cy.findByLabelText('Deposit Date').should('exist')
+ })
+ })
+
+ it('should reset the newly selected facets when checking the checkbox', () => {
+ cy.get('@useFacetsFromParentCheckbox').should('be.checked')
+ cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
+ cy.get('@useFacetsFromParentCheckbox').should('not.be.checked')
+
+ cy.get('@rightList').should('exist')
+ cy.get('@rightList').children().should('have.length', 4)
+ cy.get('@rightList').within(() => {
+ cy.findByLabelText('Author Name').should('exist')
+ cy.findByLabelText('Subject').should('exist')
+ cy.findByLabelText('Keyword Term').should('exist')
+ cy.findByLabelText('Deposit Date').should('exist')
+ })
+
+ cy.get('@transferListContainer').within(() => {
+ cy.findByLabelText('Topic Classification Term').check({ force: true })
+
+ cy.findByLabelText('Topic Classification Term').should('be.checked')
+ })
+
+ cy.get('@actionsColumn').within(() => {
+ cy.findByLabelText('move selected to right').click()
+ })
+
+ cy.get('@leftList').within(() => {
+ cy.findByLabelText('Topic Classification Term').should('not.exist')
+ })
+
+ cy.get('@rightList').children().should('have.length', 5)
+
+ cy.get('@rightList').within(() => {
+ cy.findByLabelText('Author Name').should('exist')
+ cy.findByLabelText('Subject').should('exist')
+ cy.findByLabelText('Keyword Term').should('exist')
+ cy.findByLabelText('Deposit Date').should('exist')
+ cy.findByLabelText('Topic Classification Term').should('exist')
+ })
+
+ cy.get('@useFacetsFromParentCheckbox').check({ force: true })
+
+ cy.get('@rightList').children().should('have.length', 4)
+
+ cy.get('@rightList').within(() => {
+ cy.findByLabelText('Author Name').should('exist')
+ cy.findByLabelText('Subject').should('exist')
+ cy.findByLabelText('Keyword Term').should('exist')
+ cy.findByLabelText('Deposit Date').should('exist')
+ cy.findByLabelText('Topic Classification Term').should('not.exist')
+ })
+
+ cy.get('@leftList').within(() => {
+ cy.findByLabelText('Topic Classification Term').should('exist')
+ })
+ })
+
+ it('should populate the select to filter facets by blocks correctly', () => {
+ cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
+
+ cy.findByTestId('select-facets-by-block').within(() => {
+ cy.findByLabelText('Toggle options menu').click()
+
+ cy.findAllByText('All Metadata Fields').should('have.length', 2)
+
+ cy.findByText('Citation Metadata').should('have.length', 1)
+ cy.findByText('Geospatial Metadata').should('have.length', 1)
+ cy.findByText('Astronomy and Astrophysics Metadata').should('have.length', 1)
+ cy.findByText('Life Sciences Metadata').should('have.length', 1)
+ cy.findByText('Journal Metadata').should('have.length', 1)
+ cy.findByText('Social Science and Humanities Metadata').should('have.length', 1)
+ })
+ })
+
+ it('should filter the facets by blocks correctly', () => {
+ cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
+
+ cy.findByTestId('select-facets-by-block').within(() => {
+ cy.findByLabelText('Toggle options menu').click()
+
+ cy.findByText('Journal Metadata').click()
+ })
+
+ cy.get('@leftList').children().should('have.length', 4)
+
+ cy.get('@leftList').within(() => {
+ cy.findByLabelText('Journal Volume').should('exist')
+ cy.findByLabelText('Journal Issue').should('exist')
+ cy.findByLabelText('Journal Publication Date').should('exist')
+ cy.findByLabelText('Type of Article').should('exist')
+ })
+
+ cy.findByTestId('select-facets-by-block').within(() => {
+ cy.findByLabelText('Toggle options menu').click()
+
+ cy.findByText('Social Science and Humanities Metadata').click()
+ })
+
+ cy.get('@leftList').children().should('have.length', 5)
+
+ cy.get('@leftList').within(() => {
+ cy.findByLabelText('Unit of Analysis').should('exist')
+ cy.findByLabelText('Universe').should('exist')
+ cy.findByLabelText('Time Method').should('exist')
+ cy.findByLabelText('Frequency').should('exist')
+ cy.findByLabelText('Response Rate').should('exist')
+ })
+
+ cy.findByTestId('select-facets-by-block').within(() => {
+ cy.findByLabelText('Toggle options menu').click()
+
+ cy.findByText('All Metadata Fields').click()
+ })
+
+ cy.get('@leftList')
+ .children()
+ .should('have.length', allFacetableMetadataFields.length - collectionFacets.length)
+ })
+
+ it('should reset the select to filter by facets when checking the checkbox', () => {
+ cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
+
+ cy.findByTestId('select-facets-by-block').within(() => {
+ cy.findByLabelText('Toggle options menu').click()
+
+ cy.findByText('Journal Metadata').click()
+ })
+
+ cy.get('@leftList').children().should('have.length', 4)
+
+ cy.get('@leftList').within(() => {
+ cy.findByLabelText('Journal Volume').should('exist')
+ cy.findByLabelText('Journal Issue').should('exist')
+ cy.findByLabelText('Journal Publication Date').should('exist')
+ cy.findByLabelText('Type of Article').should('exist')
+ })
+
+ cy.get('@useFacetsFromParentCheckbox').check({ force: true })
+
+ cy.get('@leftList')
+ .children()
+ .should('have.length', allFacetableMetadataFields.length - collectionFacets.length)
+
+ cy.findByTestId('select-facets-by-block').within(() => {
+ cy.findByText('All Metadata Fields').should('exist')
+ })
+ })
+
+ it('should show error message when there is no selected facet in the right list', () => {
+ cy.get('@useFacetsFromParentCheckbox').uncheck({ force: true })
+ // Move all facets to the left list
+ cy.get('@actionsColumn').within(() => {
+ cy.findByLabelText('move all left').click()
+ })
+
+ cy.findByText('At least one facet must be selected.').should('exist')
+
+ // Now move one facet to the right list and the error message should disappear
+ cy.get('@leftList').within(() => {
+ cy.findByLabelText('Journal Volume').click()
+ })
+
+ cy.get('@actionsColumn').within(() => {
+ cy.findByLabelText('move selected to right').click()
+ })
+
+ cy.findByText('At least one facet must be selected.').should('not.exist')
+ })
+ })
+ })
+
+ describe('on edit mode', () => {
+ it('should render the form', () => {
+ cy.mountAuthenticated(
+
+ )
+ cy.findByTestId('collection-form').should('exist')
+ })
+
+ it('submits a valid form and succeed', () => {
+ cy.customMount(
+
+ )
+ // Change affiliation in order to be able to save
+ cy.findByLabelText(/^Affiliation/i)
+ .clear()
+ .type('New Affiliation')
+
+ cy.findByRole('button', { name: 'Save Changes' }).click()
+
+ cy.findByText('Error').should('not.exist')
+ cy.findByText('Success!').should('exist')
+ })
+
+ it('submits a valid form and fails', () => {
+ collectionRepository.edit = cy.stub().rejects(new Error('Error editing collection'))
+
+ cy.customMount(
+
+ )
+
+ // Change affiliation in order to be able to save
+ cy.findByLabelText(/^Affiliation/i)
+ .clear()
+ .type('New Affiliation')
+
+ cy.findByRole('button', { name: 'Save Changes' }).click()
+
+ cy.findByText('Error').should('exist')
+ cy.findByText(/Error editing collection/).should('exist')
+ cy.findByText('Success!').should('not.exist')
+ })
+
+ it('should not show the Host Collection field when editing the root collection', () => {
+ cy.mountAuthenticated(
+
+ )
+
+ cy.findByLabelText(/^Host Collection/i).should('not.exist')
+ })
+
+ it('should not show Use metadata fields from [Parent] and Use browse/search facets from [Parent] when editing the root collection', () => {
+ cy.mountAuthenticated(
+
+ )
+
+ cy.findByTestId('use-fields-from-parent-checkbox').should('not.exist')
+ cy.findByTestId('use-facets-from-parent-checkbox').should('not.exist')
+ })
+
+ it('should prefill the top fields with the collection data', () => {
+ cy.mountAuthenticated(
+
+ )
+
+ cy.findByLabelText(/^Collection Name/i).should('have.value', collectionBeingEdited.name)
+ cy.findByLabelText(/^Affiliation/i).should('have.value', collectionBeingEdited.affiliation)
+ cy.findByLabelText(/^Identifier/i).should('have.value', collectionBeingEdited.id)
+ cy.findByLabelText(/^Category/i).should('have.value', collectionBeingEdited.type)
+ cy.findAllByLabelText(/^Description/i)
+ .first()
+ .should('have.value', collectionBeingEdited.description)
+
+ cy.findByLabelText(/^Email/i).should('have.value', collectionBeingEdited.contacts[0].email)
+ })
+
+ it('when editing the root collection, should not send metadataBlockNames and inputLevels as undefined if any of them have changed', () => {
+ const collectionRepository = {} as CollectionRepository
+ collectionRepository.edit = cy.stub().as('editCollection').resolves()
+ collectionRepository.getFacets = cy.stub().resolves(CollectionFacetMother.createFacets())
+
+ cy.mountAuthenticated(
+
+ )
+
+ // Check a new metadata block
+ cy.findByLabelText('Geospatial Metadata').check({ force: true })
+
+ cy.findByRole('button', { name: 'Save Changes' }).click()
+
+ cy.get('@editCollection').should((spy) => {
+ const editCollectionSpy = spy as unknown as Cypress.Agent
+ const collectionDTO = editCollectionSpy.getCall(0).args[1] as CollectionDTO
+
+ const inputLevels = collectionDTO.inputLevels
+ const metadataBlockNames = collectionDTO.metadataBlockNames
+
+ expect(inputLevels).not.to.be.undefined
+ expect(metadataBlockNames).not.to.be.undefined
+ })
+ })
+
+ it('when editing the root collection, should send metadataBlockNames and inputLevels as undefined if they didnt changed', () => {
+ const collectionRepository = {} as CollectionRepository
+ collectionRepository.edit = cy.stub().as('editCollection').resolves()
+ collectionRepository.getFacets = cy.stub().resolves(CollectionFacetMother.createFacets())
+
+ cy.mountAuthenticated(
+
+ )
+
+ // Change affiliation in order to be able to save
+ cy.findByLabelText(/^Affiliation/i)
+ .clear()
+ .type('New Affiliation')
+
+ cy.findByRole('button', { name: 'Save Changes' }).click()
+
+ cy.get('@editCollection').should((spy) => {
+ const editCollectionSpy = spy as unknown as Cypress.Agent
+ const collectionDTO = editCollectionSpy.getCall(0).args[1] as CollectionDTO
+
+ const inputLevels = collectionDTO.inputLevels
+ const metadataBlockNames = collectionDTO.metadataBlockNames
+
+ expect(inputLevels).to.be.undefined
+ expect(metadataBlockNames).to.be.undefined
+ })
+ })
+
+ it('when editing root collection, should not send facetIds as undefined if they have changed', () => {
+ const collectionRepository = {} as CollectionRepository
+ collectionRepository.edit = cy.stub().as('editCollection').resolves()
+ collectionRepository.getFacets = cy.stub().resolves(CollectionFacetMother.createFacets())
+
+ cy.mountAuthenticated(
+
+ )
+
+ cy.findByTestId('left-list-group').as('leftList')
+ cy.findByTestId('actions-column').as('actionsColumn')
+ cy.findByTestId('right-list-group').as('rightList')
+
+ // Change the default selected facets
+ cy.get('@leftList').within(() => {
+ cy.findByText('Topic Classification Term').click()
+ })
+
+ cy.get('@actionsColumn').within(() => {
+ cy.findByLabelText('move selected to right').click()
+ })
+
+ cy.get('@rightList').within(() => {
+ cy.findByLabelText('Topic Classification Term').should('exist')
+ })
+
+ cy.findByRole('button', { name: 'Save Changes' }).click()
+
+ cy.get('@editCollection').should((spy) => {
+ const editCollectionSpy = spy as unknown as Cypress.Agent
+ const collectionDTO = editCollectionSpy.getCall(0).args[1] as CollectionDTO
+
+ const facetIds = collectionDTO.facetIds
+
+ expect(facetIds).to.not.be.undefined
+ })
+ })
+
+ it('when editing root collection, should send facetIds as undefined if they didnt changed', () => {
+ const collectionRepository = {} as CollectionRepository
+ collectionRepository.edit = cy.stub().as('editCollection').resolves()
+ collectionRepository.getFacets = cy.stub().resolves(CollectionFacetMother.createFacets())
+
+ cy.mountAuthenticated(
+
+ )
+
+ // Change affiliation in order to be able to save
+ cy.findByLabelText(/^Affiliation/i)
+ .clear()
+ .type('New Affiliation')
+
+ cy.findByRole('button', { name: 'Save Changes' }).click()
+
+ cy.get('@editCollection').should((spy) => {
+ const editCollectionSpy = spy as unknown as Cypress.Agent
+ const collectionDTO = editCollectionSpy.getCall(0).args[1] as CollectionDTO
+
+ const facetIds = collectionDTO.facetIds
+
+ expect(facetIds).to.be.undefined
+ })
+ })
+ })
+})
diff --git a/tests/component/sections/create-collection/useGetAllFacetableMetadataFields.spec.tsx b/tests/component/shared/hooks/useGetAllFacetableMetadataFields.spec.tsx
similarity index 87%
rename from tests/component/sections/create-collection/useGetAllFacetableMetadataFields.spec.tsx
rename to tests/component/shared/hooks/useGetAllFacetableMetadataFields.spec.tsx
index ed47c7e8a..8c7b6164e 100644
--- a/tests/component/sections/create-collection/useGetAllFacetableMetadataFields.spec.tsx
+++ b/tests/component/shared/hooks/useGetAllFacetableMetadataFields.spec.tsx
@@ -1,7 +1,7 @@
import { act, renderHook } from '@testing-library/react'
-import { MetadataBlockInfoRepository } from '../../../../src/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
-import { MetadataBlockInfoMother } from '../../metadata-block-info/domain/models/MetadataBlockInfoMother'
-import { useGetAllFacetableMetadataFields } from '../../../../src/sections/create-collection/useGetAllFacetableMetadataFields'
+import { MetadataBlockInfoMother } from '@tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { useGetAllFacetableMetadataFields } from '@/shared/hooks/useGetAllFacetableMetadataFields'
const metadataBlockInfoRepository: MetadataBlockInfoRepository = {} as MetadataBlockInfoRepository
const allFacetableMetadataFieldsMock = MetadataBlockInfoMother.createFacetableMetadataFields()
diff --git a/tests/component/sections/create-collection/useGetAllMetadataBlocksInfo.spec.tsx b/tests/component/shared/hooks/useGetAllMetadataBlocksInfo.spec.tsx
similarity index 84%
rename from tests/component/sections/create-collection/useGetAllMetadataBlocksInfo.spec.tsx
rename to tests/component/shared/hooks/useGetAllMetadataBlocksInfo.spec.tsx
index b61e51809..a2f05c3b2 100644
--- a/tests/component/sections/create-collection/useGetAllMetadataBlocksInfo.spec.tsx
+++ b/tests/component/shared/hooks/useGetAllMetadataBlocksInfo.spec.tsx
@@ -1,8 +1,8 @@
import { act, renderHook } from '@testing-library/react'
-import { MetadataBlockInfoRepository } from '../../../../src/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
-import { MetadataBlockInfoMother } from '../../metadata-block-info/domain/models/MetadataBlockInfoMother'
-import { useGetAllMetadataBlocksInfo } from '../../../../src/sections/create-collection/useGetAllMetadataBlocksInfo'
-import { MetadataFieldsHelper } from '../../../../src/sections/shared/form/DatasetMetadataForm/MetadataFieldsHelper'
+import { MetadataBlockInfoMother } from '@tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { MetadataFieldsHelper } from '@/sections/shared/form/DatasetMetadataForm/MetadataFieldsHelper'
+import { useGetAllMetadataBlocksInfo } from '@/shared/hooks/useGetAllMetadataBlocksInfo'
const metadataBlockInfoRepository: MetadataBlockInfoRepository = {} as MetadataBlockInfoRepository
const allMetadataBlocksInfoMock = MetadataBlockInfoMother.getAllBlocks()
diff --git a/tests/component/sections/create-collection/useGetCollectionFacets.spec.tsx b/tests/component/shared/hooks/useGetCollectionFacets.spec.tsx
similarity index 88%
rename from tests/component/sections/create-collection/useGetCollectionFacets.spec.tsx
rename to tests/component/shared/hooks/useGetCollectionFacets.spec.tsx
index 797eef2e1..7e328147c 100644
--- a/tests/component/sections/create-collection/useGetCollectionFacets.spec.tsx
+++ b/tests/component/shared/hooks/useGetCollectionFacets.spec.tsx
@@ -1,7 +1,7 @@
import { act, renderHook } from '@testing-library/react'
-import { useGetCollectionFacets } from '../../../../src/sections/create-collection/useGetCollectionFacets'
-import { CollectionRepository } from '../../../../src/collection/domain/repositories/CollectionRepository'
-import { CollectionFacetMother } from '../../collection/domain/models/CollectionFacetMother'
+import { CollectionFacetMother } from '@tests/component/collection/domain/models/CollectionFacetMother'
+import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository'
+import { useGetCollectionFacets } from '@/shared/hooks/useGetCollectionFacets'
const collectionRepository: CollectionRepository = {} as CollectionRepository
const collectionFacetsMock = CollectionFacetMother.createFacets()
diff --git a/tests/component/sections/create-collection/useGetCollectionMetadataBlocksInfo.spec.tsx b/tests/component/shared/hooks/useGetCollectionMetadataBlocksInfo.spec.tsx
similarity index 87%
rename from tests/component/sections/create-collection/useGetCollectionMetadataBlocksInfo.spec.tsx
rename to tests/component/shared/hooks/useGetCollectionMetadataBlocksInfo.spec.tsx
index 5be83bc26..3da86a2bc 100644
--- a/tests/component/sections/create-collection/useGetCollectionMetadataBlocksInfo.spec.tsx
+++ b/tests/component/shared/hooks/useGetCollectionMetadataBlocksInfo.spec.tsx
@@ -1,7 +1,7 @@
import { act, renderHook } from '@testing-library/react'
-import { MetadataBlockInfoRepository } from '../../../../src/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
-import { MetadataBlockInfoMother } from '../../metadata-block-info/domain/models/MetadataBlockInfoMother'
-import { useGetCollectionMetadataBlocksInfo } from '../../../../src/sections/create-collection/useGetCollectionMetadataBlocksInfo'
+import { MetadataBlockInfoMother } from '@tests/component/metadata-block-info/domain/models/MetadataBlockInfoMother'
+import { MetadataBlockInfoRepository } from '@/metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
+import { useGetCollectionMetadataBlocksInfo } from '@/shared/hooks/useGetCollectionMetadataBlocksInfo'
const metadataBlockInfoRepository: MetadataBlockInfoRepository = {} as MetadataBlockInfoRepository
const metadataBlocksInfoMock = MetadataBlockInfoMother.getAllBlocks()
diff --git a/tests/e2e-integration/integration/collection/CollectionJSDataverseRepository.spec.ts b/tests/e2e-integration/integration/collection/CollectionJSDataverseRepository.spec.ts
index 67515343c..0ca4b4e34 100644
--- a/tests/e2e-integration/integration/collection/CollectionJSDataverseRepository.spec.ts
+++ b/tests/e2e-integration/integration/collection/CollectionJSDataverseRepository.spec.ts
@@ -7,8 +7,10 @@ import {
UpwardHierarchyNode
} from '../../../../src/shared/hierarchy/domain/models/UpwardHierarchyNode'
import { Collection } from '../../../../src/collection/domain/models/Collection'
+import { CollectionType } from '@/collection/domain/models/CollectionType'
const collectionRepository = new CollectionJSDataverseRepository()
+
const collectionExpected: Collection = {
id: 'new-collection',
name: 'Scientific Research',
@@ -24,8 +26,22 @@ const collectionExpected: Collection = {
undefined,
new UpwardHierarchyNode('Root', DvObjectType.COLLECTION, 'root', undefined, undefined, true)
),
- inputLevels: undefined
+ inputLevels: undefined,
+ type: CollectionType.LABORATORY,
+ contacts: [
+ {
+ email: 'pi@example.edu',
+ displayOrder: 0
+ },
+ {
+ email: 'student@example.edu',
+ displayOrder: 1
+ }
+ ],
+ isMetadataBlockRoot: false,
+ isFacetRoot: false
}
+
describe('Collection JSDataverse Repository', () => {
before(() => TestsUtils.setup())
beforeEach(() => {
@@ -40,6 +56,7 @@ describe('Collection JSDataverse Repository', () => {
throw new Error('Collection not found')
}
+ console.log('collection', collection)
expect(collection).to.deep.equal(collectionExpected)
})
})