): FileExternalToolResolved {
5 | return {
6 | displayName: 'File Explore Tool',
7 | fileId: 1,
8 | preview: false,
9 | toolUrlResolved: 'https://example.com/explore-tool?fileId=1',
10 | ...props
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/sections/dataset/dataset-files/DatasetFilesScrollable.module.scss:
--------------------------------------------------------------------------------
1 | .files-scrollable-container {
2 | min-height: 400px;
3 | max-height: 650px;
4 | overflow-x: auto;
5 | overflow-y: auto;
6 |
7 | &--empty {
8 | min-height: auto;
9 | }
10 |
11 | .criteria-form-container {
12 | position: sticky;
13 | top: 0;
14 | right: 0;
15 | left: 0;
16 | z-index: 2;
17 | padding: 0.25rem 0.5rem 1rem 0.25rem;
18 | background: var(--bs-white);
19 | }
20 |
21 | table {
22 | margin-bottom: 0;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/badge/Badge.tsx:
--------------------------------------------------------------------------------
1 | import { Badge as BadgeBS } from 'react-bootstrap'
2 | import { ReactNode } from 'react'
3 | import styles from './Badge.module.scss'
4 |
5 | interface BadgeProps {
6 | variant?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info'
7 | children: ReactNode
8 | }
9 |
10 | export function Badge({ variant = 'secondary', children }: BadgeProps) {
11 | return (
12 |
13 | {children}
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next'
2 | import { FileDate as FileDateModel } from '../../../../../../../files/domain/models/FileMetadata'
3 |
4 | export function FileDate({ date }: { date: FileDateModel }) {
5 | const { t } = useTranslation('files')
6 |
7 | return (
8 |
9 |
10 | {t(`table.date.${date.type}`)}
11 |
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/copy-to-clipboard-button/CopyToClipboard.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .container {
4 | display: inline-flex;
5 | align-items: center;
6 | margin: 0 3px;
7 | cursor: pointer;
8 | }
9 |
10 | %icon {
11 | margin-left: 5px;
12 | }
13 |
14 | .clipboard {
15 | @extend %icon;
16 | }
17 |
18 | .check {
19 | @extend %icon;
20 |
21 | color: $dv-success-color;
22 | }
23 |
--------------------------------------------------------------------------------
/src/sections/dataset/dataset-metadata/dataset-metadata-fields/DatasetMetadataFieldTitle.tsx:
--------------------------------------------------------------------------------
1 | import { QuestionMarkTooltip } from '@iqss/dataverse-design-system'
2 |
3 | interface DatasetMetadataFieldTitleProps {
4 | title: string
5 | description: string
6 | }
7 |
8 | export function DatasetMetadataFieldTitle({ title, description }: DatasetMetadataFieldTitleProps) {
9 | return (
10 | <>
11 | {title}
12 |
13 | >
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/dev-env/run-env.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | export DATAVERSE_IMAGE_TAG=$1
4 |
5 | # To avoid timeout issues on frontend container startup
6 | export COMPOSE_HTTP_TIMEOUT=200
7 |
8 | # Timeout for Dataverse bootstrap configbaker
9 | export DATAVERSE_BOOTSTRAP_TIMEOUT="10m"
10 |
11 | echo "INFO - Setting up Dataverse on image tag ${DATAVERSE_IMAGE_TAG}..."
12 |
13 | echo "INFO - Removing current environment if exists..."
14 | ./rm-env.sh
15 |
16 | echo "INFO - Running docker containers..."
17 | docker compose -f "./docker-compose-dev.yml" up -d --build
18 |
--------------------------------------------------------------------------------
/packages/design-system/tests/component/form/form-group/FormGroupTextArea.spec.tsx:
--------------------------------------------------------------------------------
1 | import { FormGroup } from '../../../../src/lib/components/form/form-group/FormGroup'
2 |
3 | describe('FormInput', () => {
4 | it('renders FormTextArea component without crashing', () => {
5 | cy.mount(
6 |
7 | Username
8 |
9 |
10 | )
11 |
12 | const textarea = cy.findByRole('textbox')
13 | textarea.should('exist')
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/src/collection/domain/useCases/createCollection.ts:
--------------------------------------------------------------------------------
1 | import { WriteError } from '@iqss/dataverse-client-javascript'
2 | import { CollectionRepository } from '../repositories/CollectionRepository'
3 | import { CollectionDTO } from './DTOs/CollectionDTO'
4 |
5 | export function createCollection(
6 | collectionRepository: CollectionRepository,
7 | collection: CollectionDTO,
8 | hostCollection: string
9 | ): Promise {
10 | return collectionRepository.create(collection, hostCollection).catch((error: WriteError) => {
11 | throw error
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/src/collection/domain/useCases/updateFeaturedItems.ts:
--------------------------------------------------------------------------------
1 | import { FeaturedItem } from '../models/FeaturedItem'
2 | import { CollectionRepository } from '../repositories/CollectionRepository'
3 | import { FeaturedItemsDTO } from './DTOs/FeaturedItemsDTO'
4 |
5 | export async function updateFeaturedItems(
6 | collectionRepository: CollectionRepository,
7 | featuredItems: FeaturedItemsDTO,
8 | collectionIdOrAlias: number | string
9 | ): Promise {
10 | return collectionRepository.updateFeaturedItems(collectionIdOrAlias, featuredItems)
11 | }
12 |
--------------------------------------------------------------------------------
/src/sections/collection/collection-items-panel/filter-panel/FilterPanel.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .filter-panel {
4 | @media (min-width: 992px) {
5 | padding: 1rem;
6 | border: 1px solid $dv-border-color;
7 | border-radius: 4px;
8 | }
9 |
10 | .toggle-canvas-btn {
11 | display: block;
12 |
13 | @media (min-width: 992px) {
14 | display: none;
15 | }
16 | }
17 |
18 | .filters-wrapper {
19 | width: 100%;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/sections/create-dataset/HostCollectionForm/HostCollectionForm.module.scss:
--------------------------------------------------------------------------------
1 | .input-button-wrapper {
2 | display: flex;
3 | flex-direction: column;
4 | gap: 1rem;
5 |
6 | @media (min-width: 768px) {
7 | flex-direction: row;
8 | gap: 0;
9 |
10 | button {
11 | width: 100%;
12 | }
13 | }
14 | }
15 |
16 | .edit-button-wrapper {
17 | display: flex;
18 | flex-direction: column;
19 | align-items: flex-start;
20 | justify-content: flex-end;
21 |
22 | @media (min-width: 768px) {
23 | padding-left: 1rem;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/sections/upload-dataset-files/uploaded-files-list/add-tags-modal/AddTagsModal.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .add_tags_form {
4 | padding-block: 0.5em;
5 | }
6 |
7 | .tags {
8 | display: flex;
9 | }
10 |
11 | .tags_select {
12 | width: 100%;
13 | }
14 |
15 | .tag_options {
16 | padding-block: 0.5em;
17 | }
18 |
19 | .tag_info {
20 | padding-block: 0.5em;
21 | color: $dv-subtext-color;
22 | }
23 |
24 | .apply_button {
25 | height: 2.4rem;
26 | }
27 |
--------------------------------------------------------------------------------
/tests/component/externalTools/domain/models/DatasetExternalToolResolvedMother.ts:
--------------------------------------------------------------------------------
1 | import { DatasetExternalToolResolved } from '@/externalTools/domain/models/DatasetExternalToolResolved'
2 |
3 | export class DatasetExternalToolResolvedMother {
4 | static create(props?: Partial): DatasetExternalToolResolved {
5 | return {
6 | displayName: 'Dataset Explore Tool',
7 | datasetId: 1,
8 | preview: false,
9 | toolUrlResolved: 'http://localhost:3000/external-tool',
10 | ...props
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/stories/close-button/CloseButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import type { Meta, StoryObj } from '@storybook/react'
2 | import { CloseButton } from '../../components/close-button/CloseButton'
3 |
4 | const meta: Meta = {
5 | title: 'CloseButton',
6 | component: CloseButton
7 | }
8 |
9 | export default meta
10 | type Story = StoryObj
11 |
12 | export const Default: Story = {
13 | render: () =>
14 | }
15 | export const Disabled: Story = {
16 | render: () =>
17 | }
18 |
--------------------------------------------------------------------------------
/src/collection/domain/useCases/getCollectionUserPermissions.ts:
--------------------------------------------------------------------------------
1 | import { CollectionRepository } from '../repositories/CollectionRepository'
2 | import { CollectionUserPermissions } from '../models/CollectionUserPermissions'
3 |
4 | export function getCollectionUserPermissions(
5 | collectionRepository: CollectionRepository,
6 | collectionIdOrAlias?: number | string
7 | ): Promise {
8 | return collectionRepository.getUserPermissions(collectionIdOrAlias).catch((error: Error) => {
9 | throw new Error(error.message)
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/src/dataset/domain/useCases/deaccessionDataset.ts:
--------------------------------------------------------------------------------
1 | import { DatasetRepository } from '../repositories/DatasetRepository'
2 | import { DatasetDeaccessionDTO } from '@iqss/dataverse-client-javascript'
3 |
4 | export function deaccessionDataset(
5 | datasetRepository: DatasetRepository,
6 | datasetId: string | number,
7 | version: string,
8 | deaccessionDTO: DatasetDeaccessionDTO
9 | ): Promise {
10 | return datasetRepository.deaccession(datasetId, version, deaccessionDTO).catch((error: Error) => {
11 | throw new Error(error.message)
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/src/sections/account/my-data-section/my-data-filter-panel/MyDataFilterPanel.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .filter-panel {
4 | @media (min-width: 992px) {
5 | padding: 1rem;
6 | border: 1px solid $dv-border-color;
7 | border-radius: 4px;
8 | }
9 |
10 | .toggle-canvas-btn {
11 | display: block;
12 |
13 | @media (min-width: 992px) {
14 | display: none;
15 | }
16 | }
17 |
18 | .filters-wrapper {
19 | width: 100%;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/sections/not-implemented/NotImplementedModalContext.ts:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 |
3 | interface NotImplementedModalContextProps {
4 | showModal: () => void
5 | hideModal: () => void
6 | isModalOpen: boolean
7 | }
8 |
9 | export const NotImplementedModal = createContext({
10 | isModalOpen: false,
11 | showModal: /* istanbul ignore next */ () => {},
12 | hideModal: /* istanbul ignore next */ () => {}
13 | })
14 |
15 | export const useNotImplementedModal = () => useContext(NotImplementedModal)
16 |
--------------------------------------------------------------------------------
/src/sections/shared/pagination/Pagination.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module';
2 |
3 | .row {
4 | justify-content: center;
5 | }
6 |
7 | .container {
8 | display: flex;
9 | align-items: center;
10 | }
11 |
12 | .size-selector-container {
13 | display: flex;
14 | margin-bottom: 14px;
15 | margin-left: 28px;
16 |
17 | &__text {
18 | margin-right: 14px;
19 | }
20 | }
21 |
22 | .results {
23 | margin-right: 4px;
24 | font-weight: $dv-font-weight-bold;
25 | }
26 |
--------------------------------------------------------------------------------
/dev-env/shib-dev-env/run-env.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | export DATAVERSE_IMAGE_TAG=$1
4 |
5 | # To avoid timeout issues on frontend container startup
6 | export COMPOSE_HTTP_TIMEOUT=200
7 |
8 | # Timeout for Dataverse bootstrap configbaker
9 | export DATAVERSE_BOOTSTRAP_TIMEOUT="10m"
10 |
11 | echo "INFO - Setting up Dataverse on image tag ${DATAVERSE_IMAGE_TAG}..."
12 |
13 | echo "INFO - Removing current environment if exists..."
14 | ./rm-env.sh
15 |
16 | echo "INFO - Running docker containers..."
17 | docker compose -f "./docker-compose-dev.yml" up -d --build
18 |
--------------------------------------------------------------------------------
/src/collection/domain/useCases/editCollection.ts:
--------------------------------------------------------------------------------
1 | import { WriteError } from '@iqss/dataverse-client-javascript'
2 | import { CollectionRepository } from '../repositories/CollectionRepository'
3 | import { CollectionDTO } from './DTOs/CollectionDTO'
4 |
5 | export async function editCollection(
6 | collectionRepository: CollectionRepository,
7 | updatedCollection: CollectionDTO,
8 | collectionId: string
9 | ): Promise {
10 | return collectionRepository.edit(collectionId, updatedCollection).catch((error: WriteError) => {
11 | throw error
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/src/files/domain/useCases/getFileVersionSummaries.ts:
--------------------------------------------------------------------------------
1 | import { FileRepository } from '../repositories/FileRepository'
2 | import { FileVersionSummarySubset } from '../models/FileVersionSummaryInfo'
3 | import { FileVersionPaginationInfo } from '../models/FileVersionPaginationInfo'
4 |
5 | export function getFileVersionSummaries(
6 | fileRepository: FileRepository,
7 | fileId: number | string,
8 | paginationInfo?: FileVersionPaginationInfo
9 | ): Promise {
10 | return fileRepository.getFileVersionSummaries(fileId, paginationInfo)
11 | }
12 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/grid/Row.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react'
2 | import { Row as RowBS } from 'react-bootstrap'
3 | import * as React from 'react'
4 |
5 | type RowSize = number | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '10' | '11' | '12'
6 | interface RowProps extends React.HTMLAttributes {
7 | children: ReactNode
8 | xs?: RowSize
9 | sm?: RowSize
10 | md?: RowSize
11 | lg?: RowSize
12 | }
13 |
14 | export function Row({ children, ...props }: RowProps) {
15 | return {children}
16 | }
17 |
--------------------------------------------------------------------------------
/src/metadata-block-info/domain/useCases/getMetadataBlockInfoByName.ts:
--------------------------------------------------------------------------------
1 | import { MetadataBlockInfoRepository } from '../repositories/MetadataBlockInfoRepository'
2 | import { MetadataBlockInfoDisplayFormat } from '../models/MetadataBlockInfo'
3 |
4 | export async function getMetadataBlockInfoByName(
5 | metadataBlockInfoRepository: MetadataBlockInfoRepository,
6 | name: string
7 | ): Promise {
8 | return metadataBlockInfoRepository.getByName(name).catch((error: Error) => {
9 | throw new Error(error.message)
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/dev.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:22-alpine AS build_image
2 |
3 | RUN apk --no-cache add python3 py3-setuptools make g++
4 |
5 | WORKDIR /usr/src/app/packages/design-system
6 | COPY ./packages/design-system ./
7 | COPY package-lock.json ./
8 | RUN npm install
9 | RUN npm run build
10 |
11 | WORKDIR /usr/src/app
12 | COPY package.json ./
13 | COPY package-lock.json ./
14 | COPY .npmrc ./
15 | RUN npm install
16 |
17 | FROM node:22-alpine
18 |
19 | WORKDIR /usr/src/app
20 | COPY --from=build_image /usr/src/app/node_modules ./node_modules
21 |
22 | EXPOSE 5173
23 | CMD ["npm", "start"]
24 |
--------------------------------------------------------------------------------
/src/assets/react-toastify-custom.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module';
3 |
4 | :root {
5 | --toastify-color-info: #{$dv-info-color};
6 | --toastify-color-success: #{$dv-success-color};
7 | --toastify-color-warning: #{$dv-warning-color};
8 | --toastify-color-error: #{$dv-danger-color};
9 | --toastify-font-family: #{$dv-font-family};
10 | --toastify-toast-padding: 14px 28px 14px 14px;
11 | }
12 |
--------------------------------------------------------------------------------
/src/collection/domain/useCases/getCollectionsForLinking.ts:
--------------------------------------------------------------------------------
1 | import { CollectionRepository } from '../repositories/CollectionRepository'
2 | import { CollectionSummary } from '../models/CollectionSummary'
3 |
4 | export type LinkingObjectType = 'collection' | 'dataset'
5 |
6 | export async function getCollectionsForLinking(
7 | collectionRepository: CollectionRepository,
8 | objectType: LinkingObjectType,
9 | id: number | string,
10 | searchTerm?: string
11 | ): Promise {
12 | return collectionRepository.getForLinking(objectType, id, searchTerm)
13 | }
14 |
--------------------------------------------------------------------------------
/src/files/domain/useCases/getMultipleFileDownloadUrl.ts:
--------------------------------------------------------------------------------
1 | import { FileRepository } from '../repositories/FileRepository'
2 | import { FileDownloadMode } from '../models/FileMetadata'
3 |
4 | const ONLY_ONE_FILE = 1
5 | export function getMultipleFileDownloadUrl(
6 | fileRepository: FileRepository,
7 | ids: number[],
8 | downloadMode: FileDownloadMode
9 | ): string {
10 | if (ids.length === ONLY_ONE_FILE) {
11 | return fileRepository.getFileDownloadUrl(ids[0], downloadMode)
12 | }
13 |
14 | return fileRepository.getMultipleFileDownloadUrl(ids, downloadMode)
15 | }
16 |
--------------------------------------------------------------------------------
/src/sections/file/file-version/FileVersion.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .file-versions-table {
4 | width: 100%;
5 | margin: auto;
6 | overflow-x: auto;
7 | text-align: center;
8 | table-layout: fixed;
9 | border-collapse: collapse;
10 |
11 | th {
12 | font-weight: 600;
13 | }
14 |
15 | th,
16 | td {
17 | width: auto;
18 | vertical-align: top;
19 | }
20 | }
21 |
22 | .no-summary-text {
23 | font-style: italic;
24 | color: $dv-subtext-color;
25 | }
26 |
--------------------------------------------------------------------------------
/src/sections/shared/layout/app-loader/AppLoader.tsx:
--------------------------------------------------------------------------------
1 | import cn from 'classnames'
2 | import { Spinner } from '@iqss/dataverse-design-system'
3 | import styles from './AppLoader.module.scss'
4 |
5 | interface AppLoaderProps {
6 | fullViewport?: boolean
7 | }
8 |
9 | export const AppLoader = ({ fullViewport = false }: AppLoaderProps) => {
10 | return (
11 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/packages/design-system/.storybook/main.ts:
--------------------------------------------------------------------------------
1 | import type { StorybookConfig } from '@storybook/react-vite'
2 |
3 | const config: StorybookConfig = {
4 | stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
5 | addons: [
6 | '@storybook/addon-links',
7 | '@storybook/addon-essentials',
8 | '@storybook/addon-interactions',
9 | '@storybook/addon-a11y'
10 | ],
11 | framework: {
12 | name: '@storybook/react-vite',
13 | options: {}
14 | },
15 | docs: {},
16 | typescript: {
17 | reactDocgen: false
18 | }
19 | }
20 | export default config
21 |
--------------------------------------------------------------------------------
/src/collection/domain/models/MyDataCollectionItemSubset.ts:
--------------------------------------------------------------------------------
1 | import { CollectionItem, CountPerObjectType } from '@/collection/domain/models/CollectionItemSubset'
2 | import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus'
3 |
4 | export interface MyDataCollectionItemSubset {
5 | items: CollectionItem[]
6 | publicationStatusCounts: PublicationStatusCount[]
7 | totalItemCount: number
8 | countPerObjectType: CountPerObjectType
9 | }
10 |
11 | export interface PublicationStatusCount {
12 | publicationStatus: PublicationStatus
13 | count: number
14 | }
15 |
--------------------------------------------------------------------------------
/src/sections/dataset/dataset-thumbnail/DatasetThumbnail.tsx:
--------------------------------------------------------------------------------
1 | import styles from './DatasetThumbnail.module.scss'
2 | import { DatasetIcon } from '../dataset-icon/DatasetIcon'
3 |
4 | interface DatasetThumbnailProps {
5 | thumbnail?: string
6 | title: string
7 | isDeaccessioned?: boolean
8 | }
9 |
10 | export function DatasetThumbnail({ thumbnail, title, isDeaccessioned }: DatasetThumbnailProps) {
11 | if (thumbnail && !isDeaccessioned) {
12 | return
13 | }
14 |
15 | return
16 | }
17 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/offcanvas/OffcanvasHeader.tsx:
--------------------------------------------------------------------------------
1 | import { OffcanvasHeader as OffcanvasHeaderBS } from 'react-bootstrap'
2 |
3 | export interface OffcanvasHeaderProps {
4 | closeLabel?: string
5 | closeButton?: boolean
6 | children: React.ReactNode
7 | }
8 |
9 | export const OffcanvasHeader = ({
10 | closeLabel = 'Close',
11 | closeButton = true,
12 | children
13 | }: OffcanvasHeaderProps) => {
14 | return (
15 |
16 | {children}
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Dataverse",
3 | "name": "Open source research data repository software",
4 | "icons": [
5 | {
6 | "src": "/favicon-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png",
9 | "purpose": "maskable"
10 | },
11 | {
12 | "src": "/favicon-512x512.png",
13 | "sizes": "512x512",
14 | "type": "image/png",
15 | "purpose": "maskable"
16 | }
17 | ],
18 | "start_url": ".",
19 | "display": "standalone",
20 | "theme_color": "#000000",
21 | "background_color": "#ffffff"
22 | }
23 |
--------------------------------------------------------------------------------
/src/collection/domain/useCases/getCollectionsForUnlinking.ts:
--------------------------------------------------------------------------------
1 | import { CollectionRepository } from '../repositories/CollectionRepository'
2 | import { CollectionSummary } from '../models/CollectionSummary'
3 | import { LinkingObjectType } from './getCollectionsForLinking'
4 |
5 | export async function getCollectionsForUnlinking(
6 | collectionRepository: CollectionRepository,
7 | objectType: LinkingObjectType,
8 | id: number | string,
9 | searchTerm?: string
10 | ): Promise {
11 | return collectionRepository.getForUnlinking(objectType, id, searchTerm)
12 | }
13 |
--------------------------------------------------------------------------------
/src/dataset/domain/useCases/updateDatasetMetadata.ts:
--------------------------------------------------------------------------------
1 | import { DatasetRepository } from '../repositories/DatasetRepository'
2 | import { DatasetDTO } from './DTOs/DatasetDTO'
3 |
4 | export function updateDatasetMetadata(
5 | datasetRepository: DatasetRepository,
6 | datasetId: string | number,
7 | updatedDataset: DatasetDTO,
8 | sourceLastUpdateTime?: string
9 | ): Promise {
10 | return datasetRepository
11 | .updateMetadata(datasetId, updatedDataset, sourceLastUpdateTime)
12 | .catch((error: Error) => {
13 | throw new Error(error.message)
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/src/metadata-block-info/domain/useCases/getMetadataBlockInfoByCollectionId.ts:
--------------------------------------------------------------------------------
1 | import { MetadataBlockInfo } from '../models/MetadataBlockInfo'
2 | import { MetadataBlockInfoRepository } from '../repositories/MetadataBlockInfoRepository'
3 |
4 | export async function getMetadataBlockInfoByCollectionId(
5 | metadataBlockInfoRepository: MetadataBlockInfoRepository,
6 | collectionId: number | string
7 | ): Promise {
8 | return metadataBlockInfoRepository.getByCollectionId(collectionId).catch((error: Error) => {
9 | throw new Error(error.message)
10 | })
11 | }
12 |
--------------------------------------------------------------------------------
/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .type-filters {
4 | input[type='checkbox'] {
5 | cursor: pointer;
6 |
7 | & + label {
8 | color: $dv-primary-color;
9 | cursor: pointer;
10 | }
11 |
12 | &:checked + label {
13 | font-weight: 500;
14 | }
15 |
16 | &:disabled {
17 | opacity: 0.7;
18 |
19 | & + label {
20 | opacity: 1;
21 | }
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/sections/dataset/dataset-citation/DatasetCitation.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .container {
4 | margin: 0 0 20px;
5 | padding: 10px 0;
6 | border: 1px solid $dv-info-border-color;
7 | border-radius: 4px;
8 | }
9 |
10 | .deaccessioned {
11 | margin: 0.5rem 0;
12 | padding: 10px;
13 | border: 1px solid $dv-danger-color;
14 | border-radius: 4px;
15 | }
16 |
17 | .row {
18 | margin-right: -15px;
19 | margin-left: -15px;
20 | }
21 |
22 | .thumbnail {
23 | font-size: 7.5em;
24 | }
25 |
--------------------------------------------------------------------------------
/src/files/domain/models/FilesCountInfo.ts:
--------------------------------------------------------------------------------
1 | import { FileType } from './FileMetadata'
2 | import { FileAccessOption, FileTag } from './FileCriteria'
3 |
4 | export interface FilesCountInfo {
5 | total: number
6 | perFileType: FileTypeCount[]
7 | perAccess: FileAccessCount[]
8 | perFileTag: FileTagCount[]
9 | }
10 |
11 | export interface FileTypeCount {
12 | type: FileType
13 | count: number
14 | }
15 |
16 | export interface FileAccessCount {
17 | access: FileAccessOption
18 | count: number
19 | }
20 |
21 | export interface FileTagCount {
22 | tag: FileTag
23 | count: number
24 | }
25 |
--------------------------------------------------------------------------------
/src/sections/shared/citation/CitationLearnAbout.tsx:
--------------------------------------------------------------------------------
1 | import { useTranslation } from 'react-i18next'
2 | import styles from './Citation.module.scss'
3 |
4 | export function CitationLearnAbout() {
5 | const { t } = useTranslation('shared', { keyPrefix: 'citationBlock' })
6 |
7 | return (
8 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/theme/ThemeProvider.tsx:
--------------------------------------------------------------------------------
1 | import { baseTheme, BaseThemeType } from './BaseTheme'
2 | import { ReactNode, useContext } from 'react'
3 | import { ThemeContext } from './ThemeContext'
4 | import '../../assets/styles/index.scss'
5 |
6 | export interface ThemeProps {
7 | theme?: BaseThemeType
8 | children: ReactNode
9 | }
10 |
11 | export function ThemeProvider({ theme = baseTheme, children }: ThemeProps) {
12 | return {children}
13 | }
14 |
15 | export const useTheme = () => useContext(ThemeContext)
16 |
--------------------------------------------------------------------------------
/src/dataset/infrastructure/mappers/DatasetDTOMapper.ts:
--------------------------------------------------------------------------------
1 | import { DatasetDTO } from '../../domain/useCases/DTOs/DatasetDTO'
2 | import {
3 | DatasetDTO as JSDatasetDTO,
4 | DatasetMetadataBlockValuesDTO as JSMetadataBlocksDTO
5 | } from '@iqss/dataverse-client-javascript'
6 | import { defaultLicense } from '../../domain/models/Dataset'
7 |
8 | export class DatasetDTOMapper {
9 | public static toJSDatasetDTO(dataset: DatasetDTO): JSDatasetDTO {
10 | return {
11 | license: defaultLicense,
12 | metadataBlockValues: dataset.metadataBlocks as JSMetadataBlocksDTO[]
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/sections/file/file-preview/FileIcon.tsx:
--------------------------------------------------------------------------------
1 | import styles from './FileIcon.module.scss'
2 | import { FileType } from '../../../files/domain/models/FileMetadata'
3 | import { FileTypeToFileIconMap } from './FileTypeToFileIconMap'
4 | import { IconName } from '@iqss/dataverse-design-system'
5 |
6 | export function FileIcon({ type }: { type: FileType }) {
7 | const icon = FileTypeToFileIconMap[type.value] || IconName.OTHER
8 |
9 | return (
10 |
11 | {icon}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/sections/shared/citation/CitationDescription.tsx:
--------------------------------------------------------------------------------
1 | import parse from 'html-react-parser'
2 | import styles from './Citation.module.scss'
3 | import { ReactNode } from 'react'
4 |
5 | interface CitationDescriptionProps {
6 | citation: string
7 | tooltip?: ReactNode
8 | }
9 |
10 | export function CitationDescription({ citation, tooltip }: CitationDescriptionProps) {
11 | const citationAsReactElement = parse(citation)
12 |
13 | return (
14 |
15 |
16 | {citationAsReactElement} {tooltip}
17 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/src/sections/not-implemented/NotImplementedModalProvider.tsx:
--------------------------------------------------------------------------------
1 | import { useState, PropsWithChildren } from 'react'
2 | import { NotImplementedModal } from './NotImplementedModalContext'
3 |
4 | export function NotImplementedModalProvider({ children }: PropsWithChildren) {
5 | const [isModalOpen, setIsModalOpen] = useState(false)
6 | const showModal = () => setIsModalOpen(true)
7 | const hideModal = () => setIsModalOpen(false)
8 |
9 | return (
10 |
11 | {children}
12 |
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/collection/domain/useCases/DTOs/FeaturedItemsDTO.ts:
--------------------------------------------------------------------------------
1 | import { CustomFeaturedItem, DvObjectFeaturedItem } from '../../models/FeaturedItem'
2 |
3 | export type FeaturedItemsDTO = (CustomFeaturedItemDTO | DvObjectFeaturedItemDTO)[]
4 |
5 | export interface CustomFeaturedItemDTO {
6 | id?: number
7 | type: CustomFeaturedItem['type']
8 | content: string
9 | displayOrder: number
10 | file?: File
11 | keepFile: boolean
12 | }
13 |
14 | export interface DvObjectFeaturedItemDTO {
15 | id?: number
16 | type: DvObjectFeaturedItem['type']
17 | dvObjectIdentifier: string
18 | displayOrder: number
19 | }
20 |
--------------------------------------------------------------------------------
/src/info/domain/models/DatasetMetadataExportFormats.ts:
--------------------------------------------------------------------------------
1 | export type DatasetMetadataExportFormats = Record
2 |
3 | type DatasetMetadataExportFormat = DatasetMetadataExportFormatBase | XmlDatasetMetadataExportFormat
4 |
5 | interface DatasetMetadataExportFormatBase {
6 | displayName: string
7 | mediaType: string
8 | isHarvestable: boolean
9 | isVisibleInUserInterface: boolean
10 | }
11 |
12 | interface XmlDatasetMetadataExportFormat extends DatasetMetadataExportFormatBase {
13 | XMLNameSpace: string
14 | XMLSchemaLocation: string
15 | XMLSchemaVersion: string
16 | }
17 |
--------------------------------------------------------------------------------
/src/sections/upload-dataset-files/uploaded-files-list/files-header/FilesHeader.module.scss:
--------------------------------------------------------------------------------
1 | .icon_pencil {
2 | margin-right: 0.3rem;
3 | margin-bottom: 0.2rem;
4 | }
5 |
6 | .selected_files_checkbox {
7 | display: inline-flex;
8 | align-items: center;
9 | padding-right: 2rem;
10 | padding-left: 0.5rem;
11 | }
12 |
13 | .selected_files_info {
14 | position: absolute;
15 | right: 0;
16 | display: inline-flex;
17 | align-items: center;
18 | justify-content: flex-end;
19 | }
20 |
21 | .uploaded_files_info {
22 | display: inline-flex;
23 | align-items: center;
24 | padding-left: 1em;
25 | }
26 |
--------------------------------------------------------------------------------
/src/stories/shared-mock-repositories/search/SearchMockRepository.ts:
--------------------------------------------------------------------------------
1 | import { SearchService } from '@/search/domain/models/SearchService'
2 | import { SearchRepository } from '@/search/domain/repositories/SearchRepository'
3 |
4 | export class SearchMockRepository implements SearchRepository {
5 | getServices(): Promise {
6 | return new Promise((resolve) => {
7 | setTimeout(() => {
8 | resolve([
9 | {
10 | name: 'solr',
11 | displayName: 'Dataverse Standard Search'
12 | }
13 | ])
14 | }, 1_000)
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/table/Table.tsx:
--------------------------------------------------------------------------------
1 | import { Table as TableBS } from 'react-bootstrap'
2 | import styles from './Table.module.scss'
3 |
4 | interface TableProps {
5 | striped?: boolean
6 | bordered?: boolean
7 | borderless?: boolean
8 | children: React.ReactNode
9 | }
10 |
11 | export function Table({
12 | striped = true,
13 | bordered = true,
14 | borderless = false,
15 | children
16 | }: TableProps) {
17 | return (
18 |
19 | {children}
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/src/contact/domain/useCases/sendFeedbacktoOwners.ts:
--------------------------------------------------------------------------------
1 | import { ContactRepository } from '../repositories/ContactRepository'
2 | import { WriteError } from '@iqss/dataverse-client-javascript'
3 | import { FeedbackDTO } from '../useCases/FeedbackDTO'
4 | import { ContactResponse } from '../models/ContactResponse'
5 |
6 | export async function sendFeedbacktoOwners(
7 | ContactRepository: ContactRepository,
8 | FeedbackDTO: FeedbackDTO
9 | ): Promise {
10 | return ContactRepository.sendFeedbacktoOwners(FeedbackDTO).catch((error: WriteError) => {
11 | throw new Error(error.message)
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.app.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import App from './App'
3 | import './i18n'
4 | import { LoadingProvider } from './shared/contexts/loading/LoadingProvider'
5 | import { ThemeProvider } from '@iqss/dataverse-design-system'
6 | import { AppLoader } from './sections/shared/layout/app-loader/AppLoader'
7 |
8 | export default function AppEntrypoint() {
9 | return (
10 | }>
11 |
12 |
13 |
14 |
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/stories/shared-mock-repositories/contact/ContactMockRepository.ts:
--------------------------------------------------------------------------------
1 | import { ContactResponse } from '@/contact/domain/models/ContactResponse'
2 | import { ContactRepository } from '@/contact/domain/repositories/ContactRepository'
3 | import { FeedbackDTO } from '@/contact/domain/useCases/FeedbackDTO'
4 |
5 | export class ContactMockRepository implements ContactRepository {
6 | sendFeedbacktoOwners(_feedbackDTO: FeedbackDTO): Promise {
7 | return new Promise((resolve) => {
8 | setTimeout(() => {
9 | resolve([])
10 | }, 1_000)
11 | })
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/contact/infrastructure/ContactJSDataverseRepository.ts:
--------------------------------------------------------------------------------
1 | import { submitContactInfo } from '@iqss/dataverse-client-javascript'
2 | import { ContactResponse } from '../domain/models/ContactResponse'
3 | import { ContactRepository } from '../domain/repositories/ContactRepository'
4 | import { FeedbackDTO } from '../domain/useCases/FeedbackDTO'
5 |
6 | export class ContactJSDataverseRepository implements ContactRepository {
7 | async sendFeedbacktoOwners(feedbackDTO: FeedbackDTO): Promise {
8 | return submitContactInfo.execute(feedbackDTO).then((response: ContactResponse[]) => response)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/dataset/domain/useCases/getDatasetsWithCount.ts:
--------------------------------------------------------------------------------
1 | import { DatasetRepository } from '../repositories/DatasetRepository'
2 | import { DatasetPaginationInfo } from '../models/DatasetPaginationInfo'
3 | import { DatasetsWithCount } from '../models/DatasetsWithCount'
4 |
5 | export async function getDatasetsWithCount(
6 | datasetRepository: DatasetRepository,
7 | collectionId: string,
8 | paginationInfo: DatasetPaginationInfo
9 | ): Promise {
10 | return datasetRepository.getAllWithCount(collectionId, paginationInfo).catch((error: Error) => {
11 | throw new Error(error.message)
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/src/files/infrastructure/mappers/JSFilePermissionsMapper.ts:
--------------------------------------------------------------------------------
1 | import { FilePermissions } from '../../domain/models/FilePermissions'
2 | import { FileUserPermissions as JSFilePermissions } from '@iqss/dataverse-client-javascript'
3 |
4 | export class JSFilePermissionsMapper {
5 | static toFilePermissions(jsFileUserPermissions: JSFilePermissions): FilePermissions {
6 | return {
7 | canDownloadFile: jsFileUserPermissions.canDownloadFile,
8 | canManageFilePermissions: jsFileUserPermissions.canManageFilePermissions,
9 | canEditOwnerDataset: jsFileUserPermissions.canEditOwnerDataset
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/dataset/domain/useCases/getDatasetByPersistentId.ts:
--------------------------------------------------------------------------------
1 | import { DatasetRepository } from '../repositories/DatasetRepository'
2 | import { Dataset } from '../models/Dataset'
3 |
4 | export async function getDatasetByPersistentId(
5 | datasetRepository: DatasetRepository,
6 | persistentId: string,
7 | version?: string,
8 | requestedVersion?: string,
9 | keepRawFields?: boolean
10 | ): Promise {
11 | return datasetRepository
12 | .getByPersistentId(persistentId, version, requestedVersion, keepRawFields)
13 | .catch((error: Error) => {
14 | throw new Error(error.message)
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/src/dataset/domain/useCases/getDatasetLocks.ts:
--------------------------------------------------------------------------------
1 | import { DatasetRepository } from '../repositories/DatasetRepository'
2 | import { DatasetLock } from '../models/Dataset'
3 |
4 | export function getDatasetLocks(
5 | datasetRepository: DatasetRepository,
6 | persistentId: string
7 | ): Promise {
8 | return new Promise((resolve, reject) => {
9 | setTimeout(() => {
10 | datasetRepository
11 | .getLocks(persistentId)
12 | .then(resolve)
13 | .catch((error: Error) => {
14 | reject(new Error(error.message))
15 | })
16 | }, 2000) // Delay of 2 seconds
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/src/router/index.tsx:
--------------------------------------------------------------------------------
1 | import { createBrowserRouter, RouterProvider } from 'react-router-dom'
2 | import { DatasetNonNumericVersion } from '../dataset/domain/models/Dataset'
3 | import { routes } from './routes'
4 |
5 | const browserRouter = createBrowserRouter(routes, { basename: import.meta.env.BASE_URL })
6 |
7 | export function Router() {
8 | return
9 | }
10 |
11 | export function searchParamVersionToDomainVersion(version?: string): string | undefined {
12 | if (version === 'DRAFT') {
13 | return DatasetNonNumericVersion.DRAFT.toString()
14 | }
15 |
16 | return version
17 | }
18 |
--------------------------------------------------------------------------------
/src/stories/WithLoggedInUser.tsx:
--------------------------------------------------------------------------------
1 | import { StoryFn } from '@storybook/react'
2 | import { SessionContext } from '../sections/session/SessionContext'
3 | import { UserMother } from '../../tests/component/users/domain/models/UserMother'
4 |
5 | export const WithLoggedInUser = (Story: StoryFn) => {
6 | return (
7 | {},
11 | isLoadingUser: false,
12 | sessionError: null,
13 | refetchUserSession: () => Promise.resolve()
14 | }}>
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/files/domain/useCases/uploadFile.ts:
--------------------------------------------------------------------------------
1 | import { FileRepository } from '../repositories/FileRepository'
2 |
3 | export function uploadFile(
4 | fileRepository: FileRepository,
5 | datasetId: number | string,
6 | file: File,
7 | done: () => void,
8 | failed: () => void,
9 | progress: (now: number) => void,
10 | storageIdSetter: (storageId: string) => void
11 | ): () => void {
12 | const controller = new AbortController()
13 |
14 | fileRepository
15 | .uploadFile(datasetId, { file: file }, progress, controller, storageIdSetter)
16 | .then(done)
17 | .catch(failed)
18 |
19 | return () => controller.abort()
20 | }
21 |
--------------------------------------------------------------------------------
/src/stories/error-page/ErrorPage.stories.tsx:
--------------------------------------------------------------------------------
1 | import { Meta, StoryObj } from '@storybook/react'
2 | import { ErrorPage } from '@/sections/error-page/ErrorPage'
3 | import { WithI18next } from '../WithI18next'
4 |
5 | const meta: Meta = {
6 | title: 'Pages/ErrorPage',
7 | component: ErrorPage,
8 | decorators: [WithI18next],
9 | parameters: {
10 | // Sets the delay for all stories.
11 | chromatic: { delay: 15000, pauseAnimationAtEnd: true }
12 | }
13 | }
14 |
15 | export default meta
16 | type Story = StoryObj
17 |
18 | export const Default: Story = {
19 | render: () =>
20 | }
21 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/navbar/navbar-dropdown/NavbarDropdown.tsx:
--------------------------------------------------------------------------------
1 | import { NavDropdown as NavDropdownBS } from 'react-bootstrap'
2 | import { PropsWithChildren, ReactNode } from 'react'
3 | import { NavbarDropdownItem } from './NavbarDropdownItem'
4 |
5 | interface DropdownProps {
6 | title: ReactNode
7 | id: string
8 | }
9 |
10 | function NavbarDropdown({ title, id, children }: PropsWithChildren) {
11 | return (
12 |
13 | {children}
14 |
15 | )
16 | }
17 |
18 | NavbarDropdown.Item = NavbarDropdownItem
19 |
20 | export { NavbarDropdown }
21 |
--------------------------------------------------------------------------------
/src/externalTools/domain/useCases/GetFileExternalToolResolved.ts:
--------------------------------------------------------------------------------
1 | import { FileExternalToolResolved } from '../models/FileExternalToolResolved'
2 | import { ExternalToolsRepository } from '../repositories/ExternalToolsRepository'
3 | import { GetExternalToolDTO } from './DTOs/GetExternalToolDTO'
4 |
5 | export function getFileExternalToolResolved(
6 | externalToolsRepository: ExternalToolsRepository,
7 | fileId: number | string,
8 | toolId: number,
9 | getExternalToolDTO: GetExternalToolDTO
10 | ): Promise {
11 | return externalToolsRepository.getFileExternalToolResolved(fileId, toolId, getExternalToolDTO)
12 | }
13 |
--------------------------------------------------------------------------------
/src/sections/account/my-data-section/MyDataSearchCriteria.ts:
--------------------------------------------------------------------------------
1 | import type { CollectionItemType } from '@/collection/domain/models/CollectionItemType'
2 | import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus'
3 |
4 | export class MyDataSearchCriteria {
5 | constructor(
6 | public readonly itemTypes: CollectionItemType[],
7 | public readonly roleIds: number[],
8 | public readonly publicationStatuses: PublicationStatus[],
9 | public readonly searchText?: string,
10 | public readonly otherUserName?: string
11 | ) {}
12 |
13 | hasSearchText(): boolean {
14 | return !!this.searchText
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/sections/account/my-data-section/my-data-filter-panel/role-filters/RoleFilters.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .role-filters {
4 | .title {
5 | font-weight: 600;
6 | }
7 |
8 | input[type='checkbox'] {
9 | cursor: pointer;
10 |
11 | & + label {
12 | color: $dv-primary-color;
13 | cursor: pointer;
14 | }
15 |
16 | &:checked + label {
17 | font-weight: 500;
18 | }
19 |
20 | &:disabled {
21 | opacity: 0.7;
22 |
23 | & + label {
24 | opacity: 1;
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tests/component/dataset/domain/models/DatasetDownloadCountMother.ts:
--------------------------------------------------------------------------------
1 | import { DatasetDownloadCount } from '@/dataset/domain/models/DatasetDownloadCount'
2 |
3 | export class DatasetDownloadCountMother {
4 | static createWithoutMDCStartDate(props?: Partial): DatasetDownloadCount {
5 | return {
6 | id: 1,
7 | downloadCount: 15,
8 | ...props
9 | }
10 | }
11 |
12 | static createWithMDCStartDate(props?: Partial): DatasetDownloadCount {
13 | return {
14 | id: 1,
15 | downloadCount: 10,
16 | MDCStartDate: '2019-10-01',
17 | ...props
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/component/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell.spec.tsx:
--------------------------------------------------------------------------------
1 | import { FileActionsCell } from '../../../../../../../../src/sections/dataset/dataset-files/files-table/file-actions/file-actions-cell/FileActionsCell'
2 | import { FilePreviewMother } from '../../../../../../files/domain/models/FilePreviewMother'
3 |
4 | const file = FilePreviewMother.create()
5 | describe('FileActionsCell', () => {
6 | it('renders the file action buttons', () => {
7 | cy.customMount()
8 |
9 | cy.findByRole('group', { name: 'File Action Buttons' }).should('exist')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/.npmrc.example:
--------------------------------------------------------------------------------
1 | legacy-peer-deps=true
2 |
3 | # NPM Registry
4 | //registry.npmjs.org/:_authToken=
5 | @iqss:registry=https://registry.npmjs.org/
6 |
7 | # GitHub Packages Registry
8 | //npm.pkg.github.com/:_authToken=
9 | @iqss:registry=https://npm.pkg.github.com/
10 | # IMPORTANT: Comment out the line above when:
11 | # 1. Publishing the Design System to npm, to avoid publishing to the GitHub Packages Registry.
12 | # 2. Making a release of the application and installing latest version of Dataverse Client Javascript, to avoid installing from the GitHub Packages Registry (otherwise will lead to a npm error code ETARGET).
--------------------------------------------------------------------------------
/src/keycloak-theme/login/KcContext.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/ban-types */
2 | import type { ExtendKcContext } from 'keycloakify/login'
3 | import type { KcEnvName, ThemeName } from '../kc.gen'
4 |
5 | export type KcContextExtension = {
6 | themeName: ThemeName
7 | properties: Record & {}
8 | // NOTE: Here you can declare more properties to extend the KcContext
9 | // See: https://docs.keycloakify.dev/faq-and-help/some-values-you-need-are-missing-from-in-kccontext
10 | }
11 |
12 | export type KcContextExtensionPerPage = {}
13 |
14 | export type KcContext = ExtendKcContext
15 |
--------------------------------------------------------------------------------
/src/stories/WithLoggedInSuperUser.tsx:
--------------------------------------------------------------------------------
1 | import { StoryFn } from '@storybook/react'
2 | import { SessionContext } from '../sections/session/SessionContext'
3 | import { UserMother } from '../../tests/component/users/domain/models/UserMother'
4 |
5 | export const WithLoggedInSuperUser = (Story: StoryFn) => {
6 | return (
7 | {},
11 | isLoadingUser: false,
12 | sessionError: null,
13 | refetchUserSession: () => Promise.resolve()
14 | }}>
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/stories/dataset/WithDataset.tsx:
--------------------------------------------------------------------------------
1 | import { StoryFn } from '@storybook/react'
2 | import { DatasetProvider } from '../../sections/dataset/DatasetProvider'
3 | import { DatasetRepository } from '../../dataset/domain/repositories/DatasetRepository'
4 | import { DatasetMockRepository } from './DatasetMockRepository'
5 |
6 | export const WithDataset = (Story: StoryFn) => {
7 | const datasetRepository: DatasetRepository = new DatasetMockRepository()
8 | return (
9 |
12 |
13 |
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/tests/component/info/domain/models/TermsOfUseMother.ts:
--------------------------------------------------------------------------------
1 | import { TermsOfUse } from '@/info/domain/models/TermsOfUse'
2 |
3 | export class TermsOfUseMother {
4 | static create(): TermsOfUse {
5 | return 'Be nice to each other!'
6 | }
7 |
8 | static createWithOnClickScript(): TermsOfUse {
9 | return 'Terms of Use SPA dev
Please see our full terms of use
Thanks for reading!
'
10 | }
11 |
12 | static createEmpty(): TermsOfUse {
13 | return 'There are no API Terms of Use for this Dataverse installation.'
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/dataset/domain/useCases/getDatasetVersionDiff.ts:
--------------------------------------------------------------------------------
1 | import { DatasetRepository } from '../repositories/DatasetRepository'
2 | import { DatasetVersionDiff } from '../models/DatasetVersionDiff'
3 |
4 | export async function getDatasetVersionDiff(
5 | datasetRepository: DatasetRepository,
6 | persistentId: string,
7 | oldVersion: string,
8 | newVersion: string,
9 | includeDeaccessioned: boolean
10 | ): Promise {
11 | return datasetRepository
12 | .getVersionDiff(persistentId, oldVersion, newVersion, includeDeaccessioned)
13 | .catch((error: Error) => {
14 | throw new Error(error.message)
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/src/metadata-block-info/domain/useCases/getDisplayedOnCreateMetadataBlockInfoByCollectionId.ts:
--------------------------------------------------------------------------------
1 | import { MetadataBlockInfo } from '../models/MetadataBlockInfo'
2 | import { MetadataBlockInfoRepository } from '../repositories/MetadataBlockInfoRepository'
3 |
4 | export async function getDisplayedOnCreateMetadataBlockInfoByCollectionId(
5 | metadataBlockInfoRepository: MetadataBlockInfoRepository,
6 | collectionId: number | string
7 | ): Promise {
8 | return metadataBlockInfoRepository
9 | .getDisplayedOnCreateByCollectionId(collectionId)
10 | .catch((error: Error) => {
11 | throw new Error(error.message)
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/src/sections/file/multiple-file-download/MultipleFileDownloadContext.ts:
--------------------------------------------------------------------------------
1 | import { createContext, useContext } from 'react'
2 | import { FileDownloadMode } from '../../../files/domain/models/FileMetadata'
3 |
4 | interface MultipleFileDownloadContextProps {
5 | getMultipleFileDownloadUrl: (ids: number[], downloadMode: FileDownloadMode) => string
6 | }
7 |
8 | export const MultipleFileDownloadContext = createContext({
9 | getMultipleFileDownloadUrl: () => {
10 | console.error('Not implemented')
11 | return ''
12 | }
13 | })
14 |
15 | export const useMultipleFileDownload = () => useContext(MultipleFileDownloadContext)
16 |
--------------------------------------------------------------------------------
/src/sections/shared/form/row-selection-checkbox/RowSelectionCheckbox.tsx:
--------------------------------------------------------------------------------
1 | import { HTMLProps, useEffect, useRef } from 'react'
2 |
3 | export function RowSelectionCheckbox({
4 | indeterminate,
5 | ...rest
6 | }: { indeterminate?: boolean } & HTMLProps) {
7 | const ref = useRef(null)
8 |
9 | useEffect(() => {
10 | if (typeof indeterminate === 'boolean') {
11 | if (ref.current) {
12 | ref.current.indeterminate = !rest.checked && indeterminate
13 | }
14 | }
15 | }, [ref, indeterminate, rest.checked])
16 |
17 | return
18 | }
19 |
--------------------------------------------------------------------------------
/src/dataset/domain/useCases/getDatasetVersionsSummaries.ts:
--------------------------------------------------------------------------------
1 | import { DatasetRepository } from '../repositories/DatasetRepository'
2 | import { DatasetVersionSummarySubset } from '../models/DatasetVersionSummaryInfo'
3 | import { DatasetVersionPaginationInfo } from '../models/DatasetVersionPaginationInfo'
4 |
5 | export function getDatasetVersionsSummaries(
6 | datasetRepository: DatasetRepository,
7 | datasetId: number | string,
8 | paginationInfo?: DatasetVersionPaginationInfo
9 | ): Promise {
10 | return datasetRepository.getDatasetVersionsSummaries(datasetId, paginationInfo).catch((error) => {
11 | throw error
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/src/stories/file/FileMockFailedUploadRepository.ts:
--------------------------------------------------------------------------------
1 | import { FileRepository } from '../../files/domain/repositories/FileRepository'
2 | import { FileMockRepository } from './FileMockRepository'
3 | import { FileHolder } from '../../files/domain/models/FileHolder'
4 |
5 | export class FileMockFailedRepository extends FileMockRepository implements FileRepository {
6 | uploadFile(
7 | _datasetId: number | string,
8 | _file: FileHolder,
9 | _progress: (now: number) => void,
10 | _abortController: AbortController,
11 | _storageIdSetter: (storageId: string) => void
12 | ): Promise {
13 | return Promise.reject(new Error('fail'))
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/form/form-group/form-element/FormSelectAdvanced.tsx:
--------------------------------------------------------------------------------
1 | import { PropsWithChildren, forwardRef } from 'react'
2 | import { SelectAdvanced, SelectAdvancedProps } from '../../../select-advanced/SelectAdvanced'
3 |
4 | export type FormSelectAdvancedProps = SelectAdvancedProps & {
5 | inputButtonId: string
6 | isInvalid?: boolean
7 | }
8 |
9 | export const FormSelectAdvanced = forwardRef(
10 | ({ ...props }: PropsWithChildren, ref) => {
11 | return } {...props} />
12 | }
13 | )
14 |
15 | FormSelectAdvanced.displayName = 'FormSelectAdvanced'
16 |
--------------------------------------------------------------------------------
/src/sections/error-page/useErrorLogger.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect } from 'react'
2 |
3 | const loggedErrors = new Set()
4 |
5 | export function useErrorLogger(error: Error | unknown): (error: Error) => void {
6 | const logErrorOnce = useCallback((err: Error & { data?: unknown }): void => {
7 | const errorString = String(err.data)
8 | if (!loggedErrors.has(errorString)) {
9 | loggedErrors.add(errorString)
10 | console.error('Error:', err)
11 | }
12 | }, [])
13 |
14 | useEffect(() => {
15 | if (error) {
16 | logErrorOnce(error as Error)
17 | }
18 | }, [error, logErrorOnce])
19 |
20 | return logErrorOnce
21 | }
22 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/rich-text-editor/EditorActions.module.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:color';
2 | @import 'src/lib/assets/styles/design-tokens/colors.module';
3 |
4 | .editor-actions-wrapper {
5 | display: flex;
6 | flex-wrap: wrap;
7 | gap: 0.5rem;
8 | align-items: center;
9 | padding-inline: 0.5rem;
10 |
11 | .editor-actions-button {
12 | display: grid;
13 | place-items: center;
14 |
15 | &.selected {
16 | background-color: color.adjust($dv-secondary-color, $blackness: 30%);
17 | }
18 |
19 | @media (max-width: 992px) {
20 | &.editor-actions-button-image {
21 | display: none;
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/design-system/tests/component/breadcrumb/Breadcrumb.spec.tsx:
--------------------------------------------------------------------------------
1 | import { Breadcrumb } from '../../../src/lib/components/breadcrumb/Breadcrumb'
2 |
3 | describe('Breadcrumb', () => {
4 | it('renders without errors when given valid props', () => {
5 | cy.mount(Test)
6 | cy.findByRole('navigation').should('exist')
7 | })
8 |
9 | it('renders the children prop', () => {
10 | cy.mount(Test)
11 | cy.findByText('Test').should('exist')
12 | })
13 |
14 | it('has the correct role', () => {
15 | cy.mount(Test)
16 | cy.findByRole('navigation').should('exist')
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/src/metadata-block-info/domain/repositories/MetadataBlockInfoRepository.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MetadataBlockInfoDisplayFormat,
3 | MetadataBlockInfo,
4 | MetadataField
5 | } from '../models/MetadataBlockInfo'
6 |
7 | export interface MetadataBlockInfoRepository {
8 | getByName: (name: string) => Promise
9 | getAll: () => Promise
10 | getDisplayedOnCreateByCollectionId: (
11 | collectionId: number | string
12 | ) => Promise
13 | getByCollectionId: (collectionId: number | string) => Promise
14 | getAllFacetableMetadataFields: () => Promise
15 | }
16 |
--------------------------------------------------------------------------------
/src/dataset/domain/models/DatasetItemTypePreview.ts:
--------------------------------------------------------------------------------
1 | import { CollectionItemType } from '../../../collection/domain/models/CollectionItemType'
2 | import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus'
3 | import { DatasetVersion } from './Dataset'
4 |
5 | export interface DatasetItemTypePreview {
6 | type: CollectionItemType.DATASET
7 | persistentId: string
8 | version: DatasetVersion
9 | releaseOrCreateDate: Date
10 | description: string
11 | thumbnail?: string
12 | publicationStatuses: PublicationStatus[]
13 | parentCollectionName: string
14 | parentCollectionAlias: string
15 | userRoles?: string[]
16 | isLinked?: boolean
17 | }
18 |
--------------------------------------------------------------------------------
/src/sections/account/my-data-section/my-data-filter-panel/publication-status-filters/PublicationStatusFilters.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .publication-status-filters {
4 | .title {
5 | font-weight: 600;
6 | }
7 |
8 | input[type='checkbox'] {
9 | cursor: pointer;
10 |
11 | & + label {
12 | color: $dv-primary-color;
13 | cursor: pointer;
14 | }
15 |
16 | &:checked + label {
17 | font-weight: 500;
18 | }
19 |
20 | &:disabled {
21 | opacity: 0.7;
22 |
23 | & + label {
24 | opacity: 1;
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/breadcrumb/BreadcrumbItem.tsx:
--------------------------------------------------------------------------------
1 | import { BreadcrumbItem as BreadcrumbItemBS } from 'react-bootstrap'
2 | import { ElementType, PropsWithChildren } from 'react'
3 |
4 | interface BreadcrumbItemProps {
5 | href?: string
6 | active?: boolean
7 | linkAs?: ElementType
8 | linkProps?: Record
9 | }
10 | export function BreadcrumbItem({
11 | href,
12 | active,
13 | linkAs,
14 | linkProps,
15 | children
16 | }: PropsWithChildren) {
17 | return (
18 |
19 | {children}
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/tests/component/sections/shared/app-loader/AppLoader.spec.tsx:
--------------------------------------------------------------------------------
1 | import { AppLoader } from '@/sections/shared/layout/app-loader/AppLoader'
2 |
3 | describe('AppLoader', () => {
4 | it('shows default app loader', () => {
5 | cy.mount()
6 |
7 | cy.findByTestId('app-loader').should('exist').should('be.visible')
8 |
9 | cy.findByTestId('app-loader').invoke('attr', 'class').should('not.contain', 'full-viewport')
10 | })
11 |
12 | it('with fullViewport prop enabled should have an extra classname', () => {
13 | cy.mount()
14 |
15 | cy.findByTestId('app-loader').invoke('attr', 'class').should('contain', 'full-viewport')
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx:
--------------------------------------------------------------------------------
1 | import { FileItemTypePreview } from '@/files/domain/models/FileItemTypePreview'
2 | import { FileCardHeader } from './FileCardHeader'
3 | import { FileCardBody } from './FileCardBody'
4 | import styles from './FileCard.module.scss'
5 |
6 | interface FileCardProps {
7 | filePreview: FileItemTypePreview
8 | }
9 |
10 | export function FileCard({ filePreview }: FileCardProps) {
11 | return (
12 |
13 |
14 |
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/src/sections/shared/citation/Citation.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .description {
4 | margin-bottom: 1em;
5 | word-break: break-word;
6 | }
7 |
8 | .text {
9 | color: $dv-subtext-color;
10 | }
11 |
12 | .styledCitation {
13 | color: $dv-info-border-color;
14 | }
15 |
16 | .styledCitationBox {
17 | padding: 0.5rem 1rem;
18 | overflow-x: auto;
19 | white-space: nowrap;
20 | scrollbar-width: none;
21 | background-color: #f7f7f9;
22 | border: solid 1px $dv-border-color;
23 | border-radius: 4px;
24 | }
25 |
26 | .styledCitationButton {
27 | color: $dv-link-color !important;
28 | }
29 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/form/form-group/form-element/FormFeedback.tsx:
--------------------------------------------------------------------------------
1 | import { FormControl } from 'react-bootstrap'
2 | import { PropsWithChildren } from 'react'
3 | import { Col, ColProps } from '../../../grid/Col'
4 | import { Row } from '../../../grid/Row'
5 |
6 | interface FormFeedbackProps extends ColProps {
7 | type?: 'valid' | 'invalid'
8 | as?: typeof Col | typeof Row
9 | }
10 |
11 | export function FormFeedback({
12 | type = 'valid',
13 | as,
14 | children,
15 | ...props
16 | }: PropsWithChildren) {
17 | return (
18 |
19 | {children}
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/packages/design-system/tests/component/form/form-group/FormGroup.spec.tsx:
--------------------------------------------------------------------------------
1 | import { FormGroup } from '../../../../src/lib/components/form/form-group/FormGroup'
2 | import { Form } from '../../../../src/lib/components/form/Form'
3 |
4 | describe('FormGroup', () => {
5 | it('should render childrens correctly even trough react fragments', () => {
6 | cy.mount(
7 |
15 | )
16 |
17 | cy.findByText('Username').should('exist')
18 | })
19 | })
20 |
--------------------------------------------------------------------------------
/packages/design-system/tests/component/tooltip/Tooltip.spec.tsx:
--------------------------------------------------------------------------------
1 | import { Tooltip } from '../../../src/lib/components/tooltip/Tooltip'
2 |
3 | describe('OverlayTrigger', () => {
4 | it('renders tooltip when children is hovered', () => {
5 | const message = 'Test tooltip message'
6 | const placement = 'top'
7 |
8 | cy.mount(
9 |
10 |
11 |
12 | )
13 |
14 | const hoverButton = cy.findByText('Hover me')
15 | hoverButton.focus()
16 | hoverButton.click()
17 |
18 | cy.findByText(message).should('exist')
19 | cy.findByRole('tooltip').should('exist')
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileChecksum.tsx:
--------------------------------------------------------------------------------
1 | import styles from '../FileInfoCell.module.scss'
2 | import { CopyToClipboardButton } from './copy-to-clipboard-button/CopyToClipboardButton'
3 | import { FileChecksum as FileChecksumModel } from '../../../../../../../files/domain/models/FileMetadata'
4 |
5 | export function FileChecksum({ checksum }: { checksum: FileChecksumModel | undefined }) {
6 | if (!checksum) {
7 | return <>>
8 | }
9 |
10 | return (
11 |
12 | {`${checksum.algorithm}: `}
13 |
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/sections/shared/form/EditCreateCollectionForm/collection-form/browse-search-facets-section/BrowseSearchFacetsSection.module.scss:
--------------------------------------------------------------------------------
1 | @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module';
2 |
3 | .transfer-list-container {
4 | display: flex;
5 | justify-content: center;
6 |
7 | .error-msg {
8 | width: 100%;
9 | margin-top: 0.25rem;
10 | color: $dv-danger-color;
11 | font-size: 0.875em;
12 | }
13 |
14 | @media (min-width: 768px) {
15 | justify-content: flex-start;
16 | }
17 | }
18 |
19 | .select-facets-by-block-container {
20 | width: 100%;
21 |
22 | @media (min-width: 768px) {
23 | width: 370px;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/assets/styles/design-tokens/typography.module.scss:
--------------------------------------------------------------------------------
1 | $dv-font-size: 16px;
2 | $dv-font-size-sm: 12px;
3 | $dv-brand-font-size: 24px;
4 |
5 | $dv-font-family: ('Helvetica Neue', helvetica, arial, sans-serif);
6 |
7 | $dv-font-weight: 400;
8 | $dv-font-weight-light: 300;
9 | $dv-font-weight-bold: 700;
10 |
11 | $dv-line-height: 1.5;
12 |
13 | :export {
14 | font-weight: $dv-font-weight;
15 | font-size: $dv-font-size;
16 | font-size-sm: $dv-font-size-sm;
17 | brand-font-size: $dv-brand-font-size;
18 | font-family: $dv-font-family;
19 | font-weight-light: $dv-font-weight-light;
20 | font-weight-bold: $dv-font-weight-bold;
21 | line-height: $dv-line-height;
22 | }
23 |
--------------------------------------------------------------------------------
/packages/design-system/src/lib/components/badge/Badge.module.scss:
--------------------------------------------------------------------------------
1 | @import 'bootstrap/scss/functions';
2 | @import 'bootstrap/scss/variables';
3 | @import 'src/lib/assets/styles/design-tokens/colors.module';
4 |
5 | .primary:not(#\#) {
6 | color: color-contrast($dv-primary-color);
7 | }
8 |
9 | .secondary:not(#\#) {
10 | color: color-contrast($dv-secondary-color);
11 | }
12 |
13 | .info:not(#\#) {
14 | color: color-contrast($dv-info-color);
15 | }
16 |
17 | .success:not(#\#) {
18 | color: color-contrast($dv-success-color);
19 | }
20 |
21 | .warning:not(#\#) {
22 | color: color-contrast($dv-warning-color);
23 | }
24 |
25 | .danger:not(#\#) {
26 | color: color-contrast($dv-danger-color);
27 | }
28 |
--------------------------------------------------------------------------------