Skip to content
Open
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4bc1be8
feat: initial link collection DDD
g-saracca Sep 11, 2025
dbea949
feat(DesignSystem): add className prop
g-saracca Sep 11, 2025
dfab469
feat: CollectionLinkSelect
g-saracca Sep 11, 2025
76395b7
feat(Design System): add size prop
g-saracca Sep 11, 2025
af42829
feat: improve styles
g-saracca Sep 11, 2025
4d39c75
feat: show select only for super user and non root collection
g-saracca Sep 15, 2025
87ecdaf
feat: no collections and only one cases
g-saracca Sep 15, 2025
41831d3
fix: increase right padding to avoid toast content to overlap with to…
g-saracca Sep 15, 2025
bac58f6
feat: link collection done
g-saracca Sep 15, 2025
04aacf8
refactor: simplify LinkDatasetButton rendering logic and remove unnec…
g-saracca Sep 15, 2025
40c18f2
feat: link and unlink methods
g-saracca Sep 17, 2025
b5123ae
feat: add getCollection Links method
g-saracca Sep 17, 2025
c7b6a1b
feat: add getDatasetLinkedCollections method
g-saracca Sep 17, 2025
2dac067
feat: link dataset functionality done
g-saracca Sep 17, 2025
3ace175
fix: add wrapper in toast for responsiveness
g-saracca Sep 17, 2025
8b62c8b
refactor: languages location for linking
g-saracca Sep 17, 2025
1da131c
some tweaks
g-saracca Sep 17, 2025
90efd7a
fix: rename hook
g-saracca Sep 18, 2025
2b1dca9
refactor: remove unused collection links functionality
g-saracca Sep 18, 2025
589b797
feat: link collection stories
g-saracca Sep 18, 2025
8b3e8b9
feat: add stories for link dataset
g-saracca Sep 18, 2025
dbc2d99
test: add unit cases for linking collection
g-saracca Sep 18, 2025
c197b79
test: add unit cases for linking dataset
g-saracca Sep 18, 2025
ce2216f
chore: change js-dv version
g-saracca Sep 19, 2025
650f1ab
feat: add get collections for unlinking to repos
g-saracca Sep 19, 2025
921878a
feat: unlink dataset functionaility
g-saracca Sep 19, 2025
47efad0
feat: unlink dataset btn
g-saracca Sep 19, 2025
176c0ae
test: unlink dataset btn unit tests and refactor link dataset btn tests
g-saracca Sep 19, 2025
9460643
feat: add stories, and move components
g-saracca Sep 19, 2025
7f9f8de
test: try fixing flaky test
g-saracca Sep 22, 2025
b095684
feat: get collection links method added, unused
g-saracca Sep 22, 2025
9e23783
feat: add icon to collection and dataset cards, update tests and stor…
g-saracca Sep 22, 2025
1b1f4b7
fix: little tweak to one collection only wording
g-saracca Sep 22, 2025
adcd0ae
Merge branch 'develop' into feat/361-link-collection-and-link-dataset
g-saracca Sep 29, 2025
2000392
docs: add to changelog
g-saracca Sep 29, 2025
667762c
Merge branch 'develop' into feat/361-link-collection-and-link-dataset
g-saracca Oct 3, 2025
9f7673f
fix: update package and fix stories
g-saracca Oct 3, 2025
f19347f
chore: use pr version
g-saracca Oct 3, 2025
883e780
chore: use js-dv alpha version
g-saracca Oct 14, 2025
764bbed
fix typos and change wording
g-saracca Oct 14, 2025
151ad79
tests: fix after wording change
g-saracca Oct 14, 2025
4821797
Merge branch 'develop' into feat/361-link-collection-and-link-dataset
g-saracca Oct 14, 2025
20fd016
fix lint
g-saracca Oct 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
- Dataset Templates Selector in the Create Dataset page.
- Metadata Export Dropdown to the metadata tab of Dataset Page and File Page.
- External Tools integration. All types supported: Explore, Configure, Preview and Query tools in Dataset and File pages. Still not showing external tools for Auxiliary Files as additional development is needed.
- Link Collection and Link Dataset features.

