@openneuro/app 4.33.3 → 4.34.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.
Files changed (302) hide show
  1. package/package.json +5 -4
  2. package/src/client.jsx +2 -1
  3. package/src/scripts/authentication/signOut.ts +1 -1
  4. package/src/scripts/common/containers/footer.tsx +1 -1
  5. package/src/scripts/common/containers/header.tsx +3 -2
  6. package/src/scripts/common/content/affiliate-content.jsx +72 -0
  7. package/src/scripts/common/content/assets/affiliates/bids.jpg +0 -0
  8. package/src/scripts/common/content/assets/affiliates/datalad.jpg +0 -0
  9. package/src/scripts/common/content/assets/affiliates/nih-bi-logo.png +0 -0
  10. package/src/scripts/common/content/assets/front-page/brand_mark.png +0 -0
  11. package/src/scripts/common/content/assets/front-page/cube-get.png +0 -0
  12. package/src/scripts/common/content/assets/front-page/cube-share.png +0 -0
  13. package/src/scripts/common/content/assets/front-page/cube-use.png +0 -0
  14. package/src/scripts/common/content/assets/front-page/ljaf.png +0 -0
  15. package/src/scripts/common/content/assets/front-page/logo_app.png +0 -0
  16. package/src/scripts/common/content/assets/front-page/logo_cube.png +0 -0
  17. package/src/scripts/common/content/assets/front-page/logo_data.png +0 -0
  18. package/src/scripts/common/content/assets/front-page/logo_users.png +0 -0
  19. package/src/scripts/common/content/assets/front-page/novo-logo.png +0 -0
  20. package/src/scripts/common/content/assets/front-page/nru-logo.png +0 -0
  21. package/src/scripts/common/content/assets/front-page/nsf.png +0 -0
  22. package/src/scripts/common/content/assets/front-page/sqm-logo.png +0 -0
  23. package/src/scripts/common/content/assets/front-page/squishymedia.png +0 -0
  24. package/src/scripts/common/content/assets/front-page/stanford.png +0 -0
  25. package/src/scripts/common/content/assets/modality-cubes/brain-initiative.jpg +0 -0
  26. package/src/scripts/common/content/assets/modality-cubes/eeg.jpg +0 -0
  27. package/src/scripts/common/content/assets/modality-cubes/ieeg.jpg +0 -0
  28. package/src/scripts/common/content/assets/modality-cubes/meg.jpg +0 -0
  29. package/src/scripts/common/content/assets/modality-cubes/mri.jpg +0 -0
  30. package/src/scripts/common/content/assets/modality-cubes/nih_cube.jpg +0 -0
  31. package/src/scripts/common/content/assets/modality-cubes/nirs.jpg +0 -0
  32. package/src/scripts/common/content/assets/modality-cubes/pet-scan.jpg +0 -0
  33. package/src/scripts/common/content/assets/portal-content/nih-bi-brand.png +0 -0
  34. package/src/scripts/common/content/facet-content.jsx +186 -0
  35. package/src/scripts/common/content/front-page-content.jsx +177 -0
  36. package/src/scripts/common/content/image-attribution.tsx +3 -3
  37. package/src/scripts/common/content/modality-cube-content.jsx +92 -0
  38. package/src/scripts/common/content/portal-content.jsx +149 -0
  39. package/src/scripts/common/content/portal-grant-content.jsx +30 -0
  40. package/src/scripts/common/content/sortby-list.jsx +18 -0
  41. package/src/scripts/common/forms/warn-button.jsx +1 -1
  42. package/src/scripts/components/accordion/AccordionTab.tsx +68 -0
  43. package/src/scripts/components/accordion/AccordionWrap.tsx +22 -0
  44. package/src/scripts/components/accordion/__tests__/AccordionTab.spec.tsx +35 -0
  45. package/src/scripts/components/accordion/accordion.scss +68 -0
  46. package/src/scripts/components/activity-slider/ActivitySlider.tsx +121 -0
  47. package/src/scripts/components/activity-slider/slider.scss +130 -0
  48. package/src/scripts/components/affiliate-article/AffiliateArticle.tsx +26 -0
  49. package/src/scripts/components/affiliate-article/affiliate.scss +32 -0
  50. package/src/scripts/components/aggregate-count/AggregateCount.tsx +20 -0
  51. package/src/scripts/components/assets/activity-icon.png +0 -0
  52. package/src/scripts/components/assets/close-button.png +0 -0
  53. package/src/scripts/components/assets/favicon copy.ico +0 -0
  54. package/src/scripts/components/assets/favicon.ico +0 -0
  55. package/src/scripts/components/assets/get_data_browse.png +0 -0
  56. package/src/scripts/components/assets/get_data_download.png +0 -0
  57. package/src/scripts/components/assets/ljaf copy.png +0 -0
  58. package/src/scripts/components/assets/logo_app copy.png +0 -0
  59. package/src/scripts/components/assets/logo_cube copy.png +0 -0
  60. package/src/scripts/components/assets/logo_data copy.png +0 -0
  61. package/src/scripts/components/assets/logo_users copy.png +0 -0
  62. package/src/scripts/components/assets/mri-scan.jpg +0 -0
  63. package/src/scripts/components/assets/nih copy.png +0 -0
  64. package/src/scripts/components/assets/nih-bi-brand.jpg +0 -0
  65. package/src/scripts/components/assets/nih-stanford.jpg +0 -0
  66. package/src/scripts/components/assets/nih.png +0 -0
  67. package/src/scripts/components/assets/nimh.png +0 -0
  68. package/src/scripts/components/assets/nsf copy.png +0 -0
  69. package/src/scripts/components/assets/on-dark-horz.svg +1 -0
  70. package/src/scripts/components/assets/on-dark.svg +1 -0
  71. package/src/scripts/components/assets/on-light-horz.svg +1 -0
  72. package/src/scripts/components/assets/on-light.svg +1 -0
  73. package/src/scripts/components/assets/orcid_24x24 copy.png +0 -0
  74. package/src/scripts/components/assets/orcid_24x24.png +0 -0
  75. package/src/scripts/components/assets/share_data_collaborate.png +0 -0
  76. package/src/scripts/components/assets/share_data_publish.png +0 -0
  77. package/src/scripts/components/assets/share_data_validate.png +0 -0
  78. package/src/scripts/components/assets/squishymedia copy.png +0 -0
  79. package/src/scripts/components/assets/stanford copy.png +0 -0
  80. package/src/scripts/components/assets/tab-get_data.png +0 -0
  81. package/src/scripts/components/assets/tab-share_data.png +0 -0
  82. package/src/scripts/components/assets/tab-use_data.png +0 -0
  83. package/src/scripts/components/assets/use_data_analyze.png +0 -0
  84. package/src/scripts/components/assets/use_data_snapshot.png +0 -0
  85. package/src/scripts/components/button/Button.tsx +113 -0
  86. package/src/scripts/components/button/__tests__/Button.spec.tsx +21 -0
  87. package/src/scripts/components/button/button.scss +140 -0
  88. package/src/scripts/components/count-toggle/CountToggle.tsx +65 -0
  89. package/src/scripts/components/count-toggle/__tests__/CountToggle.spec.tsx +31 -0
  90. package/src/scripts/components/count-toggle/count-toggle.scss +34 -0
  91. package/src/scripts/components/dropdown/Dropdown.tsx +38 -0
  92. package/src/scripts/components/dropdown/dropdown.scss +29 -0
  93. package/src/scripts/components/facets/FacetRadio.tsx +43 -0
  94. package/src/scripts/components/facets/FacetRange.tsx +35 -0
  95. package/src/scripts/components/facets/FacetSearch.tsx +66 -0
  96. package/src/scripts/components/facets/FacetSelect.tsx +87 -0
  97. package/src/scripts/components/facets/SingleSelect.tsx +39 -0
  98. package/src/scripts/components/facets/__tests__/FacetSelect.spec.tsx +14 -0
  99. package/src/scripts/components/facets/__tests__/SingleSelect.spec.tsx +58 -0
  100. package/src/scripts/components/facets/facet.scss +244 -0
  101. package/src/scripts/components/footer/Footer.tsx +52 -0
  102. package/src/scripts/components/footer/footer.scss +20 -0
  103. package/src/scripts/components/formatting/__tests__/modality-label.spec.tsx +34 -0
  104. package/src/scripts/components/formatting/modality-label.tsx +34 -0
  105. package/src/scripts/components/front-page/ActivityHeader.tsx +12 -0
  106. package/src/scripts/components/front-page/AffiliateBlock.tsx +22 -0
  107. package/src/scripts/components/front-page/Contributors.tsx +28 -0
  108. package/src/scripts/components/front-page/GetUpdates.tsx +99 -0
  109. package/src/scripts/components/front-page/Infographic.tsx +36 -0
  110. package/src/scripts/components/front-page/front-page.scss +295 -0
  111. package/src/scripts/components/header/Header.tsx +151 -0
  112. package/src/scripts/components/header/LandingExpandedHeader.tsx +110 -0
  113. package/src/scripts/components/header/header.scss +409 -0
  114. package/src/scripts/components/icon/Icon.tsx +55 -0
  115. package/src/scripts/components/icon/__tests__/Icon.spec.tsx +10 -0
  116. package/src/scripts/components/icon/icon.scss +8 -0
  117. package/src/scripts/components/input/Input.tsx +137 -0
  118. package/src/scripts/components/input/TermSearch.tsx +101 -0
  119. package/src/scripts/components/input/input.scss +147 -0
  120. package/src/scripts/components/input/term-search.scss +68 -0
  121. package/src/scripts/components/json-tree/ExpandableProperty.tsx +35 -0
  122. package/src/scripts/components/json-tree/RecursiveProperty.tsx +67 -0
  123. package/src/scripts/components/json-tree/json-tree.scss +19 -0
  124. package/src/scripts/components/loading/Loading.tsx +19 -0
  125. package/src/scripts/components/loading/__tests__/Loading.spec.tsx +12 -0
  126. package/src/scripts/components/loading/loading.scss +141 -0
  127. package/src/scripts/components/logo/Logo.tsx +44 -0
  128. package/src/scripts/components/logo/__tests__/Logo.spec.tsx +22 -0
  129. package/src/scripts/components/modal/Modal.tsx +51 -0
  130. package/src/scripts/components/modal/ModalExample.tsx +25 -0
  131. package/src/scripts/components/modal/UserLoginModal.tsx +74 -0
  132. package/src/scripts/components/modal/__tests__/UserLoginModal.spec.tsx +37 -0
  133. package/src/scripts/components/modal/modal.scss +60 -0
  134. package/src/scripts/components/modality-cube/ModalityCube.tsx +60 -0
  135. package/src/scripts/components/modality-cube/modality-cube.scss +303 -0
  136. package/src/scripts/components/page/Page.tsx +61 -0
  137. package/src/scripts/components/page/page.scss +408 -0
  138. package/src/scripts/components/progress-bar/ProgressBar.tsx +16 -0
  139. package/src/scripts/components/progress-bar/progress-bar.scss +34 -0
  140. package/src/scripts/components/radio/Radio.tsx +33 -0
  141. package/src/scripts/components/radio/RadioGroup.tsx +45 -0
  142. package/src/scripts/components/radio/__tests__/RadioGroup.spec.tsx +31 -0
  143. package/src/scripts/components/radio/radio.scss +335 -0
  144. package/src/scripts/components/range/TwoHandleRange.scss +108 -0
  145. package/src/scripts/components/range/TwoHandleRange.tsx +128 -0
  146. package/src/scripts/components/range/__tests__/TwoHandleRange.spec.tsx +34 -0
  147. package/src/scripts/components/read-more/ReadMore.tsx +50 -0
  148. package/src/scripts/components/read-more/__tests__/ReadMore.spec.tsx +14 -0
  149. package/src/scripts/components/read-more/read-more.scss +79 -0
  150. package/src/scripts/components/scss/_flex-grid.scss +216 -0
  151. package/src/scripts/components/scss/_helper-classes.scss +162 -0
  152. package/src/scripts/components/scss/_normalize.scss +614 -0
  153. package/src/scripts/components/scss/_variables.scss +245 -0
  154. package/src/scripts/components/scss/global.scss +124 -0
  155. package/src/scripts/components/scss/toasts.scss +33 -0
  156. package/src/scripts/components/scss/upload-modal.scss +479 -0
  157. package/src/scripts/components/search-page/CommunityHeader.tsx +27 -0
  158. package/src/scripts/components/search-page/FacetBlockContainerExample.tsx +6 -0
  159. package/src/scripts/components/search-page/FilterDateItem.tsx +44 -0
  160. package/src/scripts/components/search-page/FilterListItem.tsx +62 -0
  161. package/src/scripts/components/search-page/FiltersBlock.tsx +277 -0
  162. package/src/scripts/components/search-page/ModalityHeader.tsx +64 -0
  163. package/src/scripts/components/search-page/NeurobagelSearch.tsx +59 -0
  164. package/src/scripts/components/search-page/SearchPage.tsx +114 -0
  165. package/src/scripts/components/search-page/SearchResultItem.tsx +417 -0
  166. package/src/scripts/components/search-page/SearchResultsList.tsx +48 -0
  167. package/src/scripts/components/search-page/SearchSort.tsx +52 -0
  168. package/src/scripts/components/search-page/SearchSortContainerExample.tsx +9 -0
  169. package/src/scripts/components/search-page/TermListItem.tsx +41 -0
  170. package/src/scripts/components/search-page/__tests__/NuerobagelSearch.spec.tsx +17 -0
  171. package/src/scripts/components/search-page/filters-block.scss +99 -0
  172. package/src/scripts/components/search-page/neurobagel_logo.svg +75 -0
  173. package/src/scripts/components/search-page/search-page.scss +505 -0
  174. package/src/scripts/components/search-page/search-result.scss +117 -0
  175. package/src/scripts/components/search-page/search-sort.scss +56 -0
  176. package/src/scripts/components/select/SelectGroup.tsx +41 -0
  177. package/src/scripts/components/select/__tests__/SelectGroup.spec.tsx +43 -0
  178. package/src/scripts/components/select/select.scss +40 -0
  179. package/src/scripts/components/textarea/Textarea.tsx +68 -0
  180. package/src/scripts/components/textarea/__tests__/Textarea.spec.tsx +53 -0
  181. package/src/scripts/components/tooltip/Tooltip.tsx +30 -0
  182. package/src/scripts/components/tooltip/__tests__/Tooltip.spec.tsx +14 -0
  183. package/src/scripts/components/tooltip/tooltip.scss +159 -0
  184. package/src/scripts/components/user/UserMenu.tsx +72 -0
  185. package/src/scripts/components/user/user-menu.scss +88 -0
  186. package/src/scripts/components/warn-button/WarnButton.tsx +97 -0
  187. package/src/scripts/components/warn-button/warn-button.scss +39 -0
  188. package/src/scripts/datalad/dataset/dataset-query-context.js +2 -0
  189. package/src/scripts/dataset/comments/comment.jsx +1 -1
  190. package/src/scripts/dataset/components/AnalyzeDropdown.tsx +2 -2
  191. package/src/scripts/dataset/components/CloneDropdown.tsx +2 -2
  192. package/src/scripts/dataset/components/DatasetEventItem.tsx +105 -0
  193. package/src/scripts/dataset/components/DatasetGitAccess.tsx +2 -2
  194. package/src/scripts/dataset/components/DatasetToolButton.tsx +2 -2
  195. package/src/scripts/dataset/components/ModalitiesMetaDataBlock.tsx +2 -10
  196. package/src/scripts/dataset/components/ValidationBlock.tsx +37 -2
  197. package/src/scripts/dataset/components/VersionList.tsx +1 -1
  198. package/src/scripts/dataset/components/scss/dataset-events.module.scss +41 -0
  199. package/src/scripts/dataset/dataset-query.jsx +12 -11
  200. package/src/scripts/dataset/download/__tests__/__snapshots__/download-command-line.spec.jsx.snap +7 -1
  201. package/src/scripts/dataset/download/__tests__/download-command-line.spec.jsx +2 -2
  202. package/src/scripts/dataset/download/download-command-line.jsx +17 -13
  203. package/src/scripts/dataset/download/download-datalad.jsx +6 -3
  204. package/src/scripts/dataset/download/download-link.jsx +1 -1
  205. package/src/scripts/dataset/download/download-s3.jsx +0 -4
  206. package/src/scripts/dataset/files/file-display.jsx +1 -1
  207. package/src/scripts/dataset/files/file-tree-unloaded-directory.jsx +1 -1
  208. package/src/scripts/dataset/files/file-tree.tsx +1 -1
  209. package/src/scripts/dataset/files/file-view.jsx +1 -1
  210. package/src/scripts/dataset/files/file-viewer-type.jsx +5 -1
  211. package/src/scripts/dataset/files/file.tsx +2 -2
  212. package/src/scripts/dataset/files/files.tsx +3 -3
  213. package/src/scripts/dataset/files/viewers/file-viewer-json.jsx +1 -1
  214. package/src/scripts/dataset/files/viewers/file-viewer-markdown.tsx +13 -0
  215. package/src/scripts/dataset/fragments/cancel-button.tsx +1 -1
  216. package/src/scripts/dataset/fragments/dataset-citation.jsx +2 -2
  217. package/src/scripts/dataset/fragments/edit-button.tsx +1 -1
  218. package/src/scripts/dataset/fragments/edit-list.jsx +1 -1
  219. package/src/scripts/dataset/fragments/save-button.tsx +1 -1
  220. package/src/scripts/dataset/mutations/admin-exports.jsx +1 -1
  221. package/src/scripts/dataset/mutations/cache-clear.jsx +1 -1
  222. package/src/scripts/dataset/mutations/create-anonymous-reviewer.tsx +2 -2
  223. package/src/scripts/dataset/mutations/dataset-events.tsx +187 -0
  224. package/src/scripts/dataset/mutations/dataset-relations.tsx +1 -1
  225. package/src/scripts/dataset/mutations/delete-anonymous-reviewer.tsx +1 -1
  226. package/src/scripts/dataset/mutations/delete-comment.jsx +1 -1
  227. package/src/scripts/dataset/mutations/delete-file.jsx +1 -1
  228. package/src/scripts/dataset/mutations/deprecate-version.tsx +1 -1
  229. package/src/scripts/dataset/mutations/flag-annex-object.jsx +2 -2
  230. package/src/scripts/dataset/mutations/follow.tsx +1 -1
  231. package/src/scripts/dataset/mutations/import-dataset.tsx +1 -1
  232. package/src/scripts/dataset/mutations/publish.jsx +1 -1
  233. package/src/scripts/dataset/mutations/remove-annex-object.jsx +2 -2
  234. package/src/scripts/dataset/mutations/remove-permissions.tsx +1 -1
  235. package/src/scripts/dataset/mutations/revalidate.jsx +2 -2
  236. package/src/scripts/dataset/mutations/snapshot.tsx +1 -1
  237. package/src/scripts/dataset/mutations/star.tsx +1 -1
  238. package/src/scripts/dataset/mutations/submit-metadata.jsx +1 -1
  239. package/src/scripts/dataset/mutations/undo-deprecate-version.tsx +2 -2
  240. package/src/scripts/dataset/mutations/update-permissions.tsx +1 -1
  241. package/src/scripts/dataset/routes/admin-datalad.jsx +4 -0
  242. package/src/scripts/dataset/routes/dataset-default.tsx +1 -1
  243. package/src/scripts/dataset/routes/delete-page.tsx +7 -3
  244. package/src/scripts/dataset/routes/deprecate-snapshot-page.tsx +1 -1
  245. package/src/scripts/dataset/routes/manage-permissions.jsx +1 -1
  246. package/src/scripts/dataset/routes/snapshot-default.tsx +1 -1
  247. package/src/scripts/dataset/routes/snapshot.tsx +1 -1
  248. package/src/scripts/dataset/snapshot-container.tsx +14 -8
  249. package/src/scripts/pages/admin/flagged-files.jsx +1 -1
  250. package/src/scripts/pages/admin/user-tools.tsx +1 -1
  251. package/src/scripts/pages/admin/users.tsx +2 -2
  252. package/src/scripts/pages/api.jsx +2 -2
  253. package/src/scripts/pages/front-page/aggregate-queries/aggregate-counts-container.tsx +6 -7
  254. package/src/scripts/pages/front-page/front-page.tsx +7 -9
  255. package/src/scripts/pages/metadata/dataset-metadata.tsx +1 -1
  256. package/src/scripts/search/__tests__/search-container.spec.tsx +7 -7
  257. package/src/scripts/search/filters-block-container.tsx +66 -9
  258. package/src/scripts/search/initial-search-params.tsx +7 -7
  259. package/src/scripts/search/inputs/__tests__/sort-by-select.spec.tsx +1 -1
  260. package/src/scripts/search/inputs/admin-allDatasets-toggle.tsx +1 -1
  261. package/src/scripts/search/inputs/age-range-input.tsx +3 -2
  262. package/src/scripts/search/inputs/author-input.tsx +3 -2
  263. package/src/scripts/search/inputs/dataset-type-select.tsx +3 -2
  264. package/src/scripts/search/inputs/date-radios.tsx +3 -2
  265. package/src/scripts/search/inputs/diagnosis-select.tsx +3 -2
  266. package/src/scripts/search/inputs/initiative-select.tsx +12 -6
  267. package/src/scripts/search/inputs/keyword-input.tsx +2 -2
  268. package/src/scripts/search/inputs/modality-select.tsx +28 -26
  269. package/src/scripts/search/inputs/pet/bodyParts_input.tsx +4 -3
  270. package/src/scripts/search/inputs/pet/scannerManufacturersModelNames_input.tsx +4 -3
  271. package/src/scripts/search/inputs/pet/scannerManufacturers_input.tsx +4 -3
  272. package/src/scripts/search/inputs/pet/tracerNames_input.tsx +4 -3
  273. package/src/scripts/search/inputs/pet/tracerRadionuclides_input.tsx +4 -3
  274. package/src/scripts/search/inputs/section-select.tsx +3 -2
  275. package/src/scripts/search/inputs/sex-radios.tsx +3 -2
  276. package/src/scripts/search/inputs/show-datasets-radios.tsx +4 -3
  277. package/src/scripts/search/inputs/sort-by-select.tsx +1 -1
  278. package/src/scripts/search/inputs/species-select.tsx +3 -2
  279. package/src/scripts/search/inputs/study-domain-input.tsx +4 -3
  280. package/src/scripts/search/inputs/subject-count-range-input.tsx +3 -2
  281. package/src/scripts/search/inputs/task-input.tsx +4 -3
  282. package/src/scripts/search/search-container.tsx +14 -17
  283. package/src/scripts/search/search-params-ctx.tsx +28 -5
  284. package/src/scripts/search/search-routes.tsx +2 -4
  285. package/src/scripts/search/use-search-results.tsx +14 -13
  286. package/src/scripts/uploader/upload-issues.tsx +1 -1
  287. package/src/scripts/uploader/upload-progress.jsx +1 -1
  288. package/src/scripts/uploader/uploader-modal.jsx +1 -1
  289. package/src/scripts/users/components/close-button.tsx +1 -1
  290. package/src/scripts/users/components/edit-button.tsx +1 -1
  291. package/src/scripts/users/components/edit-list.tsx +1 -1
  292. package/src/scripts/users/components/edit-string.tsx +1 -1
  293. package/src/scripts/users/dataset-card.tsx +2 -2
  294. package/src/scripts/users/user-notification-accordion.tsx +1 -1
  295. package/src/scripts/utils/user-login-modal-ctx.tsx +1 -1
  296. package/src/scripts/validation/validation-issues.tsx +2 -1
  297. package/src/scripts/validation/validation-panel.tsx +2 -1
  298. package/src/scripts/validation/validation-results.tsx +4 -3
  299. package/src/scripts/validation-legacy/validation-panel.jsx +2 -1
  300. package/src/scripts/validation-legacy/validation-results.issues.jsx +2 -3
  301. package/src/scripts/validation-legacy/validation-results.tsx +3 -4
  302. package/tsconfig.json +1 -4
