Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

586 dataset terms tab #589

Merged
merged 47 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2f87c5f
feat: add TermsOfUse to Dataset model, and update mapping
ekraffmiller Jan 26, 2025
46c4364
feat: add Edit Terms Button and first iteration of Accordion Items
ekraffmiller Jan 26, 2025
e5fc2de
feat: add termsOfUse fields to AccordionBody
ekraffmiller Jan 27, 2025
413fa43
feat: add restricted files related fields
ekraffmiller Jan 27, 2025
1750d0c
feat: add help text to License.tsx, add display logic to Terms Tab
ekraffmiller Jan 28, 2025
33eabf6
feat: add tests for Tab display logic and Edit Terms button
ekraffmiller Jan 28, 2025
7b2cb5b
fix: JSDatasetMapper.spec.ts
ekraffmiller Jan 28, 2025
c55ad3a
feat: Add test of Terms Tab to Dataset.spec.tsx
ekraffmiller Jan 28, 2025
c21d3e3
test: increase code coverage
ekraffmiller Jan 28, 2025
cf29bc3
feat: add custom terms
ekraffmiller Jan 31, 2025
f42ab27
feat: move contents of terms tab to separate components
ekraffmiller Jan 31, 2025
0aa9864
feat: update logic for Dataset with custom terms
ekraffmiller Jan 31, 2025
2f08fc9
feat: add search param for active tab, and update Custom Terms link …
ekraffmiller Feb 3, 2025
1cc7752
feat: updates and refactoring required to show Custom License link in…
ekraffmiller Feb 4, 2025
e60b15c
Feat: Add Custom License and Custom Terms of Use to PublishDatasetMod…
ekraffmiller Feb 5, 2025
bab9b58
Feat: rename customTermsOfUse -> customTerms
ekraffmiller Feb 7, 2025
f5c6d82
fix: use latest js-dataverse version from PR
ekraffmiller Feb 7, 2025
ebb1971
fix: update CollectionDTO.ts to match js-dataverse
ekraffmiller Feb 7, 2025
2df59b3
fix: increase code coverage
ekraffmiller Feb 7, 2025
5061a53
fix: increase code coverage
ekraffmiller Feb 8, 2025
24604b1
fix: use defined route for custom terms link, remove console.log
ekraffmiller Feb 10, 2025
d14fd40
fix: onPublishSucceed()
ekraffmiller Feb 10, 2025
40a16d2
fix: revert onPublishSucceed() change
ekraffmiller Feb 10, 2025
0a5058e
fix: remove unneeded state management from Tabs
ekraffmiller Feb 11, 2025
bb560fe
fix: translation text label
ekraffmiller Feb 11, 2025
a3754cb
feat: add scrollIntoView() for CustomTerms link
ekraffmiller Feb 11, 2025
b1faa49
fix: revert license text
ekraffmiller Feb 11, 2025
5543b34
fix: remove comment and unused dependency
ekraffmiller Feb 11, 2025
8f25737
fix: remove NotImplementedModal test
ekraffmiller Feb 11, 2025
1b03ee9
fix: Edit Terms Button
ekraffmiller Feb 11, 2025
54f5f51
fix: use null return value
ekraffmiller Feb 11, 2025
d23c37d
fix: prevent unneeded calls to getDataset()
ekraffmiller Feb 11, 2025
1720b68
fix: remove unused dependency
ekraffmiller Feb 11, 2025
f722b8a
fix: create separate story for PublishDatasetModal.tsx WithCustomTerms
ekraffmiller Feb 11, 2025
4771ad0
fix: use location object to create CustomTerms url
ekraffmiller Feb 11, 2025
0356ccd
fix: remove unused Route
ekraffmiller Feb 11, 2025
431e9c8
fix: lint errors
ekraffmiller Feb 11, 2025
21d4672
merge latest from develop
ekraffmiller Feb 11, 2025
c3ccb29
fix: render Terms of Access accordion if fields are empty and restric…
ekraffmiller Feb 12, 2025
6fe6194
fix: follow conventions for optional props, move EditDatasetTermsButt…
ekraffmiller Feb 12, 2025
735c751
fix: remove style from DatasetTerms.module.scss, (it has been moved t…
ekraffmiller Feb 12, 2025
288dc1a
fix: remove unused customHooks
ekraffmiller Feb 12, 2025
3f84eab
fix: added activeKey prop to Tabs
ekraffmiller Feb 13, 2025
daf6eeb
fix: change errors to warnings, so that component behavior is not mor…
ekraffmiller Feb 13, 2025
7c15310
resolve merge conflicts
ekraffmiller Feb 18, 2025
afb30bd
fix merge error
ekraffmiller Feb 18, 2025
e97d3fb
fix: match order of fields to develop branch
ekraffmiller Feb 18, 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
2 changes: 1 addition & 1 deletion merged-coverage/lcov.info
Original file line number Diff line number Diff line change
Expand Up @@ -2412,7 +2412,7 @@ DA:17,201
DA:26,16
BRDA:1,0,0,16
end_of_record
SF:src/sections/dataset/dataset-summary/License.tsx
SF:src/sections/dataset/dataset-summary/SummaryLicense.tsx
DA:9,332
DA:10,332
DA:12,327
Expand Down
9 changes: 4 additions & 5 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 @@ -17,7 +17,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.16",
"@iqss/dataverse-client-javascript": "2.0.0-alpha.20",
"@iqss/dataverse-design-system": "*",
"@istanbuljs/nyc-config-typescript": "1.0.2",
"@tanstack/react-table": "8.9.2",
Expand Down
20 changes: 15 additions & 5 deletions packages/design-system/src/lib/components/tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@ import { Tab } from './Tab'
import { Tabs as TabsBS } from 'react-bootstrap'

interface TabsProps {
defaultActiveKey: string
defaultActiveKey?: string
activeKey?: string
onSelect?: (key: string | null) => void
}

function Tabs({ defaultActiveKey, children }: PropsWithChildren<TabsProps>) {
return <TabsBS defaultActiveKey={defaultActiveKey}>{children}</TabsBS>
function Tabs({ defaultActiveKey, activeKey, onSelect, children }: PropsWithChildren<TabsProps>) {
if (activeKey && !onSelect) {
console.warn('Tabs component requires onSelect function when activeKey is provided')
}
if (!activeKey && !defaultActiveKey) {
console.warn('Tabs component requires either activeKey or defaultActiveKey')
}
return (
<TabsBS onSelect={onSelect} activeKey={activeKey} defaultActiveKey={defaultActiveKey}>
{children}
</TabsBS>
)
}

Tabs.Tab = Tab

export { Tabs }
38 changes: 38 additions & 0 deletions packages/design-system/tests/component/tabs/Tabs.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,42 @@ describe('Tabs', () => {

cy.findByText('Tab 2').should('have.attr', 'disabled')
})
it('renders with active key', () => {
cy.mount(
<Tabs activeKey="key-3" onSelect={() => {}}>
<Tabs.Tab eventKey="key-1" title="Tab 1">
Content 1
</Tabs.Tab>
<Tabs.Tab eventKey="key-2" title="Tab 2">
Content 2
</Tabs.Tab>
<Tabs.Tab eventKey="key-3" title="Tab 3">
Content 3
</Tabs.Tab>
</Tabs>
)

cy.findByText('Tab 3').should('have.class', 'active')
cy.findByText('Content 3').should('have.class', 'show').and('have.class', 'active')
})

it('calls onSelect when a tab is selected', () => {
const onSelect = cy.stub().as('onSelect')
cy.mount(
<Tabs defaultActiveKey="key-1" onSelect={onSelect}>
<Tabs.Tab eventKey="key-1" title="Tab 1">
Content 1
</Tabs.Tab>
<Tabs.Tab eventKey="key-2" title="Tab 2">
Content 2
</Tabs.Tab>
<Tabs.Tab eventKey="key-3" title="Tab 3">
Content 3
</Tabs.Tab>
</Tabs>
)

cy.findByText('Tab 2').click()
cy.get('@onSelect').should('have.been.calledWith', 'key-2')
})
})
49 changes: 49 additions & 0 deletions public/locales/en/dataset.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{
"filesTabTitle": "Files",
"metadataTabTitle": "Metadata",
"termsTabTitle": "Terms",
"anonymizedFieldValue": "withheld",
"customTerms": {
"title": "Custom Dataset Terms",
"description": "The following custom terms have been defined for this dataset."
},
"license": {
"title": "License/Data Use Agreement",
"altTextPrefix": "License image for "
Expand Down Expand Up @@ -116,6 +121,50 @@
"alertText": "This unpublished dataset is being privately shared."
}
},
"termsTab": {
"editTermsButton": "Edit Term Requirements",
"licenseTitle": "Dataset Terms",
"termsTitle": "Restricted Files + Terms of Access",
"licenseHelpText": "Our <anchor>Community Norms</anchor> as well as good scientific practices expect that proper credit is given via citation. Please use the data citation shown on the dataset page.",
"restrictedFiles": "Restricted Files",
"termsOfAccess": "Terms of Access for Restricted Files",
"requestAccess": "Request Access",
"dataAccessPlace": "Data Access Place",
"originalArchive": "Original Archive",
"availabilityStatus": "Availability Status",
"contactForAccess": "Contact for Access",
"sizeOfCollection": "Size of Collection",
"studyCompletion": "Study Completion",
"termsOfUse": "Terms of Use",
"termsOfUseTip": "The terms of use for this dataset.",
"confidentialityDeclaration": "Confidentiality Declaration",
"specialPermissions": "Special Permissions",
"restrictions": "Restrictions",
"citationRequirements": "Citation Requirements",
"depositorRequirements": "Depositor Requirements",
"conditions": "Conditions",
"disclaimer": "Disclaimer",
"confidentialityDeclarationTip": "Indicates whether signing of a confidentiality declaration is needed to access a resource.",
"specialPermissionsTip": "Determine if any special permissions are required to access a resource (e.g., if form is a needed and where to access the form).",
"restrictionsTip": "Any restrictions on access to or use of the collection, such as privacy certification or distribution restrictions, should be indicated here. These can be restrictions applied by the author, producer, or disseminator of the data collection. If the data are restricted to only a certain class of user, specify which type.",
"citationRequirementsTip": "Include special/explicit citation requirements for data to be cited properly in articles or other publications that are based on analysis of the data. For standard data citation requirements refer to our Community Norms.",
"depositorRequirementsTip": "Information regarding user responsibility for informing Dataset Depositors, Authors or Curators of their use of data through providing citations to the published work or providing copies of the manuscripts.",
"conditionsTip": "Any additional information that will assist the user in understanding the access and use conditions of the Dataset.",
"disclaimerTip": "Information regarding responsibility for uses of the Dataset.",
"restrictedFilesOne": "There is 1 restricted file in this dataset.",
"restrictedFilesMany": "There are {{count}} restricted files in this dataset.",
"termsOfAccessTip": "Information on how and if users can access restricted files in this Dataset",
"requestAccessTip": "If checked, users can request access to the restricted files in this dataset.",
"requestAccessTrue": "Users may request access to files.",
"requestAccessFalse": "Users may not request access to files.",
"restrictedFilesTip": "The number of restricted files in this dataset.",
"dataAccessPlaceTip": "If the data is not only in Dataverse, list the location(s) where the data are currently stored.",
"originalArchiveTip": "Archive from which the data was obtained.",
"availabilityStatusTip": "Statement of Dataset availability. A depositor may need to indicate that a Dataset is unavailable because it is embargoed for a period of time, because it has been superseded, because a new edition is imminent, etc.",
"contactForAccessTip": "If different from the Dataset Contact, this is the Contact person or organization (include email or full address, and telephone number if available) that controls access to a collection.",
"sizeOfCollectionTip": "Summary of the number of physical files that exist in a Dataset, recording the number of files that contain data and noting whether the collection contains machine readable documentation and/or other supplementary files and information, such as code, data dictionaries, data definition statements, or data collection instruments.",
"studyCompletionTip": "Relationship of the data collected to the amount of data coded and stored in the Dataset. Information as to why certain items of collected information were not included in the dataset or a specific data file should be provided."
},
"publish": {
"draftQuestion": "Are you sure you want to publish this dataset? Once you do so, it must remain published.",
"draftSubtext": "This version of the dataset will be published with the following terms:",
Expand Down
2 changes: 2 additions & 0 deletions src/collection/domain/useCases/DTOs/CollectionDTO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export interface CollectionDTO {
metadataBlockNames?: string[]
facetIds?: string[]
inputLevels?: CollectionInputLevelDTO[]
inheritMetadataBlocksFromParent: boolean
inheritFacetsFromParent: boolean
}

export interface CollectionInputLevelDTO {
Expand Down
35 changes: 32 additions & 3 deletions src/dataset/domain/models/Dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,39 @@ export interface DatasetDownloadUrls {
archival: string
}

export interface TermsOfAccess {
fileAccessRequest: boolean
termsOfAccessForRestrictedFiles?: string
dataAccessPlace?: string
originalArchive?: string
availabilityStatus?: string
contactForAccess?: string
sizeOfCollection?: string
studyCompletion?: string
}

export interface CustomTerms {
termsOfUse: string
confidentialityDeclaration?: string
specialPermissions?: string
restrictions?: string
citationRequirements?: string
depositorRequirements?: string
conditions?: string
disclaimer?: string
}
export interface DatasetTermsOfUse {
termsOfAccess: TermsOfAccess
customTerms?: CustomTerms
}

export class Dataset {
constructor(
public readonly persistentId: string,
public readonly version: DatasetVersion,
public readonly alerts: Alert[],
public readonly summaryFields: DatasetMetadataBlock[],
public readonly license: DatasetLicense,
public readonly termsOfUse: DatasetTermsOfUse,
public readonly metadataBlocks: DatasetMetadataBlocks,
public readonly permissions: DatasetPermissions,
public readonly locks: DatasetLock[],
Expand All @@ -384,6 +410,7 @@ export class Dataset {
public readonly downloadUrls: DatasetDownloadUrls,
public readonly fileDownloadSizes: FileDownloadSize[],
public readonly hierarchy: UpwardHierarchyNode,
public readonly license?: DatasetLicense,
public readonly thumbnail?: string,
public readonly privateUrl?: PrivateUrl, // will be set if the user requested a version that did not exist
public readonly requestedVersion?: string,
Expand Down Expand Up @@ -466,7 +493,7 @@ export class Dataset {
public readonly persistentId: string,
public readonly version: DatasetVersion,
public readonly summaryFields: DatasetMetadataBlock[],
public readonly license: DatasetLicense = defaultLicense,
public readonly termsOfUse: DatasetTermsOfUse,
public readonly metadataBlocks: DatasetMetadataBlocks,
public readonly permissions: DatasetPermissions,
public readonly locks: DatasetLock[],
Expand All @@ -476,6 +503,7 @@ export class Dataset {
public readonly downloadUrls: DatasetDownloadUrls,
public readonly fileDownloadSizes: FileDownloadSize[],
public readonly hierarchy: UpwardHierarchyNode,
public readonly license?: DatasetLicense,
public readonly thumbnail?: string,
public readonly privateUrl?: PrivateUrl, // will be set if the user requested a version that did not exist

Expand Down Expand Up @@ -534,7 +562,7 @@ export class Dataset {
this.version,
this.alerts,
this.summaryFields,
this.license,
this.termsOfUse,
this.metadataBlocks,
this.permissions,
this.locks,
Expand All @@ -544,6 +572,7 @@ export class Dataset {
this.downloadUrls,
this.fileDownloadSizes,
this.hierarchy,
this.license,
this.thumbnail,
this.privateUrl,
this.requestedVersion,
Expand Down
3 changes: 2 additions & 1 deletion src/dataset/infrastructure/mappers/JSDatasetMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class JSDatasetMapper {
jsDataset.persistentId,
version,
JSDatasetMapper.toSummaryFields(jsDataset.metadataBlocks, jsDatasetSummaryFieldsNames),
jsDataset.license,
jsDataset.termsOfUse,
JSDatasetMapper.toMetadataBlocks(
jsDataset.metadataBlocks,
jsDataset.alternativePersistentId,
Expand All @@ -83,6 +83,7 @@ export class JSDatasetMapper {
version,
jsDataset.isPartOf
),
jsDataset.license,
undefined, // TODO: get dataset thumbnail from js-dataverse https://github.com/IQSS/dataverse-frontend/issues/203
privateUrl,
requestedVersion,
Expand Down
3 changes: 2 additions & 1 deletion src/sections/Route.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ export enum QueryParamKey {
VERSION = 'version',
PERSISTENT_ID = 'persistentId',
PAGE = 'page',
COLLECTION_ID = 'collectionId'
COLLECTION_ID = 'collectionId',
TAB = 'tab'
}
39 changes: 35 additions & 4 deletions src/sections/dataset/Dataset.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect } from 'react'
import { useRef, useEffect, useState } from 'react'
import { Col, Row, Tabs } from '@iqss/dataverse-design-system'
import styles from './Dataset.module.scss'
import { useNavigate } from 'react-router-dom'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { DatasetLabels } from './dataset-labels/DatasetLabels'
import { useLoading } from '../loading/LoadingContext'
import { DatasetSkeleton, TabsSkeleton } from './DatasetSkeleton'
Expand All @@ -26,6 +26,7 @@ import useUpdateDatasetAlerts from './useUpdateDatasetAlerts'
import { QueryParamKey, Route } from '../Route.enum'
import { MetadataBlockInfoRepository } from '../../metadata-block-info/domain/repositories/MetadataBlockInfoRepository'
import { CollectionRepository } from '../../collection/domain/repositories/CollectionRepository'
import { DatasetTerms } from '@/sections/dataset/dataset-terms/DatasetTerms'

interface DatasetProps {
datasetRepository: DatasetRepository
Expand All @@ -36,6 +37,7 @@ interface DatasetProps {
metadataUpdated?: boolean
filesTabInfiniteScrollEnabled?: boolean
publishInProgress?: boolean
tab?: string
}

export function Dataset({
Expand All @@ -46,14 +48,18 @@ export function Dataset({
created,
metadataUpdated,
filesTabInfiniteScrollEnabled,
publishInProgress
publishInProgress,
tab = 'files'
}: DatasetProps) {
const { setIsLoading } = useLoading()
const { dataset, isLoading: isDatasetLoading } = useDataset()
const { t } = useTranslation('dataset')
const navigate = useNavigate()
const [searchParams] = useSearchParams()
const { hideModal, isModalOpen } = useNotImplementedModal()
const publishCompleted = useCheckPublishCompleted(publishInProgress, dataset, datasetRepository)
const [activeTab, setActiveTab] = useState<string>(tab)
const termsTabRef = useRef<HTMLDivElement>(null)

useUpdateDatasetAlerts({
dataset,
Expand All @@ -78,7 +84,20 @@ export function Dataset({
if (isDatasetLoading && !dataset) {
return <DatasetSkeleton />
}
const handleCustomTermsClick = () => {
setActiveTab('terms')
const newParams = new URLSearchParams(searchParams)
newParams.set('tab', 'terms')
// Update URL without reloading
navigate(`?${newParams.toString()}`, { replace: true })
termsTabRef.current?.scrollIntoView({ behavior: 'smooth' })
}

const handleTabSelect = (key: string | null) => {
if (key) {
setActiveTab(key)
}
}
return (
<>
<NotImplementedModal show={isModalOpen} handleClose={hideModal} />
Expand Down Expand Up @@ -118,14 +137,15 @@ export function Dataset({
<DatasetSummary
summaryFields={dataset.summaryFields}
license={dataset.license}
onCustomTermsClick={handleCustomTermsClick}
metadataBlockInfoRepository={metadataBlockInfoRepository}
/>
</Col>
</Row>
{publishInProgress && <TabsSkeleton />}

{(!publishInProgress || !isDatasetLoading) && (
<Tabs defaultActiveKey="files">
<Tabs defaultActiveKey={activeTab} onSelect={handleTabSelect}>
<Tabs.Tab eventKey="files" title={t('filesTabTitle')}>
<div className={styles['tab-container']}>
{filesTabInfiniteScrollEnabled ? (
Expand All @@ -152,6 +172,17 @@ export function Dataset({
/>
</div>
</Tabs.Tab>
<Tabs.Tab title={t('termsTabTitle')} eventKey={'terms'}>
<div ref={termsTabRef} className={styles['tab-container']}>
<DatasetTerms
license={dataset.license}
termsOfUse={dataset.termsOfUse}
filesRepository={fileRepository}
datasetPersistentId={dataset.persistentId}
datasetVersion={dataset.version}
/>
</div>
</Tabs.Tab>
</Tabs>
)}
<SeparationLine />
Expand Down
Loading