### Changed

Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@dnd-kit/sortable": "8.0.0",
"@dnd-kit/utilities": "3.2.2",
"@faker-js/faker": "7.6.0",
"@iqss/dataverse-client-javascript": "2.0.0-alpha.67",
"@iqss/dataverse-client-javascript": "2.1.0-pr378.93da193",
"@iqss/dataverse-design-system": "*",
"@istanbuljs/nyc-config-typescript": "1.0.2",
"@tanstack/react-table": "8.9.2",
Expand Down
2 changes: 2 additions & 0 deletions packages/design-system/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
- Support for options with a shape of `{ label: string; value: string; }[]` instead of just `string[]`.
- **ButtonGroup:**
- Fix styles for vertical button groups when using tooltips.
- **FormText:** Add `className` prop to allow custom styling.
- **FormInput:** Add `size` prop to allow different input sizes (e.g., 'sm', 'lg'). Defaults to standard size if not specified.

# [2.0.2](https://github.com/IQSS/dataverse-frontend/compare/@iqss/[email protected]...@iqss/[email protected]) (2024-06-23)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface FormInputProps extends Omit<React.InputHTMLAttributes<FormInput
required?: boolean
autoFocus?: boolean
autoComplete?: string
size?: 'sm' | 'lg'
}

export const FormInput = React.forwardRef(function FormInput(
Expand All @@ -28,6 +29,7 @@ export const FormInput = React.forwardRef(function FormInput(
required,
autoFocus,
autoComplete,
size,
...props
}: FormInputProps,
ref
Expand All @@ -45,6 +47,7 @@ export const FormInput = React.forwardRef(function FormInput(
required={required}
autoFocus={autoFocus}
autoComplete={autoComplete}
size={size}
ref={ref as React.ForwardedRef<HTMLInputElement>}
{...props}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ReactNode } from 'react'
import { Form as FormBS } from 'react-bootstrap'

export function FormText({ children }: { children: ReactNode }) {
return <FormBS.Text muted>{children}</FormBS.Text>
export function FormText({ children, className }: { children: ReactNode; className?: string }) {
return (
<FormBS.Text muted className={className}>
{children}
</FormBS.Text>
)
}
8 changes: 7 additions & 1 deletion public/locales/en/collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,11 @@
},
"collectionDeletedSuccess": "Your collection has been deleted.",
"defaultCollectionDeleteError": "Something went wrong deleting the collection. Try again later.",
"description": "Description"
"description": "Description",
"linkCollection": {
"title": "Link Collection",
"helper": "Choose which of your collections you would like to link this collection to.",
"save": "Save Linked Collection",
"success": "<wrapper>{{linkedCollectionName}} has been successfully linked to <a>{{linkingCollectionName}}</a></wrapper>"
}
}
12 changes: 11 additions & 1 deletion public/locales/en/dataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,17 @@
"removeCurrentStatus": "Remove Current Status"
},
"linkDataset": {
"title": "Link Dataset"
"title": "Link Dataset",
"helper": "Choose which of your collections you would like to link this dataset to.",
"linkedCollections": "Note: This dataset is already linked to the following collections:",
"save": "Save Linked Dataset",
"success": "<wrapper>Dataset from {{parentCollection}} has been successfully linked to <a>{{linkingCollectionName}}</a></wrapper>"
},
"unlinkDataset": {
"title": "Unlink Dataset",
"helper": "Choose which of your collections you would like to unlink this dataset from.",
"save": "Remove Linked Dataset",
"success": "<wrapper>Dataset unlinked from <a>{{unlinkingCollectionName}}</a></wrapper>"
},
"editDataset": {
"title": "Edit Dataset",
Expand Down
11 changes: 11 additions & 0 deletions public/locales/en/shared.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"dragHandleLabel": "press space to select and keys to drag",
"unknown": "Unknown",
"find": "Find",
"link": "Link",
"allowPopups": "You must enable popups in your browser to open external tools in a new window or tab.",
"externalToolOpeningFailed": "There was a problem opening the external tool. Please try again.",
"exportMetadata": "Export Metadata",
Expand Down Expand Up @@ -292,6 +293,16 @@
"truncateMoreTip": "Click to read the full {{contentName}}",
"truncateLessTip": "Click to collapse the {{contentName}}"
},
"collectionLinkSelect": {
"label": "Your Collection",
"select": "Select a Collection",
"placeholder": "Enter Collection Name",
"noOptions": "No collections found",
"onlyOneCollectionToLink": "You have one collection you can link this {{linkingObjectType}} to.",
"onlyOneCollectionToUnlink": "You have one collection you can unlink this {{linkingObjectType}} from.",
"noCollectionsToLink": "No collections are showing up to link. They may already be linked, or you might not have any yet. Create a new collection to continue.",
"noCollectionsToUnlink": "No collections are showing up to unlink."
},
"exploreOptions": "Explore Options",
"queryOptions": "Query Options",
"configureOptions": "Configure Options"
Expand Down
1 change: 1 addition & 0 deletions src/assets/react-toastify-custom.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
--toastify-color-warning: #{$dv-warning-color};
--toastify-color-error: #{$dv-danger-color};
--toastify-font-family: #{$dv-font-family};
--toastify-toast-padding: 14px 28px 14px 14px;
}
1 change: 1 addition & 0 deletions src/collection/domain/models/CollectionItemTypePreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export interface CollectionItemTypePreview {
parentCollectionName: string
parentCollectionAlias: string
userRoles?: string[]
isLinked?: boolean
}
7 changes: 7 additions & 0 deletions src/collection/domain/models/CollectionLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { CollectionSummary } from './CollectionSummary'

export interface CollectionLinks {
linkedCollections: CollectionSummary[]
collectionsLinkingToThis: CollectionSummary[]
linkedDatasets: { persistentId: string; title: string }[]
}
5 changes: 5 additions & 0 deletions src/collection/domain/models/CollectionSummary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface CollectionSummary {
id: number
alias: string
displayName: string
}
18 changes: 18 additions & 0 deletions src/collection/domain/repositories/CollectionRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { CollectionDTO } from '../useCases/DTOs/CollectionDTO'
import { FeaturedItemsDTO } from '../useCases/DTOs/FeaturedItemsDTO'
import { CollectionItemType } from '@/collection/domain/models/CollectionItemType'
import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus'
import { LinkingObjectType } from '../useCases/getCollectionsForLinking'
import { CollectionSummary } from '../models/CollectionSummary'
import { CollectionLinks } from '../models/CollectionLinks'

export interface CollectionRepository {
getById: (id?: string) => Promise<Collection>
Expand Down Expand Up @@ -41,4 +44,19 @@ export interface CollectionRepository {
): Promise<FeaturedItem[]>
deleteFeaturedItems(collectionIdOrAlias: number | string): Promise<void>
deleteFeaturedItem(featuredItemId: number): Promise<void>
getForLinking(
objectType: LinkingObjectType,
id: number | string,
searchTerm?: string
): Promise<CollectionSummary[]>
getForUnlinking(
objectType: LinkingObjectType,
id: number | string,
searchTerm?: string
): Promise<CollectionSummary[]>
link(
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void>
getLinks(collectionIdOrAlias: number | string): Promise<CollectionLinks>
}
9 changes: 9 additions & 0 deletions src/collection/domain/useCases/getCollectionLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CollectionLinks } from '../models/CollectionLinks'
import { CollectionRepository } from '../repositories/CollectionRepository'

export async function getCollectionLinks(
collectionRepository: CollectionRepository,
collectionIdOrAlias: string | number
): Promise<CollectionLinks> {
return collectionRepository.getLinks(collectionIdOrAlias)
}
13 changes: 13 additions & 0 deletions src/collection/domain/useCases/getCollectionsForLinking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { CollectionRepository } from '../repositories/CollectionRepository'
import { CollectionSummary } from '../models/CollectionSummary'

export type LinkingObjectType = 'collection' | 'dataset'

export async function getCollectionsForLinking(
collectionRepository: CollectionRepository,
objectType: LinkingObjectType,
id: number | string,
searchTerm?: string
): Promise<CollectionSummary[]> {
return collectionRepository.getForLinking(objectType, id, searchTerm)
}
12 changes: 12 additions & 0 deletions src/collection/domain/useCases/getCollectionsForUnlinking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CollectionRepository } from '../repositories/CollectionRepository'
import { CollectionSummary } from '../models/CollectionSummary'
import { LinkingObjectType } from './getCollectionsForLinking'

