@openneuro/app 4.22.0 → 4.24.0-alpha.0
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 +18 -16
- package/src/client.jsx +4 -3
- package/src/scripts/components/data-table.tsx +25 -3
- package/src/scripts/dataset/__tests__/__snapshots__/snapshot-container.spec.tsx.snap +36 -34
- package/src/scripts/dataset/components/DatasetTools.tsx +1 -0
- package/src/scripts/dataset/components/NemarButton.tsx +5 -1
- package/src/scripts/dataset/files/viewers/file-viewer-nifti.tsx +11 -2
- package/src/scripts/dataset/routes/derivatives.tsx +8 -0
- package/src/scripts/errors/errorRoute.jsx +14 -14
- package/src/scripts/routes.tsx +1 -1
- package/src/scripts/scss/dataset/dataset-page.scss +3 -0
- package/src/scripts/search/initial-search-params.tsx +5 -38
- package/src/scripts/search/inputs/dataset-type-select.tsx +33 -0
- package/src/scripts/search/inputs/index.ts +2 -0
- package/src/scripts/search/search-container.tsx +8 -8
- package/src/scripts/search/search-params-ctx.tsx +2 -0
- package/src/scripts/utils/__tests__/__snapshots__/markdown.spec.tsx.snap +27 -15
- package/src/scripts/utils/markdown.tsx +6 -2
- package/src/scripts/validation/validation-results.issues.issue.jsx +1 -1
- package/src/scripts/validation/validation-results.issues.jsx +23 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openneuro/app",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.24.0-alpha.0",
|
|
4
4
|
"description": "React JS web frontend for the OpenNeuro platform.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "public/client.js",
|
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@apollo/client": "3.7.2",
|
|
18
18
|
"@artsy/fresnel": "^1.3.1",
|
|
19
|
-
"@elastic/apm-rum": "5.
|
|
19
|
+
"@elastic/apm-rum": "5.16.0",
|
|
20
20
|
"@emotion/react": "11.11.1",
|
|
21
21
|
"@emotion/styled": "11.11.0",
|
|
22
22
|
"@niivue/niivue": "0.34.0",
|
|
23
|
-
"@openneuro/client": "^4.
|
|
24
|
-
"@openneuro/components": "^4.
|
|
23
|
+
"@openneuro/client": "^4.24.0-alpha.0",
|
|
24
|
+
"@openneuro/components": "^4.24.0-alpha.0",
|
|
25
25
|
"@tanstack/react-table": "^8.9.3",
|
|
26
|
-
"bids-validator": "1.
|
|
26
|
+
"bids-validator": "1.14.6",
|
|
27
27
|
"bytes": "^3.0.0",
|
|
28
28
|
"comlink": "^4.0.5",
|
|
29
29
|
"date-fns": "^2.16.1",
|
|
@@ -32,45 +32,47 @@
|
|
|
32
32
|
"email-validator": "^2.0.4",
|
|
33
33
|
"graphql": "16.8.1",
|
|
34
34
|
"jwt-decode": "^2.2.0",
|
|
35
|
-
"markdown-to-jsx": "^7.4.0",
|
|
36
35
|
"pluralize": "8.0.0",
|
|
37
36
|
"prop-types": "^15.6.0",
|
|
38
|
-
"react": "^
|
|
37
|
+
"react": "^18.2.0",
|
|
39
38
|
"react-cookie": "4.0.3",
|
|
40
39
|
"react-copy-to-clipboard": "^5.0.1",
|
|
41
|
-
"react-dom": "^
|
|
40
|
+
"react-dom": "^18.2.0",
|
|
42
41
|
"react-helmet": "6.1.0",
|
|
42
|
+
"react-markdown": "^9.0.1",
|
|
43
43
|
"react-router-dom": "6.3.0",
|
|
44
44
|
"react-toastify": "6.0.9",
|
|
45
45
|
"react-usestateref": "^1.0.8",
|
|
46
|
+
"rehype-raw": "^7.0.0",
|
|
47
|
+
"remark-gfm": "^4.0.0",
|
|
46
48
|
"semver": "^5.5.0",
|
|
47
49
|
"subscriptions-transport-ws": "0.11.0",
|
|
48
50
|
"universal-cookie": "^4.0.4"
|
|
49
51
|
},
|
|
50
52
|
"devDependencies": {
|
|
51
|
-
"@testing-library/jest-dom": "6.
|
|
52
|
-
"@testing-library/react": "^
|
|
53
|
+
"@testing-library/jest-dom": "6.4.2",
|
|
54
|
+
"@testing-library/react": "^14.2.1",
|
|
53
55
|
"@types/dompurify": "^3",
|
|
54
56
|
"@types/jsdom": "^16",
|
|
55
|
-
"@types/node": "
|
|
57
|
+
"@types/node": "20.12.7",
|
|
56
58
|
"@types/react": "^17.0.8",
|
|
57
|
-
"@types/react-dom": "^
|
|
59
|
+
"@types/react-dom": "^18.2.19",
|
|
58
60
|
"@types/react-router-dom": "5.3.3",
|
|
59
61
|
"@types/testing-library__jest-dom": "5.14.5",
|
|
60
62
|
"core-js": "3.25.1",
|
|
61
63
|
"esbuild-plugin-globals": "^0.1.1",
|
|
62
64
|
"history": "5.3.0",
|
|
63
|
-
"jsdom": "
|
|
65
|
+
"jsdom": "24.0.0",
|
|
64
66
|
"object.fromentries": "^2.0.0",
|
|
65
67
|
"rollup-plugin-polyfill-node": "0.12.0",
|
|
66
68
|
"sass": "^1.32.8",
|
|
67
69
|
"stream-browserify": "^3.0.0",
|
|
68
70
|
"typescript": "5.1.6",
|
|
69
|
-
"vite": "4.5.
|
|
70
|
-
"vitest": "
|
|
71
|
+
"vite": "4.5.3",
|
|
72
|
+
"vitest": "1.5.0"
|
|
71
73
|
},
|
|
72
74
|
"publishConfig": {
|
|
73
75
|
"access": "public"
|
|
74
76
|
},
|
|
75
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "4e271231c20d21e4c8b5ba8851d67cb94b7f739a"
|
|
76
78
|
}
|
package/src/client.jsx
CHANGED
|
@@ -6,7 +6,7 @@ import "./scripts/apm.js"
|
|
|
6
6
|
import { ApolloProvider, InMemoryCache } from "@apollo/client"
|
|
7
7
|
import { createClient } from "@openneuro/client"
|
|
8
8
|
import React from "react"
|
|
9
|
-
import
|
|
9
|
+
import { createRoot } from "react-dom/client"
|
|
10
10
|
import { BrowserRouter, Route, Routes } from "react-router-dom"
|
|
11
11
|
import App from "./scripts/app"
|
|
12
12
|
import Index from "./scripts/index"
|
|
@@ -18,7 +18,9 @@ import "@openneuro/components/page/page.scss"
|
|
|
18
18
|
|
|
19
19
|
gtag.initialize(config.analytics.trackingIds)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
const mainElement = document.getElementById("main")
|
|
22
|
+
const container = createRoot(mainElement)
|
|
23
|
+
container.render(
|
|
22
24
|
<App>
|
|
23
25
|
<ApolloProvider
|
|
24
26
|
client={createClient(`${config.url}/crn/graphql`, {
|
|
@@ -41,5 +43,4 @@ ReactDOM.render(
|
|
|
41
43
|
</BrowserRouter>
|
|
42
44
|
</ApolloProvider>
|
|
43
45
|
</App>,
|
|
44
|
-
document.getElementById("main"),
|
|
45
46
|
)
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useReactTable,
|
|
9
9
|
} from "@tanstack/react-table"
|
|
10
10
|
import styled from "@emotion/styled"
|
|
11
|
-
import { format, isValid, parseISO } from "date-fns"
|
|
11
|
+
import { format, isValid, parse, parseISO } from "date-fns"
|
|
12
12
|
|
|
13
13
|
interface DataTableProps {
|
|
14
14
|
data: any[]
|
|
@@ -33,10 +33,32 @@ const TD = styled.td`
|
|
|
33
33
|
padding: 3px;
|
|
34
34
|
`
|
|
35
35
|
|
|
36
|
+
function extractDateString(dateString) {
|
|
37
|
+
const formats = [
|
|
38
|
+
"yyyy-MM-dd", // ISO 8601
|
|
39
|
+
"yyyy-MM-ddTHH:mm:ss", // ISO 8601 with time
|
|
40
|
+
"MM/dd/yyyy", // US (M/D/YYYY)
|
|
41
|
+
"dd/MM/yyyy", // European (D/M/YYYY)
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
for (const format of formats) {
|
|
45
|
+
const parsedDate = parse(dateString, format, new Date())
|
|
46
|
+
if (isValid(parsedDate)) {
|
|
47
|
+
return parsedDate
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
|
|
36
54
|
function CellFormat(props): any {
|
|
37
55
|
const value = props.getValue()
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
let extractedDate
|
|
57
|
+
if (typeof value === "string") {
|
|
58
|
+
extractedDate = extractDateString(value)
|
|
59
|
+
}
|
|
60
|
+
if (extractedDate instanceof Date) {
|
|
61
|
+
return format(extractedDate, "yyyy-MM-dd")
|
|
40
62
|
} else if (typeof value === "string" && /^ds[0-9]{6}$/.exec(value)) {
|
|
41
63
|
return <a href={`/datasets/${value}`}>{value}</a>
|
|
42
64
|
} else if (Array.isArray(value)) {
|
|
@@ -13,7 +13,7 @@ exports[`SnapshotContainer component > includes JSON-LD data in the header 1`] =
|
|
|
13
13
|
"onChangeClientState": [Function],
|
|
14
14
|
"scriptTags": [
|
|
15
15
|
{
|
|
16
|
-
"innerHTML": "{
|
|
16
|
+
"innerHTML": "{"@context":"http://schema.org","@type":"Dataset","name":"DS003-downsampled (only T1)","author":[{"@type":"Person","name":"J. Doe","givenName":"J.","familyName":"Doe"},{"@type":"Person","name":"J. Doe","givenName":"J.","familyName":"Doe"},{"@type":"Person","name":"J. Doe","givenName":"J.","familyName":"Doe"}],"datePublished":"2021-12-17T22:32:08.000Z","dateModified":"2021-12-17T22:32:08.000Z","license":"https://creativecommons.org/publicdomain/zero/1.0/","publisher":{"@type":"Organization","name":"OpenNeuro"},"description":"This dataset was obtained from the OpenfMRI project (http://www.openfmri.org).\\nAccession #: ds003\\nDescription: Rhyme judgment\\n\\nRelease history:\\n10/06/2011: initial release\\n3/21/2013: Updated release with QA information\\n\\nThis dataset is made available under the Public Domain Dedication and License \\nv1.0, whose full text can be found at \\nhttp://www.opendatacommons.org/licenses/pddl/1.0/. \\nWe hope that all users will follow the ODC Attribution/Share-Alike \\nCommunity Norms (http://www.opendatacommons.org/norms/odc-by-sa/); \\nin particular, while not legally required, we hope that all users \\nof the data will acknowledge the OpenfMRI project and NSF Grant \\nOCI-1131441 (R. Poldrack, PI) in any publications.\\n","version":"1.0.0","url":"https://openneuro.org/datasets/ds001032/versions/1.0.0"}",
|
|
17
17
|
"type": "application/ld+json",
|
|
18
18
|
},
|
|
19
19
|
],
|
|
@@ -498,46 +498,48 @@ exports[`SnapshotContainer component > renders successfully 1`] = `
|
|
|
498
498
|
<div>
|
|
499
499
|
<article>
|
|
500
500
|
<section>
|
|
501
|
-
<
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
).
|
|
501
|
+
<p>
|
|
502
|
+
This dataset was obtained from the OpenfMRI project (
|
|
503
|
+
<a
|
|
504
|
+
href="http://www.openfmri.org"
|
|
505
|
+
>
|
|
506
|
+
http://www.openfmri.org
|
|
507
|
+
</a>
|
|
508
|
+
).
|
|
510
509
|
Accession #: ds003
|
|
511
510
|
Description: Rhyme judgment
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
511
|
+
</p>
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
<p>
|
|
515
|
+
Release history:
|
|
515
516
|
10/06/2011: initial release
|
|
516
517
|
3/21/2013: Updated release with QA information
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
This dataset is made available under the Public Domain Dedication and License
|
|
520
|
-
v1.0, whose full text can be found at
|
|
518
|
+
</p>
|
|
519
|
+
|
|
521
520
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
.
|
|
528
|
-
|
|
521
|
+
<p>
|
|
522
|
+
This dataset is made available under the Public Domain Dedication and License
|
|
523
|
+
v1.0, whose full text can be found at
|
|
524
|
+
|
|
525
|
+
<a
|
|
526
|
+
href="http://www.opendatacommons.org/licenses/pddl/1.0/"
|
|
527
|
+
>
|
|
528
|
+
http://www.opendatacommons.org/licenses/pddl/1.0/
|
|
529
|
+
</a>
|
|
530
|
+
.
|
|
531
|
+
We hope that all users will follow the ODC Attribution/Share-Alike
|
|
529
532
|
Community Norms (
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
in particular, while not legally required, we hope that all users
|
|
537
|
-
of the data will acknowledge the OpenfMRI project and NSF Grant
|
|
533
|
+
<a
|
|
534
|
+
href="http://www.opendatacommons.org/norms/odc-by-sa/"
|
|
535
|
+
>
|
|
536
|
+
http://www.opendatacommons.org/norms/odc-by-sa/
|
|
537
|
+
</a>
|
|
538
|
+
);
|
|
539
|
+
in particular, while not legally required, we hope that all users
|
|
540
|
+
of the data will acknowledge the OpenfMRI project and NSF Grant
|
|
538
541
|
OCI-1131441 (R. Poldrack, PI) in any publications.
|
|
539
|
-
|
|
540
|
-
</div>
|
|
542
|
+
</p>
|
|
541
543
|
</section>
|
|
542
544
|
</article>
|
|
543
545
|
</div>
|
|
@@ -16,7 +16,11 @@ export const NemarButton: React.FC<NemarButtonProps> = ({
|
|
|
16
16
|
<>
|
|
17
17
|
{onNemar && (
|
|
18
18
|
<div className="brainlife-block">
|
|
19
|
-
<Tooltip
|
|
19
|
+
<Tooltip
|
|
20
|
+
tooltip="View and analyze this dataset on the NEMAR OpenNeuro portal for MEG, iEEG, and EEG data"
|
|
21
|
+
flow="up"
|
|
22
|
+
wrapText={true}
|
|
23
|
+
>
|
|
20
24
|
<Button
|
|
21
25
|
className="brainlife-link"
|
|
22
26
|
primary={true}
|
|
@@ -14,14 +14,23 @@ const FileViewerNifti = ({
|
|
|
14
14
|
url: imageUrl,
|
|
15
15
|
colorMap: "gray",
|
|
16
16
|
opacity: 1,
|
|
17
|
-
visible: true,
|
|
18
17
|
limitFrames4D: 5,
|
|
19
18
|
},
|
|
20
19
|
]
|
|
21
20
|
const nv = new Niivue({ dragAndDropEnabled: false })
|
|
22
21
|
;(window as any).niivue = nv
|
|
23
22
|
nv.attachToCanvas(canvas.current)
|
|
24
|
-
nv.loadVolumes(volumeList) // press the "v" key to cycle through
|
|
23
|
+
nv.loadVolumes(volumeList) // press the "v" key to cycle through views (axial, coronal, sagittal, 3D, etc.)
|
|
24
|
+
nv.graph.autoSizeMultiplanar = true // use autosizing
|
|
25
|
+
nv.opts.multiplanarForceRender = true // ensure that we draw the time series graph in the tile usually reserved for the 3D render
|
|
26
|
+
nv.graph.normalizeValues = false // use raw data values on y-axis
|
|
27
|
+
nv.graph.opacity = 1.0 // show the graph
|
|
28
|
+
// Notes:
|
|
29
|
+
// 1. If an image only has one volume, the timeseries graph will not be visible.
|
|
30
|
+
// The 3D render will be placed in the graph tile instead.
|
|
31
|
+
// 2. Users can navigate volumes forward and backward in the series using the left and right arrow keys on desktop devices
|
|
32
|
+
// 3. On touch screens, users can tap on the timeseries graph to jump to a volume index
|
|
33
|
+
// 4. Users can load all volumes by clicking on the "..." displayed on the timeseries graph
|
|
25
34
|
}, [imageUrl])
|
|
26
35
|
|
|
27
36
|
return <canvas ref={canvas} height={800} />
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from "react"
|
|
2
|
+
import { Navigate, useParams } from "react-router-dom"
|
|
2
3
|
import DownloadS3Derivative from "../download/download-derivative-s3"
|
|
3
4
|
import DownloadDataLadDerivative from "../download/download-derivative-datalad"
|
|
4
5
|
import { DatasetPageBorder } from "./styles/dataset-page-border"
|
|
5
6
|
import { HeaderRow3 } from "./styles/header-row"
|
|
7
|
+
import { useAgreement } from "../../components/agreement"
|
|
6
8
|
|
|
7
9
|
interface DerivativeElementProps {
|
|
8
10
|
name: string
|
|
@@ -38,6 +40,12 @@ interface DerivativesProps {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const Derivatives = ({ derivatives }: DerivativesProps): JSX.Element => {
|
|
43
|
+
const { datasetId, tag: snapshotTag } = useParams()
|
|
44
|
+
const [agreed] = useAgreement()
|
|
45
|
+
// If the derivatives page is directly visited without the agreement, return to the dataset page
|
|
46
|
+
if (!agreed) {
|
|
47
|
+
return <Navigate to={`/datasets/${datasetId}`} replace={true} />
|
|
48
|
+
}
|
|
41
49
|
return (
|
|
42
50
|
<DatasetPageBorder>
|
|
43
51
|
<HeaderRow3>Available Derivatives</HeaderRow3>
|
|
@@ -7,22 +7,22 @@ import OrcidGeneral from "./orcid/general.jsx"
|
|
|
7
7
|
import OrcidEmail from "./orcid/email.jsx"
|
|
8
8
|
import OrcidGiven from "./orcid/given.jsx"
|
|
9
9
|
import OrcidFamily from "./orcid/family.jsx"
|
|
10
|
+
import FourOFourPage from "./404page"
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<div className="
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
</div>
|
|
12
|
+
function ErrorRoute() {
|
|
13
|
+
return (
|
|
14
|
+
<div className="container errors">
|
|
15
|
+
<div className="panel">
|
|
16
|
+
<Routes>
|
|
17
|
+
<Route path="orcid" element={<OrcidGeneral />} />
|
|
18
|
+
<Route path="orcid/email" element={<OrcidEmail />} />
|
|
19
|
+
<Route path="orcid/given" element={<OrcidGiven />} />
|
|
20
|
+
<Route path="orcid/family" element={<OrcidFamily />} />
|
|
21
|
+
<Route path="*" element={<FourOFourPage />} />
|
|
22
|
+
</Routes>
|
|
23
23
|
</div>
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
</div>
|
|
25
|
+
)
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export default ErrorRoute
|
package/src/scripts/routes.tsx
CHANGED
|
@@ -155,46 +155,11 @@ export interface SearchParams {
|
|
|
155
155
|
tracerRadionuclides: string[]
|
|
156
156
|
sortBy_available
|
|
157
157
|
sortBy_selected
|
|
158
|
+
bidsDatasetType_available: string[]
|
|
159
|
+
bidsDatasetType_selected: string | null
|
|
158
160
|
}
|
|
159
161
|
|
|
160
|
-
// TODO: move to this initial state
|
|
161
|
-
// and load dynamic options on mount
|
|
162
162
|
const initialSearchParams: SearchParams = {
|
|
163
|
-
keywords: [],
|
|
164
|
-
searchAllDatasets: false,
|
|
165
|
-
datasetType_available,
|
|
166
|
-
datasetType_selected: null,
|
|
167
|
-
datasetStatus_available,
|
|
168
|
-
datasetStatus_selected: null,
|
|
169
|
-
modality_available,
|
|
170
|
-
modality_selected: null,
|
|
171
|
-
ageRange: [null, null],
|
|
172
|
-
subjectCountRange: [null, null],
|
|
173
|
-
diagnosis_available: [],
|
|
174
|
-
diagnosis_selected: null,
|
|
175
|
-
tasks: [],
|
|
176
|
-
authors: [],
|
|
177
|
-
// more
|
|
178
|
-
sex_available: [],
|
|
179
|
-
sex_selected: null,
|
|
180
|
-
date_available: [],
|
|
181
|
-
date_selected: null,
|
|
182
|
-
species_available: [],
|
|
183
|
-
species_selected: null,
|
|
184
|
-
section_available: [],
|
|
185
|
-
section_selected: null,
|
|
186
|
-
studyDomains: [],
|
|
187
|
-
bodyParts: [],
|
|
188
|
-
scannerManufacturers: [],
|
|
189
|
-
scannerManufacturersModelNames: [],
|
|
190
|
-
tracerNames: [],
|
|
191
|
-
tracerRadionuclides: [],
|
|
192
|
-
sortBy_available: sortBy,
|
|
193
|
-
sortBy_selected: sortBy[0],
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// TODO: (stretch) delete and move to dynamically loaded initialSearchParams
|
|
197
|
-
const TEMPORARY_initialSearchParams: SearchParams = {
|
|
198
163
|
keywords: [],
|
|
199
164
|
searchAllDatasets: false,
|
|
200
165
|
datasetType_available,
|
|
@@ -237,6 +202,8 @@ const TEMPORARY_initialSearchParams: SearchParams = {
|
|
|
237
202
|
tracerRadionuclides: [],
|
|
238
203
|
sortBy_available: sortBy,
|
|
239
204
|
sortBy_selected: sortBy[0],
|
|
205
|
+
bidsDatasetType_available: ["raw", "derivative"],
|
|
206
|
+
bidsDatasetType_selected: null,
|
|
240
207
|
}
|
|
241
208
|
|
|
242
|
-
export default
|
|
209
|
+
export default initialSearchParams
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { FC, useContext } from "react"
|
|
2
|
+
import { SearchParamsCtx } from "../search-params-ctx"
|
|
3
|
+
import { FacetSelect } from "@openneuro/components/facets"
|
|
4
|
+
import { AccordionTab, AccordionWrap } from "@openneuro/components/accordion"
|
|
5
|
+
|
|
6
|
+
const DatasetTypeSelect: FC = () => {
|
|
7
|
+
const { searchParams, setSearchParams } = useContext(SearchParamsCtx)
|
|
8
|
+
|
|
9
|
+
const { bidsDatasetType_selected, bidsDatasetType_available } = searchParams
|
|
10
|
+
const setDatasetType = (bidsDatasetType_selected) =>
|
|
11
|
+
setSearchParams((prevState) => ({
|
|
12
|
+
...prevState,
|
|
13
|
+
bidsDatasetType_selected,
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<AccordionWrap className="facet-accordion">
|
|
18
|
+
<AccordionTab
|
|
19
|
+
accordionStyle="plain"
|
|
20
|
+
label="Dataset Type"
|
|
21
|
+
startOpen={false}
|
|
22
|
+
>
|
|
23
|
+
<FacetSelect
|
|
24
|
+
selected={bidsDatasetType_selected}
|
|
25
|
+
setSelected={setDatasetType}
|
|
26
|
+
items={bidsDatasetType_available}
|
|
27
|
+
/>
|
|
28
|
+
</AccordionTab>
|
|
29
|
+
</AccordionWrap>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default DatasetTypeSelect
|
|
@@ -4,6 +4,7 @@ import ModalitySelect from "./modality-select"
|
|
|
4
4
|
import ShowDatasetRadios from "./show-datasets-radios"
|
|
5
5
|
import AgeRangeInput from "./age-range-input"
|
|
6
6
|
import SubjectCountRangeInput from "./subject-count-range-input"
|
|
7
|
+
import DatasetTypeSelect from "./dataset-type-select"
|
|
7
8
|
import DiagnosisSelect from "./diagnosis-select"
|
|
8
9
|
import TaskInput from "./task-input"
|
|
9
10
|
import AuthorInput from "./author-input"
|
|
@@ -24,6 +25,7 @@ export {
|
|
|
24
25
|
AllDatasetsToggle,
|
|
25
26
|
AuthorInput,
|
|
26
27
|
BodyPartsInput,
|
|
28
|
+
DatasetTypeSelect,
|
|
27
29
|
DateRadios,
|
|
28
30
|
DiagnosisSelect,
|
|
29
31
|
KeywordInput,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
AllDatasetsToggle,
|
|
17
17
|
AuthorInput,
|
|
18
18
|
BodyPartsInput,
|
|
19
|
+
DatasetTypeSelect,
|
|
19
20
|
DateRadios,
|
|
20
21
|
DiagnosisSelect,
|
|
21
22
|
KeywordInput,
|
|
@@ -101,14 +102,12 @@ const SearchContainer: FC<SearchContainerProps> = ({ portalContent }) => {
|
|
|
101
102
|
|
|
102
103
|
const { searchParams, setSearchParams } = useContext(SearchParamsCtx)
|
|
103
104
|
const modality = portalContent?.modality || null
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
)
|
|
111
|
-
}, [modality, searchParams.modality_selected, setSearchParams, location])
|
|
105
|
+
setDefaultSearch(
|
|
106
|
+
modality,
|
|
107
|
+
searchParams,
|
|
108
|
+
setSearchParams,
|
|
109
|
+
new URLSearchParams(location.search),
|
|
110
|
+
)
|
|
112
111
|
|
|
113
112
|
const { loading, data, fetchMore, refetch, variables, error } =
|
|
114
113
|
useSearchResults()
|
|
@@ -166,6 +165,7 @@ const SearchContainer: FC<SearchContainerProps> = ({ portalContent }) => {
|
|
|
166
165
|
{!portalContent
|
|
167
166
|
? <ModalitySelect portalStyles={true} label="Modalities" />
|
|
168
167
|
: <ModalitySelect portalStyles={false} label="Choose Modality" />}
|
|
168
|
+
<DatasetTypeSelect />
|
|
169
169
|
<AgeRangeInput />
|
|
170
170
|
<SubjectCountRangeInput />
|
|
171
171
|
<DiagnosisSelect />
|
|
@@ -123,6 +123,7 @@ export const getSelectParams = ({
|
|
|
123
123
|
scannerManufacturersModelNames,
|
|
124
124
|
tracerNames,
|
|
125
125
|
tracerRadionuclides,
|
|
126
|
+
bidsDatasetType_selected,
|
|
126
127
|
}) => ({
|
|
127
128
|
keywords,
|
|
128
129
|
modality_selected,
|
|
@@ -144,6 +145,7 @@ export const getSelectParams = ({
|
|
|
144
145
|
scannerManufacturersModelNames,
|
|
145
146
|
tracerNames,
|
|
146
147
|
tracerRadionuclides,
|
|
148
|
+
bidsDatasetType_selected,
|
|
147
149
|
})
|
|
148
150
|
|
|
149
151
|
/**
|
|
@@ -2,38 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`Test <Markdown> component > allows a href with certain protocols 1`] = `
|
|
4
4
|
<DocumentFragment>
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
<p>
|
|
6
|
+
<a
|
|
7
|
+
href="https://example.com"
|
|
8
|
+
>
|
|
9
|
+
Example link that should work.
|
|
10
|
+
</a>
|
|
11
|
+
</p>
|
|
10
12
|
</DocumentFragment>
|
|
11
13
|
`;
|
|
12
14
|
|
|
13
15
|
exports[`Test <Markdown> component > does not allow href with unknown protocols 1`] = `
|
|
14
16
|
<DocumentFragment>
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
<p>
|
|
18
|
+
<a>
|
|
19
|
+
Example link that should not work.
|
|
20
|
+
</a>
|
|
21
|
+
</p>
|
|
18
22
|
</DocumentFragment>
|
|
19
23
|
`;
|
|
20
24
|
|
|
21
25
|
exports[`Test <Markdown> component > filters close-break tags 1`] = `
|
|
22
26
|
<DocumentFragment>
|
|
23
|
-
<
|
|
27
|
+
<p>
|
|
24
28
|
<br />
|
|
25
29
|
sample text
|
|
26
30
|
<br />
|
|
27
|
-
</
|
|
31
|
+
</p>
|
|
28
32
|
</DocumentFragment>
|
|
29
33
|
`;
|
|
30
34
|
|
|
31
35
|
exports[`Test <Markdown> component > filters out disallowed tags 1`] = `
|
|
32
36
|
<DocumentFragment>
|
|
33
37
|
<ul>
|
|
38
|
+
|
|
39
|
+
|
|
34
40
|
<li>
|
|
35
41
|
Markdown document
|
|
36
42
|
</li>
|
|
43
|
+
|
|
44
|
+
|
|
37
45
|
</ul>
|
|
38
46
|
</DocumentFragment>
|
|
39
47
|
`;
|
|
@@ -41,17 +49,21 @@ exports[`Test <Markdown> component > filters out disallowed tags 1`] = `
|
|
|
41
49
|
exports[`Test <Markdown> component > safely handles broken HTML tags 1`] = `
|
|
42
50
|
<DocumentFragment>
|
|
43
51
|
<ul>
|
|
52
|
+
|
|
53
|
+
|
|
44
54
|
<li>
|
|
45
55
|
Markdown document
|
|
46
56
|
|
|
47
57
|
<br />
|
|
48
58
|
<br />
|
|
49
|
-
<ul>
|
|
50
|
-
<li>
|
|
51
|
-
test content
|
|
52
|
-
</li>
|
|
53
|
-
</ul>
|
|
54
59
|
</li>
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
<li>
|
|
63
|
+
test content
|
|
64
|
+
</li>
|
|
65
|
+
|
|
66
|
+
|
|
55
67
|
</ul>
|
|
56
68
|
</DocumentFragment>
|
|
57
69
|
`;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from "react"
|
|
2
|
-
import
|
|
2
|
+
import ReactMarkdown from "react-markdown"
|
|
3
3
|
import DOMPurify from "dompurify"
|
|
4
|
+
import rehypeRaw from "rehype-raw"
|
|
5
|
+
import remarkGfm from "remark-gfm"
|
|
4
6
|
|
|
5
7
|
interface MarkdownProps {
|
|
6
8
|
children: string
|
|
@@ -161,7 +163,9 @@ export function Markdown({ children }: MarkdownProps) {
|
|
|
161
163
|
})
|
|
162
164
|
return (
|
|
163
165
|
<>
|
|
164
|
-
<
|
|
166
|
+
<ReactMarkdown rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
|
|
167
|
+
{sanitizedMarkdown}
|
|
168
|
+
</ReactMarkdown>
|
|
165
169
|
</>
|
|
166
170
|
)
|
|
167
171
|
}
|
|
@@ -31,23 +31,28 @@ class Issues extends React.Component {
|
|
|
31
31
|
</span>
|
|
32
32
|
)
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// Schema validator returns multiple files here
|
|
34
|
+
let subErrors = []
|
|
35
|
+
if (issue.files instanceof Map) {
|
|
36
|
+
// Schema validator returns multiple files here as a map
|
|
37
37
|
// map those to the old sub-issue model to display them
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
38
|
+
let index = 0
|
|
39
|
+
for (const [path, fObj] of issue.files) {
|
|
40
|
+
const error = {
|
|
41
|
+
reason: path,
|
|
42
|
+
file: fObj,
|
|
43
|
+
}
|
|
44
|
+
subErrors.push(
|
|
45
|
+
(<Issue
|
|
46
|
+
type={this.props.issueType}
|
|
47
|
+
error={error}
|
|
48
|
+
index={index}
|
|
49
|
+
key={index}
|
|
50
|
+
/>),
|
|
51
|
+
)
|
|
52
|
+
index += 1
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
subErrors = Array.from(issue.files).map((error, index2) => {
|
|
51
56
|
return error
|
|
52
57
|
? (
|
|
53
58
|
<Issue
|
|
@@ -59,8 +64,8 @@ class Issues extends React.Component {
|
|
|
59
64
|
/>
|
|
60
65
|
)
|
|
61
66
|
: null
|
|
62
|
-
}
|
|
63
|
-
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
64
69
|
|
|
65
70
|
if (issue.additionalFileCount > 0) {
|
|
66
71
|
subErrors.push(
|