@openneuro/app 4.20.6-alpha.8 → 4.21.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/scripts/app.tsx +16 -10
- package/src/scripts/components/__tests__/agreement.spec.tsx +24 -0
- package/src/scripts/components/agreement.tsx +76 -0
- package/src/scripts/dataset/__tests__/__snapshots__/snapshot-container.spec.tsx.snap +10 -330
- package/src/scripts/dataset/components/BrainLifeButton.tsx +38 -0
- package/src/scripts/dataset/components/CloneDropdown.tsx +31 -0
- package/src/scripts/dataset/components/DatasetAlert.tsx +25 -0
- package/src/scripts/dataset/components/DatasetGitAccess.tsx +82 -0
- package/src/scripts/dataset/components/DatasetHeader.tsx +43 -0
- package/src/scripts/dataset/components/DatasetHeaderMeta.tsx +22 -0
- package/src/scripts/dataset/components/DatasetToolButton.tsx +52 -0
- package/src/scripts/dataset/components/DatasetTools.tsx +149 -0
- package/src/scripts/dataset/components/MetaDataBlock.tsx +24 -0
- package/src/scripts/dataset/components/MetaDataListBlock.tsx +23 -0
- package/src/scripts/dataset/components/ModalitiesMetaDataBlock.tsx +32 -0
- package/src/scripts/dataset/components/NemarButton.tsx +34 -0
- package/src/scripts/dataset/components/ValidationBlock.tsx +11 -0
- package/src/scripts/dataset/components/VersionList.tsx +115 -0
- package/src/scripts/dataset/components/__tests__/DatasetAlert.spec.tsx +25 -0
- package/src/scripts/dataset/components/__tests__/DatasetHeaders.spec.tsx +18 -0
- package/src/scripts/dataset/components/__tests__/DatasetTools.spec.tsx +133 -0
- package/src/scripts/dataset/draft-container.tsx +9 -11
- package/src/scripts/dataset/files/__tests__/file.spec.jsx +13 -4
- package/src/scripts/dataset/files/file.tsx +27 -21
- package/src/scripts/dataset/fragments/dataset-alert-draft.tsx +1 -1
- package/src/scripts/dataset/fragments/dataset-alert-version.tsx +1 -1
- package/src/scripts/dataset/routes/dataset-default.tsx +1 -1
- package/src/scripts/dataset/routes/download-dataset.tsx +7 -1
- package/src/scripts/dataset/routes/snapshot-default.tsx +1 -1
- package/src/scripts/dataset/snapshot-container.tsx +10 -12
- package/src/scripts/scss/README.md +3 -0
- package/src/scripts/scss/dataset/comments.scss +152 -0
- package/src/scripts/scss/dataset/dataset-page.scss +397 -0
- package/src/scripts/scss/dataset/dataset-tool.scss +281 -0
- package/src/scripts/scss/dataset/dropdown.scss +29 -0
- package/src/scripts/scss/dataset/validation.scss +392 -0
- package/src/scripts/scss/dataset/version-dropdown.scss +121 -0
- package/src/scripts/scss/index.scss +6 -0
- package/src/scripts/scss/variables.scss +205 -0
- package/src/scripts/utils/__tests__/local-storage.spec.tsx +32 -0
- package/src/scripts/utils/local-storage.tsx +53 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Tooltip } from "@openneuro/components/tooltip"
|
|
3
|
+
import { Button } from "@openneuro/components/button"
|
|
4
|
+
|
|
5
|
+
function copyToClipboard(text) {
|
|
6
|
+
navigator.clipboard.writeText(text)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface DatasetGitAccessProps {
|
|
10
|
+
datasetId: string
|
|
11
|
+
worker?: string
|
|
12
|
+
configUrl: string
|
|
13
|
+
gitHash: string
|
|
14
|
+
configGithub?: string
|
|
15
|
+
hasEdit?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const DatasetGitAccess = ({
|
|
19
|
+
datasetId,
|
|
20
|
+
worker,
|
|
21
|
+
configUrl,
|
|
22
|
+
gitHash,
|
|
23
|
+
configGithub,
|
|
24
|
+
hasEdit,
|
|
25
|
+
}: DatasetGitAccessProps) => {
|
|
26
|
+
const workerId = worker?.split("-").pop()
|
|
27
|
+
const url = `${configUrl}/git/${workerId}/${datasetId}`
|
|
28
|
+
const readURL = `https://github.com/${configGithub}/${datasetId}.git`
|
|
29
|
+
return (
|
|
30
|
+
<div className="dataset-git-access">
|
|
31
|
+
<span>
|
|
32
|
+
<h4>DataLad/Git URL</h4>
|
|
33
|
+
<a href="https://docs.openneuro.org/git" target="_blank">
|
|
34
|
+
View Documentation
|
|
35
|
+
</a>
|
|
36
|
+
</span>
|
|
37
|
+
<div className="git-url">
|
|
38
|
+
{workerId && (
|
|
39
|
+
<Tooltip tooltip="Copy URL To Clipboard" flow="right">
|
|
40
|
+
<Button
|
|
41
|
+
onClick={() => copyToClipboard(readURL)}
|
|
42
|
+
icon="fas fa-clipboard"
|
|
43
|
+
size="small"
|
|
44
|
+
iconSize="18px"
|
|
45
|
+
label="copy Github url"
|
|
46
|
+
/>
|
|
47
|
+
</Tooltip>
|
|
48
|
+
)}
|
|
49
|
+
<div>{readURL}</div>
|
|
50
|
+
</div>
|
|
51
|
+
{hasEdit && (
|
|
52
|
+
<div className="git-url">
|
|
53
|
+
{workerId && (
|
|
54
|
+
<Tooltip tooltip="Copy URL To Clipboard" flow="right">
|
|
55
|
+
<Button
|
|
56
|
+
onClick={() => copyToClipboard(url)}
|
|
57
|
+
icon="fas fa-clipboard"
|
|
58
|
+
size="small"
|
|
59
|
+
iconSize="18px"
|
|
60
|
+
label="copy OpenNeuro url"
|
|
61
|
+
/>
|
|
62
|
+
</Tooltip>
|
|
63
|
+
)}
|
|
64
|
+
<div>{url}</div>
|
|
65
|
+
</div>
|
|
66
|
+
)}
|
|
67
|
+
|
|
68
|
+
<div className="git-hash">
|
|
69
|
+
<Tooltip tooltip="Copy Git Hash to Clipboard" flow="right">
|
|
70
|
+
<Button
|
|
71
|
+
onClick={() => copyToClipboard(gitHash)}
|
|
72
|
+
icon="fas fa-clipboard"
|
|
73
|
+
size="small"
|
|
74
|
+
iconSize="18px"
|
|
75
|
+
label="copy git hash"
|
|
76
|
+
/>
|
|
77
|
+
</Tooltip>
|
|
78
|
+
<div>Git Hash: {gitHash.slice(0, 7)}</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Link } from "react-router-dom"
|
|
3
|
+
|
|
4
|
+
export interface DatasetHeaderProps {
|
|
5
|
+
modality: string
|
|
6
|
+
pageHeading: string
|
|
7
|
+
renderEditor?: () => React.ReactNode
|
|
8
|
+
children?: JSX.Element
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const DatasetHeader: React.FC<DatasetHeaderProps> = ({
|
|
12
|
+
pageHeading,
|
|
13
|
+
modality,
|
|
14
|
+
renderEditor,
|
|
15
|
+
children,
|
|
16
|
+
}) => {
|
|
17
|
+
return (
|
|
18
|
+
<div className="dataset-header">
|
|
19
|
+
<div className="container">
|
|
20
|
+
<div className="grid grid-between">
|
|
21
|
+
<div className="col">
|
|
22
|
+
<h1>
|
|
23
|
+
<Link to={"/search/modality/" + modality}>
|
|
24
|
+
<div className="hexagon-wrapper">
|
|
25
|
+
<div className="hexagon no-modality"></div>
|
|
26
|
+
<div className="label">
|
|
27
|
+
{modality
|
|
28
|
+
? (
|
|
29
|
+
modality.substr(0, 4)
|
|
30
|
+
)
|
|
31
|
+
: <i className="fa fa-circle-o-notch fa-spin"></i>}
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</Link>
|
|
35
|
+
{renderEditor?.() || pageHeading}
|
|
36
|
+
{children}
|
|
37
|
+
</h1>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import bytes from "bytes"
|
|
3
|
+
|
|
4
|
+
export interface DatasetHeaderMetaProps {
|
|
5
|
+
datasetId: string
|
|
6
|
+
totalFiles: number
|
|
7
|
+
size: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const DatasetHeaderMeta: React.FC<DatasetHeaderMetaProps> = ({
|
|
11
|
+
datasetId,
|
|
12
|
+
totalFiles,
|
|
13
|
+
size,
|
|
14
|
+
}) => {
|
|
15
|
+
return (
|
|
16
|
+
<div>
|
|
17
|
+
<span>OpenNeuro Accession Number:</span> {datasetId}
|
|
18
|
+
<span>Files:</span> {totalFiles}
|
|
19
|
+
<span>Size:</span> {bytes(size)}
|
|
20
|
+
</div>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import styled, { StyledComponent } from "@emotion/styled"
|
|
3
|
+
import { Link } from "react-router-dom"
|
|
4
|
+
import { Tooltip } from "@openneuro/components/tooltip"
|
|
5
|
+
import { Icon } from "@openneuro/components/icon"
|
|
6
|
+
import { useLocation } from "react-router-dom"
|
|
7
|
+
|
|
8
|
+
interface DatasetToolStyleProps {
|
|
9
|
+
active: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const DatasetToolStyle: StyledComponent<DatasetToolStyleProps> = styled
|
|
13
|
+
.span<DatasetToolStyleProps>(
|
|
14
|
+
(props) => `
|
|
15
|
+
display: flex;
|
|
16
|
+
margin: 0 auto 10px;
|
|
17
|
+
flex-basis: auto;
|
|
18
|
+
padding: 0 15px;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
a {
|
|
21
|
+
color: var(--current-theme-primary);
|
|
22
|
+
font-size: 17px;
|
|
23
|
+
text-decoration: none;
|
|
24
|
+
font-weight: 400;
|
|
25
|
+
padding: 4px;
|
|
26
|
+
border-bottom: 2px solid transparent;
|
|
27
|
+
border-bottom-color: ${
|
|
28
|
+
props.active ? "var(--current-theme-primary)" : "transparent"
|
|
29
|
+
};
|
|
30
|
+
i {
|
|
31
|
+
margin-right: 6px;
|
|
32
|
+
font-size: 15px;
|
|
33
|
+
}
|
|
34
|
+
&:hover {
|
|
35
|
+
border-bottom-color: var(--current-theme-primary);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
export const DatasetToolButton = ({ path, icon, tooltip, label }) => {
|
|
42
|
+
const location = useLocation()
|
|
43
|
+
return (
|
|
44
|
+
<DatasetToolStyle active={location.pathname == path}>
|
|
45
|
+
<Tooltip tooltip={tooltip} flow="up">
|
|
46
|
+
<Link to={path}>
|
|
47
|
+
<Icon icon={`fa ${icon}`} label={label} />
|
|
48
|
+
</Link>
|
|
49
|
+
</Tooltip>
|
|
50
|
+
</DatasetToolStyle>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { DatasetToolButton } from "./DatasetToolButton"
|
|
3
|
+
import styled, { StyledComponent } from "@emotion/styled"
|
|
4
|
+
import { useAgreement } from "../../components/agreement"
|
|
5
|
+
|
|
6
|
+
interface DatasetToolStyleProps {}
|
|
7
|
+
|
|
8
|
+
export const DatasetToolStyle: StyledComponent<DatasetToolStyleProps> = styled
|
|
9
|
+
.span`
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: flex-start;
|
|
12
|
+
flex-wrap: wrap;
|
|
13
|
+
`
|
|
14
|
+
|
|
15
|
+
export interface DatasetToolsProps {
|
|
16
|
+
hasEdit: boolean
|
|
17
|
+
isPublic: boolean
|
|
18
|
+
datasetId: string
|
|
19
|
+
snapshotId?: string
|
|
20
|
+
isAdmin: boolean
|
|
21
|
+
isDatasetAdmin: boolean
|
|
22
|
+
hasSnapshot?: boolean
|
|
23
|
+
hasDerivatives?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const DatasetTools = ({
|
|
27
|
+
hasEdit,
|
|
28
|
+
isPublic,
|
|
29
|
+
datasetId,
|
|
30
|
+
snapshotId,
|
|
31
|
+
isAdmin,
|
|
32
|
+
hasSnapshot,
|
|
33
|
+
isDatasetAdmin,
|
|
34
|
+
hasDerivatives,
|
|
35
|
+
}: DatasetToolsProps) => {
|
|
36
|
+
const [agree] = useAgreement()
|
|
37
|
+
const isSnapshot = snapshotId
|
|
38
|
+
return (
|
|
39
|
+
<DatasetToolStyle>
|
|
40
|
+
<DatasetToolButton
|
|
41
|
+
tooltip={"View the dataset file tree"}
|
|
42
|
+
path={snapshotId
|
|
43
|
+
? `/datasets/${datasetId}/versions/${snapshotId}`
|
|
44
|
+
: `/datasets/${datasetId}`}
|
|
45
|
+
icon="fa-folder"
|
|
46
|
+
label="Files"
|
|
47
|
+
/>
|
|
48
|
+
{hasEdit && !isPublic && !isSnapshot && (
|
|
49
|
+
<>
|
|
50
|
+
{hasSnapshot
|
|
51
|
+
? (
|
|
52
|
+
<DatasetToolButton
|
|
53
|
+
tooltip="Publicize the dataset"
|
|
54
|
+
path={`/datasets/${datasetId}/publish`}
|
|
55
|
+
icon="fa-globe"
|
|
56
|
+
label="Publish"
|
|
57
|
+
/>
|
|
58
|
+
)
|
|
59
|
+
: (
|
|
60
|
+
<DatasetToolButton
|
|
61
|
+
tooltip="Create a version to publish"
|
|
62
|
+
path={`/datasets/${datasetId}/snapshot?publish`}
|
|
63
|
+
icon="fa-globe"
|
|
64
|
+
label="Publish"
|
|
65
|
+
/>
|
|
66
|
+
)}
|
|
67
|
+
</>
|
|
68
|
+
)}
|
|
69
|
+
{hasEdit && !isSnapshot && (
|
|
70
|
+
<DatasetToolButton
|
|
71
|
+
tooltip="Share this dataset with collaborators"
|
|
72
|
+
path={`/datasets/${datasetId}/share`}
|
|
73
|
+
icon="fa-user"
|
|
74
|
+
label="Share"
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
{hasEdit && isSnapshot && (
|
|
78
|
+
<DatasetToolButton
|
|
79
|
+
tooltip="View the Dataset Draft"
|
|
80
|
+
path={`/datasets/${datasetId}`}
|
|
81
|
+
icon="fa-pencil"
|
|
82
|
+
label="View Draft"
|
|
83
|
+
/>
|
|
84
|
+
)}
|
|
85
|
+
{hasEdit && !isSnapshot && (
|
|
86
|
+
<DatasetToolButton
|
|
87
|
+
tooltip="Create a new version of the dataset"
|
|
88
|
+
path={`/datasets/${datasetId}/snapshot`}
|
|
89
|
+
icon="fa-camera"
|
|
90
|
+
label="Versioning"
|
|
91
|
+
/>
|
|
92
|
+
)}
|
|
93
|
+
{isAdmin && !isSnapshot && (
|
|
94
|
+
<DatasetToolButton
|
|
95
|
+
tooltip="Admin Datalad Tools"
|
|
96
|
+
path={`/datasets/${datasetId}/admin`}
|
|
97
|
+
icon="fa-magic"
|
|
98
|
+
label="Admin"
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
{agree && (
|
|
102
|
+
<DatasetToolButton
|
|
103
|
+
tooltip="How to Download"
|
|
104
|
+
path={snapshotId
|
|
105
|
+
? `/datasets/${datasetId}/versions/${snapshotId}/download`
|
|
106
|
+
: `/datasets/${datasetId}/download`}
|
|
107
|
+
icon="fa-download"
|
|
108
|
+
label="Download"
|
|
109
|
+
/>
|
|
110
|
+
)}
|
|
111
|
+
{hasDerivatives && (
|
|
112
|
+
<DatasetToolButton
|
|
113
|
+
tooltip="Available Derivatives"
|
|
114
|
+
path={snapshotId
|
|
115
|
+
? `/datasets/${datasetId}/versions/${snapshotId}/derivatives`
|
|
116
|
+
: `/datasets/${datasetId}/derivatives`}
|
|
117
|
+
icon="fa-cubes"
|
|
118
|
+
label="Derivatives"
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
121
|
+
<DatasetToolButton
|
|
122
|
+
tooltip={hasEdit
|
|
123
|
+
? "A form to describe your dataset (helps colleagues discover your dataset)"
|
|
124
|
+
: "View the dataset metadata"}
|
|
125
|
+
path={snapshotId
|
|
126
|
+
? `/datasets/${datasetId}/versions/${snapshotId}/metadata`
|
|
127
|
+
: `/datasets/${datasetId}/metadata`}
|
|
128
|
+
icon="fa-file-code"
|
|
129
|
+
label="Metadata"
|
|
130
|
+
/>
|
|
131
|
+
{isDatasetAdmin && !isSnapshot && (
|
|
132
|
+
<DatasetToolButton
|
|
133
|
+
tooltip="Remove your dataset from OpenNeuro"
|
|
134
|
+
path={`/datasets/${datasetId}/delete`}
|
|
135
|
+
icon="fa-trash"
|
|
136
|
+
label="Delete"
|
|
137
|
+
/>
|
|
138
|
+
)}
|
|
139
|
+
{hasEdit && isSnapshot && (
|
|
140
|
+
<DatasetToolButton
|
|
141
|
+
tooltip="Flag this version as deprecated"
|
|
142
|
+
path={`/datasets/${datasetId}/versions/${snapshotId}/deprecate`}
|
|
143
|
+
icon="fa-remove"
|
|
144
|
+
label="Deprecate Version"
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
147
|
+
</DatasetToolStyle>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Markdown from "markdown-to-jsx"
|
|
2
|
+
import React from "react"
|
|
3
|
+
|
|
4
|
+
export interface MetaDataBlockProps {
|
|
5
|
+
heading: string
|
|
6
|
+
item: React.ReactNode | string[] | number
|
|
7
|
+
className?: string
|
|
8
|
+
renderEditor?: () => React.ReactNode
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const MetaDataBlock = ({
|
|
12
|
+
heading,
|
|
13
|
+
item,
|
|
14
|
+
className,
|
|
15
|
+
renderEditor,
|
|
16
|
+
}: MetaDataBlockProps) => {
|
|
17
|
+
const fieldContent = renderEditor ? renderEditor() : item
|
|
18
|
+
return (
|
|
19
|
+
<div className={"dataset-meta-block " + className}>
|
|
20
|
+
<h2 className="dmb-heading">{heading}</h2>
|
|
21
|
+
<>{fieldContent}</>
|
|
22
|
+
</div>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
export interface MetaDataListBlockProps {
|
|
4
|
+
heading: string
|
|
5
|
+
item: React.ReactNode | string[] | number
|
|
6
|
+
className?: string
|
|
7
|
+
renderEditor?: () => React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const MetaDataListBlock = ({
|
|
11
|
+
heading,
|
|
12
|
+
item,
|
|
13
|
+
className,
|
|
14
|
+
renderEditor,
|
|
15
|
+
}: MetaDataListBlockProps) => {
|
|
16
|
+
const fieldContent = renderEditor ? renderEditor() : item
|
|
17
|
+
return (
|
|
18
|
+
<div className={"dataset-meta-block " + className}>
|
|
19
|
+
<h2 className="dmb-heading">{heading}</h2>
|
|
20
|
+
{fieldContent}
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Link } from "react-router-dom"
|
|
3
|
+
|
|
4
|
+
export interface ModalitiesMetaDataBlockProps {
|
|
5
|
+
items: string[]
|
|
6
|
+
className?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ModalitiesMetaDataBlock = ({
|
|
10
|
+
items,
|
|
11
|
+
className,
|
|
12
|
+
}: ModalitiesMetaDataBlockProps) => {
|
|
13
|
+
const customCase = {
|
|
14
|
+
mri: "MRI",
|
|
15
|
+
ieeg: "iEEG",
|
|
16
|
+
pet: "PET",
|
|
17
|
+
eeg: "EEG",
|
|
18
|
+
meg: "MEG",
|
|
19
|
+
}
|
|
20
|
+
return (
|
|
21
|
+
<div className={"dataset-meta-block " + className}>
|
|
22
|
+
<h2 className="dmb-heading">Available Modalities</h2>
|
|
23
|
+
{items.map((item, index) => (
|
|
24
|
+
<Link key={index} to={"/search/modality/" + item.toLowerCase()}>
|
|
25
|
+
{item.toLowerCase() in customCase
|
|
26
|
+
? customCase[item.toLowerCase()]
|
|
27
|
+
: item}
|
|
28
|
+
</Link>
|
|
29
|
+
))}
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Tooltip } from "@openneuro/components/tooltip"
|
|
3
|
+
import { Button } from "@openneuro/components/button"
|
|
4
|
+
|
|
5
|
+
export interface NemarButtonProps {
|
|
6
|
+
datasetId: string
|
|
7
|
+
onNemar: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const NemarButton: React.FC<NemarButtonProps> = ({
|
|
11
|
+
datasetId,
|
|
12
|
+
onNemar,
|
|
13
|
+
}) => {
|
|
14
|
+
const url = `https://nemar.org/dataexplorer/detail?dataset_id=${datasetId}`
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
{onNemar && (
|
|
18
|
+
<div className="brainlife-block">
|
|
19
|
+
<Tooltip tooltip="View and analyze on NEMAR" flow="up">
|
|
20
|
+
<Button
|
|
21
|
+
className="brainlife-link"
|
|
22
|
+
primary={true}
|
|
23
|
+
size="small"
|
|
24
|
+
onClick={() => {
|
|
25
|
+
window.open(url, "_blank")
|
|
26
|
+
}}
|
|
27
|
+
label="NEMAR"
|
|
28
|
+
/>
|
|
29
|
+
</Tooltip>
|
|
30
|
+
</div>
|
|
31
|
+
)}
|
|
32
|
+
</>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
export interface ValidationBlockProps {
|
|
4
|
+
children?: React.ReactNode
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const ValidationBlock: React.FC<ValidationBlockProps> = ({
|
|
8
|
+
children,
|
|
9
|
+
}) => {
|
|
10
|
+
return <div className="validation-accordion">{children}</div>
|
|
11
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Link } from "react-router-dom"
|
|
3
|
+
import { Dropdown } from "@openneuro/components/dropdown"
|
|
4
|
+
|
|
5
|
+
export interface VersionListProps {
|
|
6
|
+
items: {
|
|
7
|
+
label: string
|
|
8
|
+
value: string
|
|
9
|
+
id: string
|
|
10
|
+
tag: string
|
|
11
|
+
created: Date
|
|
12
|
+
deprecated: boolean
|
|
13
|
+
}[]
|
|
14
|
+
hasEdit: boolean
|
|
15
|
+
selected: string
|
|
16
|
+
setSelected: (selected: string) => void
|
|
17
|
+
className: string
|
|
18
|
+
activeDataset: string
|
|
19
|
+
dateModified: string
|
|
20
|
+
datasetId?: string
|
|
21
|
+
}
|
|
22
|
+
const formatDate = (dateObject) =>
|
|
23
|
+
new Date(dateObject).toISOString().split("T")[0]
|
|
24
|
+
|
|
25
|
+
export const VersionList = ({
|
|
26
|
+
items,
|
|
27
|
+
selected,
|
|
28
|
+
setSelected,
|
|
29
|
+
className,
|
|
30
|
+
dateModified,
|
|
31
|
+
datasetId,
|
|
32
|
+
hasEdit,
|
|
33
|
+
}: VersionListProps) => {
|
|
34
|
+
const deprecatedItem = (itemTag, itemCreated) => {
|
|
35
|
+
setSelected(itemTag)
|
|
36
|
+
}
|
|
37
|
+
const setVersion = (itemTag, itemCreated) => {
|
|
38
|
+
setSelected(itemTag)
|
|
39
|
+
}
|
|
40
|
+
return (
|
|
41
|
+
<>
|
|
42
|
+
<div className="active-version">
|
|
43
|
+
<div>{selected === "draft" ? "Draft" : selected}</div>
|
|
44
|
+
{selected === "draft" ? "Updated" : "Created"}: {dateModified}
|
|
45
|
+
</div>
|
|
46
|
+
{items.length
|
|
47
|
+
? (
|
|
48
|
+
<Dropdown
|
|
49
|
+
className={className}
|
|
50
|
+
label={
|
|
51
|
+
<div className="version-list-label">
|
|
52
|
+
<b>Versions</b>
|
|
53
|
+
<i className="fas fa-chevron-up" />
|
|
54
|
+
<i className="fas fa-chevron-down" />
|
|
55
|
+
</div>
|
|
56
|
+
}
|
|
57
|
+
>
|
|
58
|
+
<div className="version-list-dropdown">
|
|
59
|
+
<ul>
|
|
60
|
+
<li
|
|
61
|
+
onClick={() => setVersion("draft", dateModified)}
|
|
62
|
+
className={selected === "draft" ? "selected" : ""}
|
|
63
|
+
>
|
|
64
|
+
<Link className="dataset-tool" to={"/datasets/" + datasetId}>
|
|
65
|
+
<span className="label">
|
|
66
|
+
Draft
|
|
67
|
+
<span className="active">
|
|
68
|
+
{selected === "draft" ? "*" : ""}
|
|
69
|
+
</span>
|
|
70
|
+
</span>
|
|
71
|
+
{dateModified}
|
|
72
|
+
</Link>
|
|
73
|
+
</li>
|
|
74
|
+
{items.map((item, index) => (
|
|
75
|
+
<li
|
|
76
|
+
key={index}
|
|
77
|
+
onClick={item.deprecated === true
|
|
78
|
+
? () => deprecatedItem(item.tag, item.created)
|
|
79
|
+
: () => setVersion(item.tag, formatDate(item.created))}
|
|
80
|
+
className={selected === item.tag ? "selected" : ""}
|
|
81
|
+
>
|
|
82
|
+
<Link
|
|
83
|
+
className="dataset-tool"
|
|
84
|
+
to={"/datasets/" + datasetId + "/versions/" + item.tag}
|
|
85
|
+
>
|
|
86
|
+
<span className="label">
|
|
87
|
+
v{item.tag}
|
|
88
|
+
<span className="active">
|
|
89
|
+
{selected === item.tag ? "*" : ""}
|
|
90
|
+
</span>
|
|
91
|
+
<span className="deprecated">
|
|
92
|
+
{item.deprecated === true ? "Deprecated" : ""}
|
|
93
|
+
</span>
|
|
94
|
+
</span>
|
|
95
|
+
{formatDate(item.created)}
|
|
96
|
+
</Link>
|
|
97
|
+
</li>
|
|
98
|
+
))}
|
|
99
|
+
</ul>
|
|
100
|
+
</div>
|
|
101
|
+
</Dropdown>
|
|
102
|
+
)
|
|
103
|
+
: hasEdit
|
|
104
|
+
? (
|
|
105
|
+
<Link
|
|
106
|
+
className="dataset-tool"
|
|
107
|
+
to={"/datasets/" + datasetId + "/snapshot"}
|
|
108
|
+
>
|
|
109
|
+
Create Version
|
|
110
|
+
</Link>
|
|
111
|
+
)
|
|
112
|
+
: null}
|
|
113
|
+
</>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { render, screen } from "@testing-library/react"
|
|
3
|
+
import { DatasetAlert } from "../DatasetAlert"
|
|
4
|
+
|
|
5
|
+
describe("DatasetAlert component", () => {
|
|
6
|
+
it("all props are rendered", () => {
|
|
7
|
+
render(
|
|
8
|
+
<DatasetAlert alert="alert text" footer="footer text" level="warning">
|
|
9
|
+
<span role="marquee">test text</span>
|
|
10
|
+
</DatasetAlert>,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
expect(screen.getByRole("marquee")).toBeVisible()
|
|
14
|
+
expect(screen.getByText("alert text")).toBeVisible()
|
|
15
|
+
expect(screen.getByText("footer text")).toBeVisible()
|
|
16
|
+
})
|
|
17
|
+
it('sets the correct alert class based on "level" prop', () => {
|
|
18
|
+
render(
|
|
19
|
+
<DatasetAlert alert="alert text" footer="footer text" level="warning">
|
|
20
|
+
Warning!
|
|
21
|
+
</DatasetAlert>,
|
|
22
|
+
)
|
|
23
|
+
expect(screen.getByRole("alert")).toHaveClass("warning")
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { render, screen } from "@testing-library/react"
|
|
3
|
+
import { MemoryRouter } from "react-router-dom"
|
|
4
|
+
import { DatasetHeader } from "../DatasetHeader"
|
|
5
|
+
|
|
6
|
+
describe("DatasetHeader component", () => {
|
|
7
|
+
it("renders with an undefined modality", () => {
|
|
8
|
+
render(
|
|
9
|
+
<DatasetHeader
|
|
10
|
+
pageHeading="test page"
|
|
11
|
+
modality={undefined}
|
|
12
|
+
renderEditor={() => <></>}
|
|
13
|
+
/>,
|
|
14
|
+
{ wrapper: MemoryRouter },
|
|
15
|
+
)
|
|
16
|
+
expect(screen.queryByRole("heading")).toBeInTheDocument()
|
|
17
|
+
})
|
|
18
|
+
})
|