export async function getCollectionsForUnlinking(
collectionRepository: CollectionRepository,
objectType: LinkingObjectType,
id: number | string,
searchTerm?: string
): Promise<CollectionSummary[]> {
return collectionRepository.getForUnlinking(objectType, id, searchTerm)
}
9 changes: 9 additions & 0 deletions src/collection/domain/useCases/linkCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CollectionRepository } from '../repositories/CollectionRepository'

export async function linkCollection(
collectionRepository: CollectionRepository,
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void> {
return collectionRepository.link(linkedCollectionIdOrAlias, linkingCollectionIdOrAlias)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
updateCollectionFeaturedItems,
deleteCollectionFeaturedItems,
deleteCollection,
deleteCollectionFeaturedItem
deleteCollectionFeaturedItem,
getCollectionsForLinking,
linkCollection,
getCollectionLinks
} from '@iqss/dataverse-client-javascript'
import { JSCollectionMapper } from '../mappers/JSCollectionMapper'
import { CollectionDTO } from '../../domain/useCases/DTOs/CollectionDTO'
Expand All @@ -28,6 +31,9 @@ import { FeaturedItemsDTO } from '@/collection/domain/useCases/DTOs/FeaturedItem
import { MyDataCollectionItemSubset } from '@/collection/domain/models/MyDataCollectionItemSubset'
import { CollectionItemType } from '@/collection/domain/models/CollectionItemType'
import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus'
import { CollectionSummary } from '@/collection/domain/models/CollectionSummary'
import { LinkingObjectType } from '@/collection/domain/useCases/getCollectionsForLinking'
import { CollectionLinks } from '@/collection/domain/models/CollectionLinks'

export class CollectionJSDataverseRepository implements CollectionRepository {
getById(id?: string): Promise<Collection> {
Expand Down Expand Up @@ -142,4 +148,31 @@ export class CollectionJSDataverseRepository implements CollectionRepository {
deleteFeaturedItem(featuredItemId: number): Promise<void> {
return deleteCollectionFeaturedItem.execute(featuredItemId)
}

getForLinking(
objectType: LinkingObjectType,
id: number | string,
searchTerm?: string
): Promise<CollectionSummary[]> {
return getCollectionsForLinking.execute(objectType, id, searchTerm)
}

getForUnlinking(
objectType: LinkingObjectType,
id: number | string,
searchTerm?: string
): Promise<CollectionSummary[]> {
return getCollectionsForLinking.execute(objectType, id, searchTerm, true)
}

link(
linkedCollectionIdOrAlias: number | string,
linkingCollectionIdOrAlias: number | string
): Promise<void> {
return linkCollection.execute(linkedCollectionIdOrAlias, linkingCollectionIdOrAlias)
}

getLinks(collectionIdOrAlias: number | string): Promise<CollectionLinks> {
return getCollectionLinks.execute(collectionIdOrAlias)
}
}
63 changes: 63 additions & 0 deletions src/dataset/domain/hooks/useGetDatasetLinkedCollections.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useCallback, useEffect, useState } from 'react'
import { ReadError } from '@iqss/dataverse-client-javascript'
import { JSDataverseReadErrorHandler } from '@/shared/helpers/JSDataverseReadErrorHandler'
import { CollectionSummary } from '@/collection/domain/models/CollectionSummary'
import { DatasetRepository } from '../repositories/DatasetRepository'
import { getDatasetLinkedCollections } from '../useCases/getDatasetLinkedCollections'

interface useGetDatasetLinkedCollectionsProps {
datasetRepository: DatasetRepository
datasetId: string | number
autoFetch?: boolean
}

export const useGetDatasetLinkedCollections = ({
datasetRepository,
datasetId,
autoFetch = true
}: useGetDatasetLinkedCollectionsProps) => {
const [datasetLinkedCollections, setDatasetLinkedCollections] = useState<CollectionSummary[]>([])
const [isLoadingDatasetLinkedCollections, setIsLoadingDatasetLinkedCollections] =
useState<boolean>(autoFetch)
const [errorGetDatasetLinkedCollections, setErrorGetDatasetLinkedCollections] = useState<
string | null
>(null)

const fetchDatasetLinkedCollections = useCallback(async () => {
setIsLoadingDatasetLinkedCollections(true)
setErrorGetDatasetLinkedCollections(null)

try {
const linkedCollections = await getDatasetLinkedCollections(datasetRepository, datasetId)

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

setErrorGetDatasetLinkedCollections(formattedError)
} else {
setErrorGetDatasetLinkedCollections(
'Something went wrong getting the dataset linked collections. Try again later.'
)
}
} finally {
setIsLoadingDatasetLinkedCollections(false)
}
}, [datasetRepository, datasetId])

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

return {
datasetLinkedCollections,
isLoadingDatasetLinkedCollections,
errorGetDatasetLinkedCollections,
fetchDatasetLinkedCollections
}
}
1 change: 1 addition & 0 deletions src/dataset/domain/models/DatasetItemTypePreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export interface DatasetItemTypePreview {
parentCollectionName: string
parentCollectionAlias: string
userRoles?: string[]
isLinked?: boolean
}
Loading
Loading