@@ -0,0 +1,417 @@
1
+ import React from "react"
2
+ import bytes from "bytes"
3
+ import parseISO from "date-fns/parseISO"
4
+ import formatDistanceToNow from "date-fns/formatDistanceToNow"
5
+ import { Link } from "react-router-dom"
6
+
7
+ import { Tooltip } from "../../components/tooltip/Tooltip"
8
+ import { Icon } from "../../components/icon/Icon"
9
+
10
+ import "./search-result.scss"
11
+ import activityPulseIcon from "../../../assets/activity-icon.png"
12
+ import { ModalityLabel } from "../../components/formatting/modality-label"
13
+
14
+ /**
15
+ * Return an equivalent to moment(date).format('L') without moment
16
+ * @param {*} dateObject
17
+ */
18
+ export const formatDate = (dateObject) =>
19
+ new Date(dateObject).toISOString().split("T")[0]
20
+
21
+ export interface SearchResultItemProps {
22
+ node: {
23
+ id: string
24
+ created: string
25
+ uploader: {
26
+ id: string
27
+ name: string
28
+ }
29
+ public: boolean
30
+ permissions: {
31
+ id: string
32
+ userPermissions: [
33
+ {
34
+ userId: string
35
+ level: string
36
+ access: string
37
+ user: {
38
+ id: string
39
+ name: string
40
+ email: string
41
+ provider: string
42
+ }
43
+ },
44
+ ]
45
+ }
46
+ metadata: {
47
+ ages: number[]
48
+ }
49
+ latestSnapshot: {
50
+ id: string
51
+ size: number
52
+ summary: {
53
+ pet: {
54
+ BodyPart: string
55
+ ScannerManufacturer: string
56
+ ScannerManufacturersModelName: string
57
+ TracerName: string[]
58
+ TracerRadionuclide: string
59
+ }
60
+ modalities: string[]
61
+ sessions: []
62
+ subjects: string[]
63
+ subjectMetadata: [
64
+ {
65
+ participantId: string
66
+ age: [{ age?: number }]
67
+ sex: string
68
+ group: null
69
+ },
70
+ ]
71
+ tasks: string[]
72
+ size: number
73
+ totalFiles: number
74
+ dataProcessed: boolean
75
+ }
76
+ issues: [
77
+ {
78
+ severity: string
79
+ },
80
+ ]
81
+ validation: {
82
+ errors: number
83
+ warnings: number
84
+ }
85
+ description: {
86
+ Name: string
87
+ }
88
+ }
89
+ analytics: {
90
+ views: number
91
+ downloads: number
92
+ }
93
+ stars: [
94
+ {
95
+ userId: string
96
+ datasetId: string
97
+ },
98
+ ]
99
+ followers: [
100
+ {
101
+ userId: string
102
+ datasetId: string
103
+ },
104
+ ]
105
+ snapshots: [
106
+ {
107
+ id: string
108
+ created: string
109
+ tag: string
110
+ },
111
+ ]
112
+ }
113
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
114
+ profile: Record<string, any> // TODO - Use the actual user type here
115
+ datasetTypeSelected?: string
116
+ hasEditPermissions: (permissions: object, userId: string) => boolean
117
+ }
118
+
119
+ export const SearchResultItem = ({
120
+ node,
121
+ profile,
122
+ datasetTypeSelected,
123
+ hasEditPermissions,
124
+ }: SearchResultItemProps) => {
125
+ const isAdmin = profile?.admin
126
+ const hasEdit = hasEditPermissions(node.permissions, profile?.sub) || isAdmin
127
+
128
+ const heading = node.latestSnapshot.description?.Name
129
+ const summary = node.latestSnapshot?.summary
130
+ const datasetId = node.id
131
+ const numSessions = summary?.sessions.length > 0 ? summary.sessions.length : 1
132
+ const numSubjects = summary?.subjects.length > 0 ? summary.subjects.length : 1
133
+ const accessionNumber = (
134
+ <span className="result-summary-meta">
135
+ <strong>Openneuro Accession Number:</strong>
136
+ <span>{node.id}</span>
137
+ </span>
138
+ )
139
+ const sessions = (
140
+ <span className="result-summary-meta">
141
+ <strong>Sessions:</strong>
142
+ <span>{numSessions.toLocaleString()}</span>
143
+ </span>
144
+ )
145
+
146
+ const ages = (value) => {
147
+ if (value) {
148
+ const ages = value.filter((x) => x)
149
+ if (ages.length === 0) return "N/A"
150
+ else if (ages.length === 1) return ages[0]
151
+ else return `${Math.min(...ages)} - ${Math.max(...ages)}`
152
+ } else return "N/A"
153
+ }
154
+
155
+ const agesRange = (
156
+ <span className="result-summary-meta">
157
+ <strong>
158
+ {node?.metadata?.ages?.length === 1
159
+ ? "Participant's Age"
160
+ : "Participants' Ages"}
161
+ :{" "}
162
+ </strong>
163
+ <span>
164
+ {ages(summary?.subjectMetadata?.map((subject) => subject.age))}
165
+ </span>
166
+ </span>
167
+ )
168
+ const subjects = (
169
+ <span className="result-summary-meta">
170
+ <strong>Participants:</strong>
171
+ <span>{numSubjects.toLocaleString()}</span>
172
+ </span>
173
+ )
174
+ const size = (
175
+ <span className="result-summary-meta">
176
+ <strong>Size:</strong>
177
+ <span>{bytes(node?.latestSnapshot?.size) || "unknown"}</span>
178
+ </span>
179
+ )
180
+ const files = (
181
+ <span className="result-summary-meta">
182
+ <strong>Files:</strong>
183
+ <span>{summary?.totalFiles.toLocaleString()}</span>
184
+ </span>
185
+ )
186
+
187
+ const dateAdded = formatDate(node.created)
188
+ const dateAddedDifference = formatDistanceToNow(parseISO(node.created))
189
+ let lastUpdatedDate
190
+ if (node.snapshots.length) {
191
+ const dateUpdated = formatDate(
192
+ node.snapshots[node.snapshots.length - 1].created,
193
+ )
194
+ const dateUpdatedDifference = formatDistanceToNow(
195
+ parseISO(node.snapshots[node.snapshots.length - 1].created),
196
+ )
197
+
198
+ lastUpdatedDate = (
199
+ <>
200
+ <span className="updated-divider">|</span>
201
+ <div className="updated-date">
202
+ <span>Updated:</span>
203
+ {dateUpdated} - {dateUpdatedDifference} ago
204
+ </div>
205
+ </>
206
+ )
207
+ }
208
+
209
+ const uploader = (
210
+ <div className="uploader">
211
+ <span>Uploaded by:</span>
212
+ {node.uploader.name} on {dateAdded} - {dateAddedDifference} ago
213
+ </div>
214
+ )
215
+ const downloads = node.analytics.downloads
216
+ ? node.analytics.downloads.toLocaleString() + " Downloads \n"
217
+ : ""
218
+ const views = node.analytics.views
219
+ ? node.analytics.views.toLocaleString() + " Views \n"
220
+ : ""
221
+ const following = node.followers.length
222
+ ? node.followers.length.toLocaleString() + " Follower \n"
223
+ : ""
224
+ const stars = node.stars.length
225
+ ? node.stars.length.toLocaleString() + " Bookmarked"
226
+ : ""
227
+
228
+ const activtyTooltip = downloads + views + following + stars
229
+
230
+ const activityIcon = (
231
+ <Tooltip
232
+ tooltip={activtyTooltip}
233
+ flow="up"
234
+ className="result-icon result-activity-icon"
235
+ >
236
+ <Icon
237
+ imgSrc={activityPulseIcon}
238
+ iconSize="22px"
239
+ label="activity"
240
+ iconOnly={true}
241
+ />
242
+ </Tooltip>
243
+ )
244
+
245
+ const sharedWithIcon = (
246
+ <Tooltip
247
+ tooltip="Shared with me"
248
+ flow="up"
249
+ className="result-icon result-shared-icon"
250
+ >
251
+ <Icon
252
+ icon="fas fa-user"
253
+ color="rgb(119,191,217)"
254
+ iconSize="16px"
255
+ label="Shared With"
256
+ iconOnly={true}
257
+ />
258
+ </Tooltip>
259
+ )
260
+ const publicIcon = (
261
+ <Tooltip
262
+ tooltip="Visible to all viewers"
263
+ flow="up"
264
+ className="result-icon result-publlic-icon"
265
+ >
266
+ <Icon
267
+ icon="fas fa-globe"
268
+ color="rgb(116,181,105)"
269
+ iconSize="16px"
270
+ label="Public"
271
+ iconOnly={true}
272
+ />
273
+ </Tooltip>
274
+ )
275
+
276
+ const errorsIcon = hasEdit && (
277
+ <Tooltip
278
+ tooltip="Invalid"
279
+ flow="up"
280
+ className="result-icon result-errors-icon"
281
+ >
282
+ <Icon
283
+ icon="fas fa-exclamation-circle"
284
+ color="rgb(202,97,86)"
285
+ iconSize="16px"
286
+ label="Invalid"
287
+ iconOnly={true}
288
+ />
289
+ </Tooltip>
290
+ )
291
+
292
+ const _list = (type, items) => {
293
+ if (items && items.length > 0) {
294
+ return (
295
+ <>
296
+ <strong>{type}:</strong>
297
+ <div>
298
+ {items.map((item, index) => (
299
+ <span className="list-item" key={index}>
300
+ {item}
301
+ </span>
302
+ ))}
303
+ </div>
304
+ </>
305
+ )
306
+ } else {
307
+ return null
308
+ }
309
+ }
310
+
311
+ let invalid = false
312
+ // Legacy issues still flagged
313
+ if (node.latestSnapshot.issues) {
314
+ invalid = node.latestSnapshot.issues.some((issue) =>
315
+ issue.severity === "error"
316
+ )
317
+ } else {
318
+ // Test if there's any schema validator errors
319
+ invalid = node.latestSnapshot.validation?.errors > 0
320
+ }
321
+ const shared = !node.public && node.uploader.id !== profile.sub
322
+
323
+ const MyDatasetsPage = datasetTypeSelected === "My Datasets"
324
+ const datasetPerms = node.permissions.userPermissions.map((item) => {
325
+ if (item.user.id === profile?.sub && item.access !== null) {
326
+ if (item.access === "ro") {
327
+ return "Read Only"
328
+ } else if (item.access === "rw") {
329
+ return "Edit"
330
+ } else {
331
+ return "Admin"
332
+ }
333
+ } else {
334
+ return null
335
+ }
336
+ })
337
+
338
+ const datasetOwenerIcons = (
339
+ <div className="owner-icon-wrap">
340
+ {node.public ? publicIcon : null}
341
+ {shared ? sharedWithIcon : null}
342
+ {invalid ? errorsIcon : null}
343
+ </div>
344
+ )
345
+
346
+ const modalityList = summary?.modalities.length
347
+ ? (
348
+ <div className="modality-list">
349
+ {_list(
350
+ <>{summary?.modalities.length === 1 ? "Modality" : "Modalities"}</>,
351
+ summary?.modalities.map((modality) => (
352
+ <ModalityLabel key={modality} modality={modality} />
353
+ )),
354
+ )}
355
+ </div>
356
+ )
357
+ : null
358
+ const taskList = summary?.tasks.length
359
+ ? <div className="task-list">{_list(<>Tasks</>, summary?.tasks)}</div>
360
+ : null
361
+
362
+ const tracers = summary?.pet?.TracerName?.length
363
+ ? (
364
+ <div className="tracers-list">
365
+ {_list(
366
+ <>
367
+ {summary?.pet?.TracerName.length === 1
368
+ ? "Radiotracer"
369
+ : "Radiotracers"}
370
+ </>,
371
+ summary?.pet?.TracerName,
372
+ )}
373
+ </div>
374
+ )
375
+ : null
376
+
377
+ return (
378
+ <>
379
+ <div className="grid grid-nogutter search-result">
380
+ <div className="col col-9">
381
+ <h3>
382
+ <Link to={"/datasets/" + datasetId}>{heading}</Link>
383
+ </h3>
384
+ <div className="result-upload-info">
385
+ {uploader}
386
+ {lastUpdatedDate}
387
+ </div>
388
+ </div>
389
+
390
+ <div className="col col-3 col-sm">
391
+ {MyDatasetsPage && (
392
+ <div className="dataset-permissions-tag">
393
+ <small>Access: {datasetPerms}</small>
394
+ </div>
395
+ )}
396
+ <div className="result-icon-wrap">
397
+ {datasetOwenerIcons}
398
+ {activityIcon}
399
+ </div>
400
+ </div>
401
+ <div className="col col-12 result-meta-body">
402
+ {modalityList}
403
+ {taskList}
404
+ {tracers}
405
+ </div>
406
+ <div className="result-meta-footer">
407
+ {accessionNumber}
408
+ {sessions}
409
+ {subjects}
410
+ {agesRange}
411
+ {size}
412
+ {files}
413
+ </div>
414
+ </div>
415
+ </>
416
+ )
417
+ }
@@ -0,0 +1,48 @@
1
+ import React from "react"
2
+
3
+ import { SearchResultItem } from "./SearchResultItem"
4
+
5
+ import "./search-page.scss"
6
+
7
+ // TODO - unify this type with the one in the app package
8
+ export interface OpenNeuroTokenProfile {
9
+ sub: string
10
+ email: string
11
+ provider: string
12
+ name: string
13
+ admin: boolean
14
+ iat: number
15
+ exp: number
16
+ }
17
+
18
+ export interface SearchResultsListProps {
19
+ items
20
+ profile?: OpenNeuroTokenProfile
21
+ datasetTypeSelected: string
22
+ hasEditPermissions: (permissions: object, userId: string) => boolean
23
+ }
24
+
25
+ export const SearchResultsList = ({
26
+ items,
27
+ profile,
28
+ datasetTypeSelected,
29
+ hasEditPermissions,
30
+ }: SearchResultsListProps) => {
31
+ return (
32
+ <div className="search-results">
33
+ {items.map((data) => {
34
+ if (data) {
35
+ return (
36
+ <SearchResultItem
37
+ node={data.node}
38
+ key={data.node.id}
39
+ profile={profile}
40
+ hasEditPermissions={hasEditPermissions}
41
+ datasetTypeSelected={datasetTypeSelected}
42
+ />
43
+ )
44
+ }
45
+ })}
46
+ </div>
47
+ )
48
+ }
@@ -0,0 +1,52 @@
1
+ import React from "react"
2
+ import { Dropdown } from "../dropdown/Dropdown"
3
+ import "./search-sort.scss"
4
+
5
+ export interface SearchSortProps {
6
+ items: {
7
+ label: string
8
+ value: string
9
+ }[]
10
+
11
+ selected: {
12
+ label: string
13
+ value: string
14
+ }
15
+ setSelected: (selected: { label: string; value: string }) => void
16
+ }
17
+
18
+ export const SearchSort = ({
19
+ items,
20
+ selected,
21
+ setSelected,
22
+ }: SearchSortProps) => {
23
+ return (
24
+ <div className="col search-sort">
25
+ <Dropdown
26
+ label={
27
+ <div className="search-sort-list-label">
28
+ <b>SORT BY:</b> {selected.label}
29
+ <i className="fas fa-exchange-alt fa-rotate-90" />
30
+ </div>
31
+ }
32
+ children={
33
+ <div className="search-sort-dropdown-list">
34
+ <ul>
35
+ {items.map((item, index) => (
36
+ <li
37
+ key={index}
38
+ onClick={() => setSelected(item)}
39
+ >
40
+ {selected.value === item.value && (
41
+ <i className="fas fa-check" />
42
+ )}
43
+ <span className="label">{item.label}</span>
44
+ </li>
45
+ ))}
46
+ </ul>
47
+ </div>
48
+ }
49
+ />
50
+ </div>
51
+ )
52
+ }
@@ -0,0 +1,9 @@
1
+ import React from "react"
2
+ import { SearchSort } from "./SearchSort"
3
+
4
+ export const SearchSortContainerExample = ({ items }) => {
5
+ const [selected, setSelected] = React.useState(items[0])
6
+ return (
7
+ <SearchSort items={items} selected={selected} setSelected={setSelected} />
8
+ )
9
+ }
@@ -0,0 +1,41 @@
1
+ import React from "react"
2
+
3
+ type Item = {
4
+ param: string
5
+ values: string[]
6
+ }
7
+
8
+ export interface TermListItemProps {
9
+ item: Item
10
+ type?: string
11
+ removeFilterItem?(param, value): void
12
+ }
13
+ export const TermListItem = ({
14
+ item,
15
+ type,
16
+ removeFilterItem = () => {},
17
+ }: TermListItemProps) => {
18
+ if (item.values.length === 0) {
19
+ return null
20
+ } else {
21
+ return (
22
+ <>
23
+ <li className="search-term-list">
24
+ <strong className="terms-heading">{type}:</strong>
25
+ <ul>
26
+ {item.values.map((term, index) => (
27
+ <li key={index}>
28
+ <span>
29
+ {term}
30
+ <span onClick={() => removeFilterItem(item.param, term)}>
31
+ &times;
32
+ </span>
33
+ </span>
34
+ </li>
35
+ ))}
36
+ </ul>
37
+ </li>
38
+ </>
39
+ )
40
+ }
41
+ }
@@ -0,0 +1,17 @@
1
+ import React from "react"
2
+ import { fireEvent, render, screen } from "@testing-library/react"
3
+ import { NeurobagelSearch } from "../NeurobagelSearch"
4
+
5
+ describe("NeurobagelSearch component", () => {
6
+ it("? toggle can be clicked", async () => {
7
+ render(<NeurobagelSearch />)
8
+ await fireEvent.click(await screen.getByRole("switch"))
9
+ expect(await screen.findByRole("switch")).toHaveClass("open")
10
+ expect(
11
+ screen.getByText(
12
+ "Neurobagel is a collection of tools for harmonizing phenotypic and imaging data descriptions",
13
+ { exact: false },
14
+ ),
15
+ ).toBeVisible()
16
+ })
17
+ })
@@ -0,0 +1,99 @@
1
+ @import '../scss/variables';
2
+
3
+ .filters-block {
4
+ position: relative;
5
+ border-bottom: 1px solid $newspaper;
6
+ margin-bottom: 10px;
7
+ h2 {
8
+ margin: 0 0 20px;
9
+ display: flex;
10
+ align-items: flex-end;
11
+ line-height: 1;
12
+ font-weight: 400;
13
+ font-size: 18px;
14
+ @media (max-width: 450px) {
15
+ flex-wrap: wrap;
16
+ }
17
+ span {
18
+ color: #000;
19
+ font-weight: 700;
20
+ margin: 0 5px;
21
+ }
22
+ .on-button {
23
+ display: block;
24
+ width: 65px;
25
+ margin-left: auto;
26
+ text-transform: uppercase;
27
+ border: 0;
28
+ border-radius: 0;
29
+ padding: 0;
30
+ background: none;
31
+ &:hover {
32
+ color: #000;
33
+ }
34
+ @media (max-width: 450px) {
35
+ min-width: 100%;
36
+ margin-top: 20px;
37
+ text-align: left;
38
+ }
39
+ }
40
+ }
41
+ .active-filters {
42
+ padding: 0;
43
+ margin: 0;
44
+ list-style: none;
45
+ display: flex;
46
+ flex-wrap: wrap;
47
+ align-items: center;
48
+ li {
49
+ margin-right: 30px;
50
+ margin-bottom: 15px;
51
+ font-size: 13px;
52
+ line-height: 1.7em;
53
+ &:last-child {
54
+ margin-right: 0;
55
+ }
56
+ strong {
57
+ margin-right: 10px;
58
+ text-transform: uppercase;
59
+ color: #666;
60
+ }
61
+ > span {
62
+ display: flex;
63
+ padding: 6px 10px;
64
+ color: #fff;
65
+ border-radius: $border-radius-default;
66
+ background-color: $chiclet-color;
67
+ transition: background-color 0.3s;
68
+ justify-content: center;
69
+ cursor: pointer;
70
+ &:hover {
71
+ background-color: darken($chiclet-color, 10%);
72
+ }
73
+ span {
74
+ color: #fff;
75
+ font-size: 22px;
76
+ margin-left: 8px;
77
+ margin-top: -1px;
78
+ }
79
+ }
80
+ &.search-term-list {
81
+ margin-bottom: 0;
82
+ margin-right: 30px;
83
+ ul li {
84
+ margin-right: 10px;
85
+ }
86
+ }
87
+ ul {
88
+ padding: 0;
89
+ margin: 0;
90
+ list-style: none;
91
+ display: flex;
92
+ flex-wrap: wrap;
93
+ align-items: center;
94
+ li {
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }