-
-
-
- {modality
- ? (
- modality.substr(0, 4)
- )
- : }
-
-
+
{renderEditor?.() || pageHeading}
diff --git a/packages/openneuro-app/src/scripts/scss/dataset/dataset-page.scss b/packages/openneuro-app/src/scripts/scss/dataset/dataset-page.scss
index bb2e30d876..01b5a18042 100644
--- a/packages/openneuro-app/src/scripts/scss/dataset/dataset-page.scss
+++ b/packages/openneuro-app/src/scripts/scss/dataset/dataset-page.scss
@@ -1,8 +1,8 @@
@import '../variables.scss';
.dataset {
.dataset-header {
- .ds-header-inner{
- display: flex;
+ .ds-header-inner {
+ display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-between;
@@ -11,45 +11,47 @@
justify-content: flex-start;
align-items: center;
}
- .ds-inner-left{
+ .ds-inner-left {
max-width: 70%;
@media screen and (max-width: 767px) {
max-width: 100%;
}
+ h1 a {
+ margin-right: 10px;
+ }
}
- .ds-inner-right{
+ .ds-inner-right {
max-width: 30%;
@media screen and (max-width: 767px) {
max-width: 100%;
}
}
- h1 {
- display: flex;
- align-items: center;
- color: #fff;
- font-size: 24px;
- margin:10px 0 ;
- @media (max-width: 767px) {
- font-size: 20px;
- flex-wrap: wrap;
- justify-content: center;
+ h1 {
+ display: flex;
+ align-items: center;
+ color: #fff;
+ font-size: 24px;
+ margin: 10px 0;
+ @media (max-width: 767px) {
+ font-size: 20px;
+ flex-wrap: wrap;
+ justify-content: center;
+ }
}
- }
- .hexagon-wrapper {
-
- @media (max-width: 767px) {
- width: 56px;
- height: 56px;
- div.label {
- font-size: 17px;
+ .hexagon-wrapper {
+ @media (max-width: 767px) {
+ width: 56px;
+ height: 56px;
+ div.label {
+ font-size: 17px;
+ }
}
}
- }
- .dataset-tool-buttons{
- flex-basis: 100%;
+ .dataset-tool-buttons {
+ flex-basis: 100%;
+ }
}
}
- }
.dataset-header-alert {
padding: 10px 20px;
@@ -171,106 +173,6 @@
}
}
-.dataset {
- .hexagon-wrapper {
- text-align: center;
- margin: 20px 10px;
- position: relative;
- display: inline-block;
- width: 45px;
- height: 45px;
- div.label {
- position: absolute;
- top: 0;
- text-align: center;
- width: 100%;
- left: 0;
- right: 0;
- bottom: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- font-weight: 600;
- font-size: 13px;
- }
-
- .hexagon {
- height: 100%;
- width: calc(100% * 0.57735);
- display: inline-block;
- }
-
- .hexagon:before {
- position: absolute;
- top: 0;
- right: calc((100% / 2) - ((100% * 0.57735) / 2));
- background-color: inherit;
- height: inherit;
- width: inherit;
- content: '';
- transform: rotateZ(60deg);
- }
-
- .hexagon:after {
- position: absolute;
- top: 0;
- right: calc((100% / 2) - ((100% * 0.57735) / 2));
- background-color: inherit;
- height: inherit;
- width: inherit;
- content: '';
- transform: rotateZ(-60deg);
- }
- }
-
- a {
- .hexagon {
- transition: color 0.3s;
- background-color: #fff;
-
- &.mri {
- color: $mri-theme;
- }
- &.eeg {
- color: $eeg-theme;
- }
-
- &.pet {
- color: $pet-theme;
- }
- &.ieeg {
- color: $ieeg-theme;
- }
-
- &.meg {
- color: $meg-theme;
- }
- }
- &:hover {
- .hexagon {
- color: lighten($on-dark-aqua, 15%);
- &.mri {
- color: lighten($mri-theme, 10%);
- }
- &.eeg {
- color: lighten($eeg-theme, 10%);
- }
-
- &.pet {
- color: lighten($pet-theme, 10%);
- }
- &.ieeg {
- color: lighten($ieeg-theme, 10%);
- }
-
- &.meg {
- color: lighten($meg-theme, 10%);
- }
- }
- }
- }
-}
-
.sidebar .dataset-meta-block {
margin-bottom: 25px;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
diff --git a/packages/openneuro-app/src/scripts/scss/variables.scss b/packages/openneuro-app/src/scripts/scss/variables.scss
index b1b79e95cb..e8f88168b6 100644
--- a/packages/openneuro-app/src/scripts/scss/variables.scss
+++ b/packages/openneuro-app/src/scripts/scss/variables.scss
@@ -7,14 +7,11 @@ $mine-shaft: #222;
$stanford-red: #8c1515;
$coral: #eb472c;
$salmon: #ec7764;
-
$rock: #5f574f;
$stone: #928b81;
$driftwood: #b6b1a9;
$misty: #dad7cb;
-
$fuchsia: #9169a7;
-
$ocean: #00505c;
$algae: #175e54;
$sky: rgb(0, 124, 146);
@@ -23,64 +20,81 @@ $persian-green: #00b489;
$night: #012a30;
$jungle-mist: #b5cbd3;
$dodger-blue: #5890ff;
-
$yellow: #ffd820;
$honey: #fffcee;
-
$success: #24c85e;
$info: #24c8c8;
$warning: #f7ba4b;
$danger: #c82424;
-
$notification-green: #77e6a3;
-
$warning-orange-cream: #ffefdb;
$warning-orange: #df7600;
-
//end old vars
+// gray colors
$charcoal: #333;
$newspaper: #dfdfdf;
+$cloud: #F8F8F8;
/* SCSS RGB */
$on-light-aqua: rgb(55, 188, 210);
$on-dark-aqua: rgba(32, 78, 90, 1);
$on-secondary: #196583;
-$on-dark-aqua-light: #e5f4f7;
+$on-light: #8bc8d4;
+$on-ultralight: adjust-color($on-dark-aqua, $lightness: 72%);
$mri-theme: rgba(79, 51, 130, 1);
$mri-hover: adjust-color($mri-theme, $lightness: 10%);
$mri-dark: darken($mri-theme, 25%);
$mri-secondary: adjust-color($mri-theme, $lightness: 15%);
$mri-light: adjust-color($mri-theme, $lightness: 45%);
+$mri-ultralight: adjust-color($mri-theme, $lightness: 62%);
$eeg-theme: rgba(134, 31, 55, 1);
$eeg-hover: adjust-color($eeg-theme, $lightness: 10%);
$eeg-dark: darken($eeg-theme, 25%);
$eeg-secondary: adjust-color($eeg-theme, $lightness: 15%);
$eeg-light: adjust-color($eeg-theme, $lightness: 45%);
+$eeg-ultralight: adjust-color($eeg-theme, $lightness: 65%);
$pet-theme: rgba(0, 105, 192, 1);
$pet-hover: adjust-color($pet-theme, $lightness: 10%);
$pet-dark: darken($pet-theme, 25%);
$pet-secondary: adjust-color($pet-theme, $lightness: 15%);
$pet-light: adjust-color($pet-theme, $lightness: 45%);
+$pet-ultralight: adjust-color($pet-theme, $lightness: 60%);
$ieeg-theme: rgba(18, 109, 62, 1);
$ieeg-hover: adjust-color($ieeg-theme, $lightness: 10%);
$ieeg-dark: darken($ieeg-theme, 25%);
$ieeg-secondary: adjust-color($ieeg-theme, $lightness: 15%);
$ieeg-light: rgb(200, 212, 153);
+$ieeg-ultralight: adjust-color($ieeg-theme, $lightness: 72%);
$meg-theme: rgba(156, 57, 0, 1);
$meg-hover: adjust-color($meg-theme, $lightness: 10%);
$meg-dark: darken($meg-theme, 25%);
$meg-secondary: adjust-color($meg-theme, $lightness: 15%);
$meg-light: adjust-color($meg-theme, $lightness: 45%);
+$meg-ultralight: adjust-color($meg-theme, $lightness: 68%);
+
+$nih-theme: rgb(34, 133, 148);
+$nih-hover: adjust-color($nih-theme, $lightness: 10%);
+$nih-dark: rgba(32, 85, 138, 1);
+$nih-secondary: adjust-color($nih-theme, $lightness: 15%);
+$nih-light: rgba(155, 211, 221, 1);
+
+$nirs-theme: rgb(166 21 40);
+$nirs-hover: adjust-color($nirs-theme, $lightness: 10%);
+$nirs-dark: darken($nirs-theme, 25%);
+$nirs-secondary: adjust-color($nirs-theme, $lightness: 15%);
+$nirs-light: adjust-color($nirs-theme, $lightness: 45%);
+$nirs-ultralight: adjust-color($nirs-theme, $lightness: 61%);
$on-light-green: #00eeb5;
$on-light-red: #c00342;
$on-light-orange: rgb(255, 110, 43);
+$text-cite: #509145;
$primary: $on-dark-aqua;
@@ -95,7 +109,6 @@ $screen-lg: 989px;
$border-radius-default: 4px;
-
/* CSS HEX */
:root {
--mri-theme: #{$mri-theme};
@@ -103,7 +116,8 @@ $border-radius-default: 4px;
--pet-theme: #{$pet-theme};
--ieeg-theme: #{$ieeg-theme};
--meg-theme: #{$meg-theme};
-
+ --nirs-theme: #{$nirs-theme};
+ --nih-theme: #{$nih-theme};
--on-light-aqua: #{$on-light-aqua};
--on-dark-aqua: #{$on-dark-aqua};
--on-light-green: #{on-light-green};
@@ -112,12 +126,15 @@ $border-radius-default: 4px;
--current-theme-primary-hover: #{$on-light-aqua};
--current-theme-secondary: #{$on-secondary};
--current-theme-primary-dark: #{$on-dark-aqua};
- --current-theme-primary-light: #{$on-dark-aqua-light};
+ --current-theme-primary-light: #{$on-light};
+ --current-theme-primary-ultralight: #{$on-ultralight};
--current-theme-header: #333;
--current-theme-header-dark: #333;
--font-sans: #{$font-sans};
--border-radius-default: #{$border-radius-default};
--newspaper: #{$newspaper};
+ --cloud: #{$cloud};
+ --text-citation: #{$text-cite};
}
.mri-theme {
@@ -148,11 +165,18 @@ $border-radius-default: 4px;
background-color: $meg-theme;
}
+.nih-theme {
+ background: $nih-theme;
+}
+
+.nirs-theme {
+ background: $nirs-theme;
+}
+
.gray-bg {
background: #f3f3f3;
}
-
.search-page-mri,
.dataset-page-mri {
--current-theme-primary: #{$mri-theme};
@@ -162,6 +186,7 @@ $border-radius-default: 4px;
--current-theme-primary-light: #{$mri-light};
--current-theme-header: #{$mri-theme};
--current-theme-header-dark: #{$mri-dark};
+ --current-theme-primary-ultralight: #{$mri-ultralight};
}
.search-page-eeg,
@@ -173,6 +198,7 @@ $border-radius-default: 4px;
--current-theme-primary-light: #{$eeg-light};
--current-theme-header: #{$eeg-theme};
--current-theme-header-dark: #{$eeg-dark};
+ --current-theme-primary-ultralight: #{$eeg-ultralight};
}
.search-page-pet,
@@ -184,6 +210,7 @@ $border-radius-default: 4px;
--current-theme-primary-light: #{$pet-light};
--current-theme-header: #{$pet-theme};
--current-theme-header-dark: #{$pet-dark};
+ --current-theme-primary-ultralight: #{$pet-ultralight};
}
.search-page-ieeg,
@@ -195,6 +222,7 @@ $border-radius-default: 4px;
--current-theme-primary-light: #{$ieeg-light};
--current-theme-header: #{$ieeg-theme};
--current-theme-header-dark: #{$ieeg-dark};
+ --current-theme-primary-ultralight: #{$ieeg-ultralight};
}
.search-page-meg,
@@ -206,4 +234,28 @@ $border-radius-default: 4px;
--current-theme-primary-light: #{$meg-light};
--current-theme-header: #{$meg-theme};
--current-theme-header-dark: #{$meg-dark};
+ --current-theme-primary-ultralight: #{$meg-ultralight};
+}
+
+.search-page-nirs,
+.dataset-page-nirs {
+ --current-theme-primary: #{$nirs-theme};
+ --current-theme-primary-hover: #{$nirs-hover};
+ --current-theme-primary-dark: #{$nirs-dark};
+ --current-theme-secondary: #{$nirs-secondary};
+ --current-theme-primary-light: #{$nirs-light};
+ --current-theme-header: #{$nirs-theme};
+ --current-theme-header-dark: #{$nirs-dark};
+ --current-theme-primary-ultralight: #{$nirs-ultralight};
+}
+
+.search-page-nih,
+.dataset-page-nih {
+ --current-theme-primary: #{$nih-theme};
+ --current-theme-primary-hover: #{$nih-hover};
+ --current-theme-primary-dark: #{$nih-dark};
+ --current-theme-secondary: #{$nih-secondary};
+ --current-theme-primary-light: #{$nih-light};
+ --current-theme-header: #{$nih-theme};
+ --current-theme-header-dark: #{$nih-dark};
}
diff --git a/packages/openneuro-app/src/scripts/components/search-page/__tests__/NuerobagelSearch.spec.tsx b/packages/openneuro-app/src/scripts/search/__tests__/NuerobagelSearch.spec.tsx
similarity index 89%
rename from packages/openneuro-app/src/scripts/components/search-page/__tests__/NuerobagelSearch.spec.tsx
rename to packages/openneuro-app/src/scripts/search/__tests__/NuerobagelSearch.spec.tsx
index 34bc393244..484fd2d9a1 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/__tests__/NuerobagelSearch.spec.tsx
+++ b/packages/openneuro-app/src/scripts/search/__tests__/NuerobagelSearch.spec.tsx
@@ -1,6 +1,6 @@
import React from "react"
import { fireEvent, render, screen } from "@testing-library/react"
-import { NeurobagelSearch } from "../NeurobagelSearch"
+import { NeurobagelSearch } from "../components/NeurobagelSearch"
describe("NeurobagelSearch component", () => {
it("? toggle can be clicked", async () => {
diff --git a/packages/openneuro-app/src/scripts/components/search-page/CommunityHeader.tsx b/packages/openneuro-app/src/scripts/search/components/CommunityHeader.tsx
similarity index 100%
rename from packages/openneuro-app/src/scripts/components/search-page/CommunityHeader.tsx
rename to packages/openneuro-app/src/scripts/search/components/CommunityHeader.tsx
diff --git a/packages/openneuro-app/src/scripts/search/components/DatasetsRadioTabs.tsx b/packages/openneuro-app/src/scripts/search/components/DatasetsRadioTabs.tsx
new file mode 100644
index 0000000000..10041187da
--- /dev/null
+++ b/packages/openneuro-app/src/scripts/search/components/DatasetsRadioTabs.tsx
@@ -0,0 +1,103 @@
+import React, { useCallback, useContext } from "react"
+import type { FC } from "react"
+import { SearchParamsCtx } from "../search-params-ctx"
+import { useCookies } from "react-cookie"
+import { getUnexpiredProfile } from "../../authentication/profile"
+import { useUser } from "../../queries/user"
+import SlidingRadioGroup from "../inputs/sliding-radio-group"
+
+export const DatasetsRadioTabs: FC = () => {
+ const [cookies] = useCookies()
+ const loggedOut = !getUnexpiredProfile(cookies)
+ const { user } = useUser()
+ const isAdmin = user?.admin
+ const { searchParams, setSearchParams } = useContext(SearchParamsCtx)
+ const {
+ datasetType_available,
+ datasetType_selected,
+ datasetStatus_available,
+ datasetStatus_selected,
+ searchAllDatasets,
+ } = searchParams
+
+ const updatedDatasetTypeAvailable = [...datasetType_available]
+ if (isAdmin) {
+ const adminDatasetValue = "Admin: All Datasets"
+ const adminDatasetLabel = "Admin" // Define the new label
+
+ // Check if an item with the 'Admin: All Datasets' value already exists
+ const alreadyHasAdminDataset = updatedDatasetTypeAvailable.some((item) =>
+ (typeof item === "object" ? item.value : item) === adminDatasetValue
+ )
+
+ if (!alreadyHasAdminDataset) {
+ // Push it as an object with both value and the desired label
+ updatedDatasetTypeAvailable.push({
+ value: adminDatasetValue,
+ label: adminDatasetLabel,
+ })
+ }
+ }
+
+ const setShowSelected = useCallback(
+ (newDatasetTypeSelected: string) => {
+ setSearchParams((prevState) => {
+ const newSearchAllDatasets =
+ newDatasetTypeSelected === "Admin: All Datasets"
+
+ return {
+ ...prevState,
+ datasetType_selected: newDatasetTypeSelected,
+ searchAllDatasets: newSearchAllDatasets,
+
+ datasetStatus_selected: newSearchAllDatasets
+ ? undefined
+ : newDatasetTypeSelected === "My Datasets"
+ ? prevState.datasetStatus_selected
+ : undefined,
+ }
+ })
+ },
+ [setSearchParams],
+ )
+
+ const setShowMyUploadsSelected = useCallback(
+ (newDatasetStatusSelected: string) => {
+ setSearchParams((prevState) => ({
+ ...prevState,
+ datasetStatus_selected: newDatasetStatusSelected,
+ }))
+ },
+ [setSearchParams],
+ )
+
+ if (loggedOut) {
+ return null
+ }
+
+ return (
+ <>
+
+
+ {datasetType_selected === "My Datasets" && !searchAllDatasets && (
+
+ )}
+ >
+ )
+}
diff --git a/packages/openneuro-app/src/scripts/components/search-page/FacetBlockContainerExample.tsx b/packages/openneuro-app/src/scripts/search/components/FacetBlockContainerExample.tsx
similarity index 100%
rename from packages/openneuro-app/src/scripts/components/search-page/FacetBlockContainerExample.tsx
rename to packages/openneuro-app/src/scripts/search/components/FacetBlockContainerExample.tsx
diff --git a/packages/openneuro-app/src/scripts/components/search-page/FilterDateItem.tsx b/packages/openneuro-app/src/scripts/search/components/FilterDateItem.tsx
similarity index 100%
rename from packages/openneuro-app/src/scripts/components/search-page/FilterDateItem.tsx
rename to packages/openneuro-app/src/scripts/search/components/FilterDateItem.tsx
diff --git a/packages/openneuro-app/src/scripts/components/search-page/FilterListItem.tsx b/packages/openneuro-app/src/scripts/search/components/FilterListItem.tsx
similarity index 95%
rename from packages/openneuro-app/src/scripts/components/search-page/FilterListItem.tsx
rename to packages/openneuro-app/src/scripts/search/components/FilterListItem.tsx
index 9ba61f8b5f..9b212ee988 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/FilterListItem.tsx
+++ b/packages/openneuro-app/src/scripts/search/components/FilterListItem.tsx
@@ -1,5 +1,5 @@
import React from "react"
-import { ModalityLabel } from "../formatting/modality-label"
+import { ModalityLabel } from "../../components/formatting/modality-label"
type TextList = string[]
type Text = string
diff --git a/packages/openneuro-app/src/scripts/components/search-page/FiltersBlock.tsx b/packages/openneuro-app/src/scripts/search/components/FiltersBlock.tsx
similarity index 96%
rename from packages/openneuro-app/src/scripts/components/search-page/FiltersBlock.tsx
rename to packages/openneuro-app/src/scripts/search/components/FiltersBlock.tsx
index ef18a984eb..30df59fc90 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/FiltersBlock.tsx
+++ b/packages/openneuro-app/src/scripts/search/components/FiltersBlock.tsx
@@ -3,7 +3,8 @@ import { Button } from "../../components/button/Button"
import { FilterListItem } from "./FilterListItem"
import { TermListItem } from "./TermListItem"
import type { FacetSelectValueType } from "../../components/facets/FacetSelect"
-import "./filters-block.scss"
+import "../scss/filters-block.scss"
+import { modalityShortMapping } from "../../components/formatting/modality-label"
export interface FiltersBlockProps {
keywords: string[]
@@ -69,17 +70,13 @@ export const FiltersBlock = ({
const subjectCountRangeIsNull =
JSON.stringify(subjectCountRange) === JSON.stringify([null, null])
+ const labelText = modalityShortMapping(modality_selected)
+
return (
{noFilters
- ? (
-
- Showing all available {modality_selected ? modality_selected : ""}
- {" "}
- datasets
-
- )
+ ? Showing all available {labelText ? labelText : ""} datasets
: (
<>
{loading
diff --git a/packages/openneuro-app/src/scripts/search/components/MetaListItemList.tsx b/packages/openneuro-app/src/scripts/search/components/MetaListItemList.tsx
new file mode 100644
index 0000000000..318e01b7f6
--- /dev/null
+++ b/packages/openneuro-app/src/scripts/search/components/MetaListItemList.tsx
@@ -0,0 +1,31 @@
+import React from "react"
+import type { FC, ReactNode } from "react"
+
+interface MetaListItemListProps {
+ typeLabel: ReactNode
+ items: (string | ReactNode)[]
+}
+
+/**
+ * A reusable component for rendering a list of meta items in the search results details.
+ */
+export const MetaListItemList: FC = (
+ { typeLabel, items },
+) => {
+ if (!items || items.length === 0) {
+ return null
+ }
+
+ return (
+
+
+
+ {items.map((item, index) => (
+
+ {item}
+
+ ))}
+
+
+ )
+}
diff --git a/packages/openneuro-app/src/scripts/components/search-page/ModalityHeader.tsx b/packages/openneuro-app/src/scripts/search/components/ModalityHeader.tsx
similarity index 100%
rename from packages/openneuro-app/src/scripts/components/search-page/ModalityHeader.tsx
rename to packages/openneuro-app/src/scripts/search/components/ModalityHeader.tsx
diff --git a/packages/openneuro-app/src/scripts/components/search-page/NeurobagelSearch.tsx b/packages/openneuro-app/src/scripts/search/components/NeurobagelSearch.tsx
similarity index 100%
rename from packages/openneuro-app/src/scripts/components/search-page/NeurobagelSearch.tsx
rename to packages/openneuro-app/src/scripts/search/components/NeurobagelSearch.tsx
diff --git a/packages/openneuro-app/src/scripts/components/search-page/SearchPage.tsx b/packages/openneuro-app/src/scripts/search/components/SearchPage.tsx
similarity index 87%
rename from packages/openneuro-app/src/scripts/components/search-page/SearchPage.tsx
rename to packages/openneuro-app/src/scripts/search/components/SearchPage.tsx
index 5c94f60038..2ffe92f147 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/SearchPage.tsx
+++ b/packages/openneuro-app/src/scripts/search/components/SearchPage.tsx
@@ -1,7 +1,7 @@
import React from "react"
import { ModalityHeader } from "./ModalityHeader"
import { CommunityHeader } from "./CommunityHeader"
-import "./search-page.scss"
+import "../scss/search-page.scss"
export interface PortalContent {
className?: string
@@ -15,6 +15,7 @@ export interface PortalContent {
}
export interface SearchPageProps {
+ hasDetailsOpen?: boolean
portalContent?: PortalContent
renderSearchFacets: () => React.ReactNode
renderSearchResultsList: () => React.ReactNode
@@ -23,15 +24,18 @@ export interface SearchPageProps {
renderSearchHeader: () => React.ReactNode
renderLoading: () => React.ReactNode
renderAggregateCounts: () => React.ReactNode
+ renderItemDetails: () => React.ReactNode
}
export const SearchPage = ({
+ hasDetailsOpen,
portalContent,
renderSearchFacets,
renderSearchResultsList,
renderSortBy,
renderFilterBlock,
renderSearchHeader,
+ renderItemDetails,
renderLoading,
renderAggregateCounts,
}: SearchPageProps) => {
@@ -69,18 +73,14 @@ export const SearchPage = ({
>
)
: null}
-
+
-
-
{renderSearchHeader()}
-
-
{renderSearchFacets()}
-
+
+
{renderSearchHeader()}
{renderLoading()}
{renderFilterBlock()}
+
{renderSearchResultsList()}
+ {renderItemDetails()}
diff --git a/packages/openneuro-app/src/scripts/search/components/SearchResultDetails.tsx b/packages/openneuro-app/src/scripts/search/components/SearchResultDetails.tsx
new file mode 100644
index 0000000000..e4d538093e
--- /dev/null
+++ b/packages/openneuro-app/src/scripts/search/components/SearchResultDetails.tsx
@@ -0,0 +1,167 @@
+import React, { useEffect, useRef } from "react"
+import type { FC, ReactNode } from "react"
+import bytes from "bytes"
+import parseISO from "date-fns/parseISO"
+import formatDistanceToNow from "date-fns/formatDistanceToNow"
+import { Link } from "react-router-dom"
+import type { SearchResultItemProps } from "./SearchResultItem"
+import { ModalityLabel } from "../../components/formatting/modality-label"
+import { MetaListItemList } from "./MetaListItemList"
+import "../scss/search-result-details.scss"
+
+interface SearchResultDetailsProps {
+ itemData: SearchResultItemProps["node"] | null
+ onClose: () => void
+}
+
+export const SearchResultDetails: FC
= (
+ { itemData, onClose },
+) => {
+ const closeButtonRef = useRef(null)
+
+ useEffect(() => {
+ if (itemData && closeButtonRef.current) {
+ closeButtonRef.current.focus()
+ }
+ }, [itemData])
+
+ if (!itemData) {
+ return null
+ }
+
+ const formatDate = (dateString: string): string => {
+ if (!dateString) return "N/A"
+ const date = new Date(dateString)
+ return date.toISOString().split("T")[0]
+ }
+
+ const summary = itemData.latestSnapshot?.summary
+ const numSessions = summary?.sessions?.length > 0
+ ? summary.sessions.length
+ : 1
+ const numSubjects = summary?.subjects?.length > 0
+ ? summary.subjects.length
+ : 1
+
+ // Header for more details
+ const moreDetailsHeader = (
+
+
+ {itemData.latestSnapshot?.description?.Name || itemData.id}
+
+
+ )
+
+ // Lists
+ const modalityList = summary?.modalities?.length
+ ? (
+
+ {summary?.modalities.length === 1 ? "Modality" : "Modalities"}>
+ }
+ items={summary?.modalities.map((modality) => (
+
+ ))}
+ />
+
+ )
+ : null
+
+ const taskList = summary?.tasks?.length
+ ? (
+
+ Tasks>} items={summary?.tasks} />
+
+ )
+ : null
+
+ const tracers = summary?.pet?.TracerName?.length
+ ? (
+
+
+ {summary?.pet?.TracerName.length === 1
+ ? "Radiotracer"
+ : "Radiotracers"}
+ >
+ }
+ items={summary?.pet?.TracerName}
+ />
+
+ )
+ : null
+
+ // function for consistent meta item rendering
+ const renderMetaItem = (
+ label: string | ReactNode,
+ content: ReactNode,
+ ): JSX.Element => (
+
+
+ {content}
+
+ )
+
+ const sessions = renderMetaItem("Sessions", numSessions.toLocaleString())
+ const subjects = renderMetaItem("Participants", numSubjects.toLocaleString())
+ const size = renderMetaItem(
+ "Size",
+ bytes(itemData?.latestSnapshot?.size) || "unknown",
+ )
+ const files = renderMetaItem("Files", summary?.totalFiles?.toLocaleString())
+ const lastUpdatedDisplay = renderMetaItem(
+ "Last Updated",
+
+ {formatDate(
+ itemData.snapshots?.[itemData.snapshots.length - 1]?.created ||
+ itemData.created,
+ )}
+
,
+ )
+ const accessionNumberDisplay = renderMetaItem(
+ "Openneuro Accession Number",
+
+ {itemData?.id}
+ ,
+ )
+ const authors = renderMetaItem(
+ "Authors",
+ {itemData.latestSnapshot?.description?.Authors}
,
+ )
+ const uploaderDisplay = renderMetaItem(
+ "Uploader by",
+
+ {itemData.uploader?.name} on {formatDate(itemData?.created)} -{" "}
+ {formatDistanceToNow(parseISO(itemData?.created))} ago
+
,
+ )
+
+ return (
+
+
+
+ {moreDetailsHeader}
+ {authors}
+ {modalityList}
+ {taskList}
+ {accessionNumberDisplay}
+ {tracers}
+ {sessions}
+ {subjects}
+ {size}
+ {files}
+ {uploaderDisplay}
+ {lastUpdatedDisplay}
+
+
+ )
+}
diff --git a/packages/openneuro-app/src/scripts/components/search-page/SearchResultItem.tsx b/packages/openneuro-app/src/scripts/search/components/SearchResultItem.tsx
similarity index 52%
rename from packages/openneuro-app/src/scripts/components/search-page/SearchResultItem.tsx
rename to packages/openneuro-app/src/scripts/search/components/SearchResultItem.tsx
index 2dfcf8dc1e..e4de85fd00 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/SearchResultItem.tsx
+++ b/packages/openneuro-app/src/scripts/search/components/SearchResultItem.tsx
@@ -1,21 +1,17 @@
import React from "react"
-import bytes from "bytes"
+import getYear from "date-fns/getYear"
import parseISO from "date-fns/parseISO"
-import formatDistanceToNow from "date-fns/formatDistanceToNow"
import { Link } from "react-router-dom"
import { Tooltip } from "../../components/tooltip/Tooltip"
import { Icon } from "../../components/icon/Icon"
import { useCookies } from "react-cookie"
import { getProfile } from "../../authentication/profile"
import { useUser } from "../../queries/user"
-import "./search-result.scss"
+import "../scss/search-result.scss"
import activityPulseIcon from "../../../assets/activity-icon.png"
-import { ModalityLabel } from "../../components/formatting/modality-label"
import { hasEditPermissions } from "../../authentication/profile"
-/**
- * Return an equivalent to moment(date).format('L') without moment
- * @param {*} dateObject
- */
+import { ModalityHexagon } from "../../components/modality-cube/ModalityHexagon"
+
export const formatDate = (dateObject) =>
new Date(dateObject).toISOString().split("T")[0]
@@ -50,6 +46,7 @@ export interface SearchResultItemProps {
latestSnapshot: {
id: string
size: number
+ readme: string
summary: {
pet: {
BodyPart: string
@@ -58,6 +55,7 @@ export interface SearchResultItemProps {
TracerName: string[]
TracerRadionuclide: string
}
+ primaryModality: string
modalities: string[]
sessions: []
subjects: string[]
@@ -84,7 +82,9 @@ export interface SearchResultItemProps {
warnings: number
}
description: {
+ Authors: string[]
Name: string
+ DatasetDOI: string
}
}
analytics: {
@@ -112,11 +112,15 @@ export interface SearchResultItemProps {
]
}
datasetTypeSelected?: string
+ onClick: (itemId: string, event: React.MouseEvent) => void
+ isExpanded: boolean
}
export const SearchResultItem = ({
node,
datasetTypeSelected,
+ onClick,
+ isExpanded,
}: SearchResultItemProps) => {
const { user } = useUser()
const [cookies] = useCookies()
@@ -125,96 +129,10 @@ export const SearchResultItem = ({
const isAdmin = user?.admin
const hasEdit = hasEditPermissions(node.permissions, profileSub) || isAdmin
-
- const heading = node.latestSnapshot.description?.Name
- ? node.latestSnapshot.description?.Name
- : node.id
- const summary = node.latestSnapshot?.summary
const datasetId = node.id
- const numSessions = summary?.sessions.length > 0 ? summary.sessions.length : 1
- const numSubjects = summary?.subjects.length > 0 ? summary.subjects.length : 1
- const accessionNumber = (
-
- Openneuro Accession Number:
- {node.id}
-
- )
- const sessions = (
-
- Sessions:
- {numSessions.toLocaleString()}
-
- )
-
- const ages = (value) => {
- if (value) {
- const ages = value.filter((x) => x)
- if (ages.length === 0) return "N/A"
- else if (ages.length === 1) return ages[0]
- else return `${Math.min(...ages)} - ${Math.max(...ages)}`
- } else return "N/A"
- }
- const agesRange = (
-
-
- {node?.metadata?.ages?.length === 1
- ? "Participant's Age"
- : "Participants' Ages"}
- :{" "}
-
-
- {ages(summary?.subjectMetadata?.map((subject) => subject.age))}
-
-
- )
- const subjects = (
-
- Participants:
- {numSubjects.toLocaleString()}
-
- )
- const size = (
-
- Size:
- {bytes(node?.latestSnapshot?.size) || "unknown"}
-
- )
- const files = (
-
- Files:
- {summary?.totalFiles.toLocaleString()}
-
- )
+ const heading = node.latestSnapshot.description?.Name?.trim() || datasetId
- const dateAdded = formatDate(node.created)
- const dateAddedDifference = formatDistanceToNow(parseISO(node.created))
- let lastUpdatedDate
- if (node.snapshots.length) {
- const dateUpdated = formatDate(
- node.snapshots[node.snapshots.length - 1].created,
- )
- const dateUpdatedDifference = formatDistanceToNow(
- parseISO(node.snapshots[node.snapshots.length - 1].created),
- )
-
- lastUpdatedDate = (
- <>
- |
-
- Updated:
- {dateUpdated} - {dateUpdatedDifference} ago
-
- >
- )
- }
-
- const uploader = (
-
- Uploaded by:
- {node.uploader?.name} on {dateAdded} - {dateAddedDifference} ago
-
- )
const downloads = node.analytics.downloads
? node.analytics.downloads.toLocaleString() + " Downloads \n"
: ""
@@ -292,33 +210,12 @@ export const SearchResultItem = ({
)
- const _list = (type, items) => {
- if (items && items.length > 0) {
- return (
- <>
- {type}:
-
- {items.map((item, index) => (
-
- {item}
-
- ))}
-
- >
- )
- } else {
- return null
- }
- }
-
let invalid = false
- // Legacy issues still flagged
if (node.latestSnapshot.issues) {
- invalid = node.latestSnapshot.issues.some((issue) =>
- issue.severity === "error"
+ invalid = node.latestSnapshot.issues.some(
+ (issue) => issue.severity === "error",
)
} else {
- // Test if there's any schema validator errors
invalid = node.latestSnapshot.validation?.errors > 0
}
const shared = !node.public && node.uploader?.id !== profileSub
@@ -346,74 +243,61 @@ export const SearchResultItem = ({
)
- const modalityList = summary?.modalities.length
- ? (
-
- {_list(
- <>{summary?.modalities.length === 1 ? "Modality" : "Modalities"}>,
- summary?.modalities.map((modality) => (
-
- )),
- )}
-
- )
- : null
- const taskList = summary?.tasks.length
- ?
{_list(<>Tasks>, summary?.tasks)}
- : null
-
- const tracers = summary?.pet?.TracerName?.length
- ? (
-
- {_list(
- <>
- {summary?.pet?.TracerName.length === 1
- ? "Radiotracer"
- : "Radiotracers"}
- >,
- summary?.pet?.TracerName,
- )}
-
- )
- : null
+ const year = getYear(parseISO(node.created))
+ const authors = node.latestSnapshot.description?.Authors
+ ? node.latestSnapshot.description.Authors.join(" and ")
+ : "NO AUTHORS FOUND"
+ const datasetCite =
+ `${authors} (${year}). ${node.latestSnapshot.description.Name}. OpenNeuro. [Dataset] doi: ${node.latestSnapshot.description.DatasetDOI}`
+ const trimlength = 450
return (
<>
-
+
-
- {heading}
-
-
- {uploader}
- {lastUpdatedDate}
+
+
+ {heading}
+
+
+ {node.latestSnapshot?.readme
+ ? (node.latestSnapshot.readme.length > trimlength
+ ? `${node.latestSnapshot.readme.substring(0, trimlength)}...`
+ : node.latestSnapshot.readme)
+ : ""}
+
+
{datasetCite}
-
+
+
+ {datasetOwenerIcons}
+ {activityIcon}
+
+
{MyDatasetsPage && (
-
+
Access: {datasetPerms}
)}
-
- {datasetOwenerIcons}
- {activityIcon}
+
+
-
- {modalityList}
- {taskList}
- {tracers}
-
-
- {accessionNumber}
- {sessions}
- {subjects}
- {agesRange}
- {size}
- {files}
-
>
)
diff --git a/packages/openneuro-app/src/scripts/search/components/SearchResultsList.tsx b/packages/openneuro-app/src/scripts/search/components/SearchResultsList.tsx
new file mode 100644
index 0000000000..8b41925e2a
--- /dev/null
+++ b/packages/openneuro-app/src/scripts/search/components/SearchResultsList.tsx
@@ -0,0 +1,45 @@
+import React from "react"
+import { SearchResultItem } from "./SearchResultItem"
+import "../scss/search-page.scss"
+import type { SearchResultItemProps } from "./SearchResultItem"
+
+export interface SearchResultsListProps {
+ items: { node: SearchResultItemProps["node"] }[]
+ datasetTypeSelected: string
+ clickedItemData: SearchResultItemProps["node"] | null
+ handleItemClick: (
+ itemId: string,
+ event: React.MouseEvent
,
+ ) => void
+}
+
+export const SearchResultsList = ({
+ items,
+ datasetTypeSelected,
+ clickedItemData,
+ handleItemClick,
+}: SearchResultsListProps) => {
+ return (
+ <>
+
+ {items.map((data) => {
+ if (data) {
+ const isActive = clickedItemData?.id === data.node.id
+ return (
+
+ )
+ }
+ return null
+ })}
+
+ >
+ )
+}
diff --git a/packages/openneuro-app/src/scripts/components/search-page/SearchSort.tsx b/packages/openneuro-app/src/scripts/search/components/SearchSort.tsx
similarity index 92%
rename from packages/openneuro-app/src/scripts/components/search-page/SearchSort.tsx
rename to packages/openneuro-app/src/scripts/search/components/SearchSort.tsx
index f72a259f7e..79d1054385 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/SearchSort.tsx
+++ b/packages/openneuro-app/src/scripts/search/components/SearchSort.tsx
@@ -1,6 +1,6 @@
import React from "react"
-import { Dropdown } from "../dropdown/Dropdown"
-import "./search-sort.scss"
+import { Dropdown } from "../../components/dropdown/Dropdown"
+import "../scss/search-sort.scss"
export interface SearchSortProps {
items: {
diff --git a/packages/openneuro-app/src/scripts/components/search-page/SearchSortContainerExample.tsx b/packages/openneuro-app/src/scripts/search/components/SearchSortContainerExample.tsx
similarity index 100%
rename from packages/openneuro-app/src/scripts/components/search-page/SearchSortContainerExample.tsx
rename to packages/openneuro-app/src/scripts/search/components/SearchSortContainerExample.tsx
diff --git a/packages/openneuro-app/src/scripts/components/search-page/TermListItem.tsx b/packages/openneuro-app/src/scripts/search/components/TermListItem.tsx
similarity index 100%
rename from packages/openneuro-app/src/scripts/components/search-page/TermListItem.tsx
rename to packages/openneuro-app/src/scripts/search/components/TermListItem.tsx
diff --git a/packages/openneuro-app/src/scripts/components/search-page/neurobagel_logo.svg b/packages/openneuro-app/src/scripts/search/components/neurobagel_logo.svg
similarity index 100%
rename from packages/openneuro-app/src/scripts/components/search-page/neurobagel_logo.svg
rename to packages/openneuro-app/src/scripts/search/components/neurobagel_logo.svg
diff --git a/packages/openneuro-app/src/scripts/search/filters-block-container.tsx b/packages/openneuro-app/src/scripts/search/filters-block-container.tsx
index 32aed935d6..8496dc7dd7 100644
--- a/packages/openneuro-app/src/scripts/search/filters-block-container.tsx
+++ b/packages/openneuro-app/src/scripts/search/filters-block-container.tsx
@@ -13,7 +13,7 @@ import {
SearchParamsCtx,
useCheckIfParamsAreSelected,
} from "./search-params-ctx"
-import { FiltersBlock } from "../components/search-page/FiltersBlock"
+import { FiltersBlock } from "./components/FiltersBlock"
import initialSearchParams from "./initial-search-params"
interface FiltersBlockContainerProps {
diff --git a/packages/openneuro-app/src/scripts/search/inputs/admin-allDatasets-toggle.tsx b/packages/openneuro-app/src/scripts/search/inputs/admin-allDatasets-toggle.tsx
deleted file mode 100644
index 5e55c08925..0000000000
--- a/packages/openneuro-app/src/scripts/search/inputs/admin-allDatasets-toggle.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import React, { useContext } from "react"
-import type { FC } from "react"
-import { SearchParamsCtx } from "../search-params-ctx"
-import { Button } from "../../components/button/Button"
-import styled from "@emotion/styled"
-
-const StyledButton = styled(Button)`
- width: auto;
- padding: 6px 10px;
- margin-bottom: 20px;
- justify-content: flex-start;
- &.active {
- background: #fff5f3;
- border-color: #e68383;
- color: #710000;
- i {
- color: #710000;
- }
- }
-`
-
-const TaskInput: FC = () => {
- const {
- searchParams: { searchAllDatasets },
- setSearchParams,
- } = useContext(SearchParamsCtx)
-
- const toggleSearchAllDatasets = () =>
- setSearchParams((prevState) => ({
- ...prevState,
- searchAllDatasets: !prevState.searchAllDatasets,
- }))
-
- return (
-
-
- )
-}
-
-export default TaskInput
diff --git a/packages/openneuro-app/src/scripts/search/inputs/index.ts b/packages/openneuro-app/src/scripts/search/inputs/index.ts
index 645b4434f7..4cc2fcbb28 100644
--- a/packages/openneuro-app/src/scripts/search/inputs/index.ts
+++ b/packages/openneuro-app/src/scripts/search/inputs/index.ts
@@ -1,8 +1,6 @@
import KeywordInput from "./keyword-input"
-import AllDatasetsToggle from "./admin-allDatasets-toggle"
import ModalitySelect from "./modality-select"
import InitiativeSelect from "./initiative-select"
-import ShowDatasetRadios from "./show-datasets-radios"
import AgeRangeInput from "./age-range-input"
import SubjectCountRangeInput from "./subject-count-range-input"
import DatasetTypeSelect from "./dataset-type-select"
@@ -23,7 +21,6 @@ import SortBySelect from "./sort-by-select"
export {
AgeRangeInput,
- AllDatasetsToggle,
AuthorInput,
BodyPartsInput,
DatasetTypeSelect,
@@ -36,7 +33,6 @@ export {
ScannerManufacturersModelNames,
SectionSelect,
SexRadios,
- ShowDatasetRadios,
SortBySelect,
SpeciesSelect,
StudyDomainInput,
diff --git a/packages/openneuro-app/src/scripts/search/inputs/show-datasets-radios.tsx b/packages/openneuro-app/src/scripts/search/inputs/show-datasets-radios.tsx
deleted file mode 100644
index 0eb9ba6a69..0000000000
--- a/packages/openneuro-app/src/scripts/search/inputs/show-datasets-radios.tsx
+++ /dev/null
@@ -1,74 +0,0 @@
-import React, { useContext } from "react"
-import type { FC } from "react"
-import { SearchParamsCtx } from "../search-params-ctx"
-import { RadioGroup } from "../../components/radio/RadioGroup"
-import { FacetSelect } from "../../components/facets/FacetSelect"
-import { useCookies } from "react-cookie"
-import { getUnexpiredProfile } from "../../authentication/profile"
-import { AccordionTab } from "../../components/accordion/AccordionTab"
-import { AccordionWrap } from "../../components/accordion/AccordionWrap"
-
-const ShowDatasetsRadios: FC = () => {
- const [cookies] = useCookies()
- const loggedOut = !getUnexpiredProfile(cookies)
-
- const { searchParams, setSearchParams } = useContext(SearchParamsCtx)
-
- const {
- datasetType_available,
- datasetType_selected,
- datasetStatus_available,
- datasetStatus_selected,
- } = searchParams
-
- const setShowSelected = (datasetType_selected) => {
- setSearchParams((prevState) => ({
- ...prevState,
- datasetType_selected,
- datasetStatus_selected: datasetType_selected === "My Datasets"
- ? prevState.datasetStatus_selected
- : undefined,
- }))
- }
-
- const setShowMyUploadsSelected = (datasetStatus_selected) => {
- setSearchParams((prevState) => ({
- ...prevState,
- datasetStatus_selected,
- }))
- }
-
- return loggedOut ? null : (
- <>
-
-
-
- {datasetType_selected === "My Datasets" && (
-
-
-
-
-
- )}
- >
- )
-}
-
-export default ShowDatasetsRadios
diff --git a/packages/openneuro-app/src/scripts/search/inputs/sliding-radio-group.tsx b/packages/openneuro-app/src/scripts/search/inputs/sliding-radio-group.tsx
new file mode 100644
index 0000000000..8dc944960e
--- /dev/null
+++ b/packages/openneuro-app/src/scripts/search/inputs/sliding-radio-group.tsx
@@ -0,0 +1,127 @@
+import React, { useCallback, useEffect, useRef, useState } from "react"
+import type { FC } from "react"
+import "../scss/sliding-radio-group.scss"
+
+interface RadioItem {
+ value: string
+ label: string
+}
+
+interface SlidingRadioGroupProps {
+ items: (string | RadioItem)[]
+ selected: string | undefined
+ setSelected: (value: string) => void
+ groupName: string
+ className?: string
+ initialSelectedValueOverride?: string
+}
+
+const SlidingRadioGroup: FC = ({
+ items,
+ selected,
+ setSelected,
+ groupName,
+ className = "",
+ initialSelectedValueOverride,
+}) => {
+ const containerRef = useRef(null)
+ const radioLabelRefs = useRef<{ [key: string]: HTMLLabelElement }>({})
+ const [sliderStyles, setSliderStyles] = useState({
+ left: 0,
+ width: 0,
+ height: 0,
+ top: 0,
+ })
+
+ const calculateSliderPosition = useCallback((targetValue: string) => {
+ if (!containerRef.current || !radioLabelRefs.current[targetValue]) {
+ return { left: 0, width: 0, height: 0, top: 0 }
+ }
+
+ const targetLabel = radioLabelRefs.current[targetValue]
+ const containerRect = containerRef.current.getBoundingClientRect()
+ const labelRect = targetLabel.getBoundingClientRect()
+
+ return {
+ left: labelRect.left - containerRect.left,
+ width: labelRect.width,
+ height: labelRect.height,
+ top: labelRect.top - containerRect.top,
+ }
+ }, [])
+
+ useEffect(() => {
+ const updateSlider = () => {
+ const valueToCalculate = initialSelectedValueOverride || selected
+
+ if (valueToCalculate) {
+ const newStyles = calculateSliderPosition(valueToCalculate)
+ setSliderStyles(newStyles)
+ } else {
+ // If nothing is selected, hide the slider
+ setSliderStyles({ left: 0, width: 0, height: 0, top: 0 })
+ }
+ }
+
+ const timeoutId = setTimeout(updateSlider, 50)
+ window.addEventListener("resize", updateSlider)
+
+ return () => {
+ clearTimeout(timeoutId)
+ window.removeEventListener("resize", updateSlider)
+ }
+ }, [selected, calculateSliderPosition, initialSelectedValueOverride])
+
+ const isSliderVisible = sliderStyles.width > 0 && sliderStyles.height > 0
+
+ return (
+
+ {/* The sliding highlight element */}
+
+
+
+ {items.map((item) => {
+ const value = typeof item === "object" ? item.value : item
+ const label = typeof item === "object" ? item.label : item
+ const isChecked = selected === value
+
+ return (
+
+ setSelected(value)}
+ />
+
+
+ )
+ })}
+
+
+ )
+}
+
+export default SlidingRadioGroup
diff --git a/packages/openneuro-app/src/scripts/search/inputs/sort-by-select.tsx b/packages/openneuro-app/src/scripts/search/inputs/sort-by-select.tsx
index 0c84bab378..7473f7cd6c 100644
--- a/packages/openneuro-app/src/scripts/search/inputs/sort-by-select.tsx
+++ b/packages/openneuro-app/src/scripts/search/inputs/sort-by-select.tsx
@@ -1,7 +1,7 @@
import React, { useContext } from "react"
import type { FC } from "react"
import { SearchParamsCtx } from "../search-params-ctx"
-import { SearchSort } from "../../components/search-page/SearchSort"
+import { SearchSort } from "../components/SearchSort"
interface SortBySelectProps {
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
diff --git a/packages/openneuro-app/src/scripts/components/search-page/filters-block.scss b/packages/openneuro-app/src/scripts/search/scss/filters-block.scss
similarity index 98%
rename from packages/openneuro-app/src/scripts/components/search-page/filters-block.scss
rename to packages/openneuro-app/src/scripts/search/scss/filters-block.scss
index 8a5ec1eb7f..70e79c8439 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/filters-block.scss
+++ b/packages/openneuro-app/src/scripts/search/scss/filters-block.scss
@@ -1,4 +1,4 @@
-@import '../scss/variables';
+@import '../../scss/variables';
.filters-block {
position: relative;
diff --git a/packages/openneuro-app/src/scripts/components/search-page/search-page.scss b/packages/openneuro-app/src/scripts/search/scss/search-page.scss
similarity index 74%
rename from packages/openneuro-app/src/scripts/components/search-page/search-page.scss
rename to packages/openneuro-app/src/scripts/search/scss/search-page.scss
index c75ee308ca..5a551f469d 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/search-page.scss
+++ b/packages/openneuro-app/src/scripts/search/scss/search-page.scss
@@ -1,9 +1,29 @@
-@import '../scss/variables';
+@import '../../scss/variables';
+
+// Common styles for portal headers (can be extended)
+.search-page-portal-header,
+.search-page-coms {
+ .primary-content {
+ font-weight: 300;
+ }
+ .secondary-content {
+ font-weight: 300;
+ margin-top: 25px;
+ display: flex;
+ }
+}
.search-page-portal-header {
position: relative;
color: #fff;
z-index: 1100;
+ background-color: var(--current-theme-primary);
+ background: linear-gradient(
+ 16deg,
+ var(--current-theme-primary-dark) 0%,
+ var(--current-theme-primary) 70%
+ );
+
@media (max-width: 980px) {
z-index: 3;
}
@@ -19,6 +39,7 @@
display: none;
}
}
+
@media (max-width: 767px) {
.portal-primary {
width: 100%;
@@ -27,12 +48,8 @@
}
}
- .primary-content,
- .secondary-content {
- font-weight: 300;
- font-size: 24px;
- }
.primary-content {
+ font-size: 24px;
a {
color: #fff;
}
@@ -41,8 +58,7 @@
}
}
.secondary-content {
- margin: 25px 0 50px;
- display: flex;
+ margin: 25px 0 50px; // This margin is specific to portal-header
@media (max-width: 450px) {
flex-direction: column;
font-size: 17px;
@@ -56,31 +72,19 @@
}
}
-.search-page-portal-header {
- background-color: var(--current-theme-primary);
- background: linear-gradient(
- 16deg,
- var(--current-theme-primary-dark) 0%,
- var(--current-theme-primary) 70%
- );
-}
-
.search-page-coms {
- position: relative;
background-color: #f3f7f6;
padding: 20px 0 40px;
+ position: relative; // Only defined here for .search-page-coms
+
h2 {
font-size: 19px;
}
.primary-content {
- font-weight: 300;
font-size: 19px;
}
.secondary-content {
- font-weight: 300;
font-size: 15px;
- margin-top: 25px;
- display: flex;
}
}
@@ -89,7 +93,6 @@
width: 250px;
height: 144.34px;
margin: 72.17px 0;
-
background-size: auto 288.6751px;
background-position: center;
@@ -104,8 +107,6 @@
background: inherit;
left: 36.61px;
- /*counter transform the bg image on the caps*/
- &:after,
&:after {
content: '';
position: absolute;
@@ -125,7 +126,6 @@
.hexBottom {
bottom: -88.3883px;
-
&:after {
background-position: center bottom;
}
@@ -163,13 +163,13 @@
width: 100%;
color: #000;
font-weight: bold;
+
&.front {
transform: translateZ(69px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
-
background-color: rgba(0, 0, 0, 0.32);
}
&.top {
@@ -184,9 +184,6 @@
}
}
-.search {
- padding: 0 0 50px;
-}
.search-heading {
margin: 20px 0;
@media (max-width: 450px) {
@@ -202,40 +199,48 @@
font-size: 14px;
}
}
+
.search-wrapper {
+ box-sizing: border-box;
+ flex: 0 0 auto;
+ flex-grow: 1;
+ flex-basis: 100%;
+ max-width: 100%;
+ min-width: 0;
display: flex;
+ position: relative;
+ padding: 20px 35px;
}
.close-filters-btn,
.show-filters-btn {
- display: none;
-}
+ display: none; // Hidden by default
-@media (max-width: 980px) {
- .close-filters-btn,
- .show-filters-btn {
- display: block;
+ @media (max-width: 980px) {
+ display: block; // Show on smaller screens
position: absolute;
- top: 40px;
+ top: 20px;
right: 0;
background: none;
border: 0;
font-size: 12px;
margin: 10px;
padding: 0;
+ z-index: 1100;
+ cursor: pointer;
}
-}
-@media (max-width: 450px) {
- .close-filters-btn,
- .show-filters-btn {
+ @media (max-width: 450px) {
top: 0px;
left: 0;
margin: 10px 0 20px;
border: 1px solid #ccc;
padding: 10px;
- display: block;
}
+}
+
+// Specific placement for close button on smaller screens
+@media (max-width: 450px) {
.close-filters-btn {
left: 20px;
}
@@ -243,8 +248,9 @@
.search-nav {
flex: 0 0 auto;
- padding: 0;
- width: 430px;
+ padding: 20px 30px;
+ width: 40%;
+ max-width: 450px;
@media (max-width: 980px) {
opacity: 0;
@@ -261,13 +267,15 @@
min-height: 100%;
overflow: scroll;
position: fixed;
- z-index: 1205;
+ z-index: 1205; // Higher z-index for fixed overlay
top: 0;
padding: 80px 25px;
+
&.show-mobile-filters {
opacity: 1;
transform: translateX(0);
transition: opacity 1s;
+ max-width: 100vw;
}
}
@@ -288,21 +296,47 @@
.search-content {
flex: 1;
- padding: 0 0 0 30px;
+ padding: 20px 50px 20px 20px;
position: relative;
- max-width: 100%;
+ max-width: calc(100% - 450px - 380px);
+ @media screen and (max-width: 1410px) {
+ max-width: 100%;
+ }
@media (max-width: 980px) {
- padding: 0;
+ padding: 0 10px;
}
.search-results {
- border: 1px solid $newspaper;
- border-radius: $border-radius-default;
max-width: 100%;
+
.search-result {
- border-bottom: 1px solid $newspaper;
+ border: 1px solid transparent;
+ &:nth-child(odd) {
+ background-color: var(--cloud);
+ }
+ &.expanded {
+ border-top-color: var(--current-theme-primary-light);
+ border-bottom-color: var(--current-theme-primary-light);
+ background-color: var(--current-theme-primary-ultralight);
+ }
}
}
+}
+.search-details {
+ flex-grow: 1;
+ max-width: 380px;
+ @media screen and (max-width: 1410px) {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ max-width: 100%;
+ z-index: 5000;
+ padding: 20px;
+ overflow-y: auto;
+ }
}
.search-sort {
@@ -323,42 +357,42 @@
flex-basis: 50%;
}
-.on-accordion-wrapper .keyword-accordion {
- .accordion-title {
- background: #e9e9e9;
- width: 18px;
- border-radius: 50%;
- line-height: 0;
- display: flex;
- justify-content: center;
- align-items: center;
- height: 18px;
- font-size: 12px;
- align-content: space-around;
- flex-direction: row;
- font-weight: 600;
- color: #515151;
- font-family: monospace;
- &:after {
- display: none;
+.on-accordion-wrapper {
+ .keyword-accordion {
+ .accordion-title {
+ background: #e9e9e9;
+ width: 18px;
+ border-radius: 50%;
+ line-height: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 18px;
+ font-size: 12px;
+ align-content: space-around;
+ flex-direction: row;
+ font-weight: 600;
+ color: #515151;
+ font-family: monospace;
+ &:after {
+ display: none;
+ }
}
- }
- .accordion-content {
- font-size: 12px;
- margin: 10px 0;
- .on-icon {
- font-size: 10px;
- padding: 1px 4px;
- border-radius: $border-radius-default;
- line-height: 1;
- background-color: var(--current-theme-primary);
- color: #fff;
+ .accordion-content {
+ font-size: 12px;
+ margin: 10px 0;
+ .on-icon {
+ font-size: 10px;
+ padding: 1px 4px;
+ border-radius: $border-radius-default;
+ line-height: 1;
+ background-color: var(--current-theme-primary);
+ color: #fff;
+ }
}
}
-}
-
-.on-accordion-wrapper .keyword-accordion {
- .accordion-title {
+ // Specific placement for keyword-accordion title
+ .keyword-accordion .accordion-title {
position: absolute;
top: -20px;
left: 70px;
@@ -388,13 +422,11 @@
cursor: pointer;
align-items: center;
justify-content: flex-start;
-
font-size: 14px;
border-top: 1px solid $newspaper;
padding: 10px;
display: block;
font-weight: 500;
- font-weight: 500;
&.selected-item {
color: var(--current-theme-primary);
}
@@ -406,10 +438,12 @@
.search-facet-wrapper {
.modality-facet.facet-accordion.on-accordion-wrapper {
margin: 20px 0;
- border: 0;
- background: #e5f4f7;
+ border: 0; // Explicitly override border for mobile
+ background: var(--current-theme-primary-ultralight);
+ border: 1px solid var(--current-theme-primary-light);
border-radius: $border-radius-default;
padding: 10px;
+
.accordion-item.collapsed {
max-height: initial;
}
@@ -421,7 +455,7 @@
color: var(--current-theme-primary);
}
.accordion-title {
- margin: 10px 0 15px 0;
+ margin: 10px 0 15px 0; // Explicitly override margin for accordion title
padding: 0;
&:after {
display: none;
@@ -442,7 +476,6 @@
display: flex;
align-items: center;
justify-content: flex-start;
-
span {
margin-left: 10px;
display: inline-block;
@@ -452,7 +485,6 @@
.level-1 > li {
border-top: 1px solid $newspaper;
-
> .label {
padding: 10px;
font-weight: 500;
@@ -465,7 +497,6 @@
> li {
display: flex;
justify-content: flex-start;
-
.label {
margin-left: 15px;
}
diff --git a/packages/openneuro-app/src/scripts/search/scss/search-result-details.scss b/packages/openneuro-app/src/scripts/search/scss/search-result-details.scss
new file mode 100644
index 0000000000..ea51094d17
--- /dev/null
+++ b/packages/openneuro-app/src/scripts/search/scss/search-result-details.scss
@@ -0,0 +1,70 @@
+@import '../../scss/variables';
+
+.search-details {
+ border-left: 1px solid var(--current-theme-primary-light);
+ background-color: var(--current-theme-primary-ultralight);
+ padding: 20px;
+ font-size: 14px;
+ min-height: calc(100vh - 106px);
+ h4 {
+ font-size: 16px;
+ }
+
+ .result-summary-meta {
+ margin-bottom: 10px;
+
+ label {
+ font-weight: 600;
+ color: #000;
+ margin-bottom: 5px;
+ display: inline-block;
+ }
+ }
+
+ .search-details-scroll {
+ position: sticky;
+ top: 10px;
+ align-self: flex-start;
+ width: 100%;
+ padding: 15px;
+ z-index: 100;
+ max-height: calc(100vh - 20px);
+ overflow-y: auto;
+ overflow-x: hidden;
+
+ }
+
+ .task-list,
+ .modality-list {
+ .list-item {
+ display: inline-block;
+ font-weight: 600;
+ padding: 3px 6px;
+ margin: 0 10px 5px 0;
+ color: var(--current-theme-secondary);
+ border: 1px solid;
+ border-radius: $border-radius-default;
+ }
+ }
+
+
+
+ .close-details-button {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ right: 0;
+ border: 0;
+ background-color: transparent;
+ color: #000;
+ font-size: 20px;
+ padding: 2px 5px;
+ margin-right: 10px;
+ &:hover {
+ font-weight: bold;
+ }
+ &:focus {
+ outline: -webkit-focus-ring-color auto 1px;
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/openneuro-app/src/scripts/components/search-page/search-result.scss b/packages/openneuro-app/src/scripts/search/scss/search-result.scss
similarity index 50%
rename from packages/openneuro-app/src/scripts/components/search-page/search-result.scss
rename to packages/openneuro-app/src/scripts/search/scss/search-result.scss
index 5853d367fb..84a58b4bb3 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/search-result.scss
+++ b/packages/openneuro-app/src/scripts/search/scss/search-result.scss
@@ -1,8 +1,9 @@
-@import '../scss/variables';
+@import '../../scss/variables';
.search-result {
- padding: 20px;
+ padding: 30px 20px;
h3 {
margin: 0 0 5px;
+ font-size: 19px;
a {
color: var(--current-theme-primary);
&:hover {
@@ -10,11 +11,26 @@
}
}
}
+ cite {
+ color: var(--text-citation);
+ }
+ .hexagon-wrapper {
+ margin-left: 10px;
+ width: 28px;
+ height: 28px;
+ div.label {
+ font-size: 10px;
+ font-weight: normal;
+ }
+ }
+ .dataset-permissions-tag,
.result-icon-wrap,
.owner-icon-wrap {
display: flex;
justify-content: flex-end;
+ align-items: center;
+ padding: 0;
@media (max-width: 480px) {
justify-content: flex-start;
margin-bottom: 10px;
@@ -26,7 +42,6 @@
.result-activity-icon {
width: 18px;
height: 19px;
-
&[data-tooltip]::after {
width: 130px;
padding: 5px;
@@ -36,65 +51,23 @@
}
}
- .result-meta-body > div {
- font-size: 14px;
- margin-bottom: 10px;
- display: flex;
- flex-direction: column;
-
- strong {
- text-transform: uppercase;
- color: #666;
- margin-bottom: 5px;
- }
- .list-item {
- display: inline-block;
-
- border-right: 1px solid #cacaca;
- font-weight: 600;
- padding: 3px 6px;
- margin: 0 10px 5px 0;
- color: var(--current-theme-secondary);
- border: 1px solid;
- border-radius: $border-radius-default;
- }
- }
-
- .result-meta-footer {
- display: flex;
- flex-wrap: wrap;
- .result-summary-meta {
- margin: 0 10px 10px 0;
- padding: 0 40px 0 0;
- font-size: 14px;
- font-weight: 600;
- &:last-child {
- padding-right: 0;
- }
- strong {
- text-transform: uppercase;
- color: #666;
- }
- }
- }
-
.updated-divider {
margin: 0 10px;
@media (max-width: 767px) {
display: none;
}
}
- .result-upload-info {
- text-align: left;
- font-size: 12px;
- margin: 0 0 20px;
- display: flex;
- justify-content: flex-start;
- @media (max-width: 767px) {
- flex-direction: column;
- }
- span {
- color: #666;
+
+ .result-actions {
+ text-align: right;
+ padding: 0;
+ .on-button {
+ border: 1px solid var(--current-theme-secondary);
+ background-color: #fff;
+ &.expanded {
+ color: #fff;
+ background-color: var(--current-theme-secondary);
+ }
}
}
}
diff --git a/packages/openneuro-app/src/scripts/components/search-page/search-sort.scss b/packages/openneuro-app/src/scripts/search/scss/search-sort.scss
similarity index 96%
rename from packages/openneuro-app/src/scripts/components/search-page/search-sort.scss
rename to packages/openneuro-app/src/scripts/search/scss/search-sort.scss
index 5aea9dca64..db8cf37cc9 100644
--- a/packages/openneuro-app/src/scripts/components/search-page/search-sort.scss
+++ b/packages/openneuro-app/src/scripts/search/scss/search-sort.scss
@@ -1,4 +1,4 @@
-@import '../scss/variables';
+@import '../../scss/variables';
.search-sort-list-label {
padding: 0 0 5px;
diff --git a/packages/openneuro-app/src/scripts/search/scss/sliding-radio-group.scss b/packages/openneuro-app/src/scripts/search/scss/sliding-radio-group.scss
new file mode 100644
index 0000000000..f1fddc1455
--- /dev/null
+++ b/packages/openneuro-app/src/scripts/search/scss/sliding-radio-group.scss
@@ -0,0 +1,115 @@
+@import '../../scss/variables';
+
+
+
+// Root container for the general SlidingRadioGroup component
+.sliding-radio-group-root {
+ *,
+ *::before,
+ *::after {
+ box-sizing: border-box;
+ }
+ &.btn-group-wrapper.facet-radio.show-dataset-radios-container {
+ position: relative;
+ margin: 0 0 10px;
+ background-color: #f6f6f6;
+ border: 1px solid #eee;
+ border-radius: 4px;
+ overflow: hidden;
+ min-height: 40px;
+
+ display: flex;
+ flex-direction: row;
+ align-items: stretch;
+ justify-content: flex-start;
+
+ &:after {
+ content: none;
+ display: none;
+ background: none;
+ position: static;
+ width: 0;
+ height: 0;
+ }
+ }
+
+ // Styles for .dataset-filter-radio (individual radio button wrapper)
+ .dataset-filter-radio {
+ margin: 0;
+
+ [type='radio']:checked,
+ [type='radio']:not(:checked) {
+ position: absolute;
+ left: -9999px;
+ }
+
+ > label {
+ font-size: 13px;
+ line-height: 1.7em;
+ position: relative;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ white-space: nowrap;
+ padding: 6px 12px;
+ border-right: 1px solid #ddd;
+
+ &::before,
+ &::after {
+ display: none;
+ }
+ }
+
+ &:last-child > label,
+ label.is-active {
+ border-right: none;
+ }
+
+ input[type='radio']:focus-within + label {
+ box-shadow: inset 0 0 3px 1px #00a3ff;
+ outline: 0;
+ }
+ }
+
+ // Styles for the inner group of radio buttons container
+
+ .show-dataset-radios-group {
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-start;
+ align-items: stretch;
+ z-index: 1;
+ position: relative;
+ width: 100%;
+ }
+
+ .sliding-highlight {
+ position: absolute;
+ background-color: var(--current-theme-primary);
+ border-radius: 4px;
+
+ transition: left 0.3s ease, width 0.3s ease, top 0.3s ease, height 0.3s ease,
+ opacity 0.3s ease;
+ z-index: 0;
+ opacity: 0;
+ }
+
+ //Specific button states within this component (text color changes)
+
+ .dataset-filter-radio {
+ > label {
+ color: var(--current-theme-primary);
+ background-color: transparent;
+ }
+
+ [type='radio']:checked + label {
+ color: #fff;
+ }
+
+ [type='radio']:not(:checked) + label:hover {
+ color: var(--current-theme-primary-dark);
+ }
+ }
+}
diff --git a/packages/openneuro-app/src/scripts/search/search-container.tsx b/packages/openneuro-app/src/scripts/search/search-container.tsx
index ca73c0d0f2..9bb6710f14 100644
--- a/packages/openneuro-app/src/scripts/search/search-container.tsx
+++ b/packages/openneuro-app/src/scripts/search/search-container.tsx
@@ -1,14 +1,16 @@
-import React, { useContext, useEffect } from "react"
+import React, { useContext, useEffect, useRef, useState } from "react"
import type { FC } from "react"
+import * as Sentry from "@sentry/react"
import { useLocation } from "react-router-dom"
-import { SearchPage } from "../components/search-page/SearchPage"
-import { SearchResultsList } from "../components/search-page/SearchResultsList"
-import { NeurobagelSearch } from "../components/search-page/NeurobagelSearch"
+import { SearchPage } from "./components/SearchPage"
+import { SearchResultsList } from "./components/SearchResultsList"
+import { NeurobagelSearch } from "./components/NeurobagelSearch"
import { Button } from "../components/button/Button"
import { Loading } from "../components/loading/Loading"
+import { modalityShortMapping } from "../components/formatting/modality-label"
+
import {
AgeRangeInput,
- AllDatasetsToggle,
AuthorInput,
BodyPartsInput,
DatasetTypeSelect,
@@ -21,7 +23,6 @@ import {
ScannerManufacturersModelNames,
SectionSelect,
SexRadios,
- ShowDatasetRadios,
SortBySelect,
SpeciesSelect,
StudyDomainInput,
@@ -30,13 +31,15 @@ import {
TracerNames,
TracerRadionuclides,
} from "./inputs"
+import { DatasetsRadioTabs } from "./components/DatasetsRadioTabs"
import FiltersBlockContainer from "./filters-block-container"
import AggregateCountsContainer from "../pages/front-page/aggregate-queries/aggregate-counts-container"
import { useSearchResults } from "./use-search-results"
import { SearchParamsCtx } from "./search-params-ctx"
import type { SearchParams } from "./initial-search-params"
import Helmet from "react-helmet"
-import AdminUser from "../authentication/admin-user.jsx"
+import type { SearchResultItemProps } from "./components/SearchResultItem"
+import { SearchResultDetails } from "./components/SearchResultDetails"
export interface SearchContainerProps {
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
@@ -97,7 +100,8 @@ export const setDefaultSearch = (
// Check for grant-related conditions
if (
- is_grant_portal && grant === "nih" &&
+ is_grant_portal &&
+ grant === "nih" &&
searchParams.brain_initiative !== "true"
) {
setSearchParams((prevState) => ({
@@ -153,12 +157,59 @@ const SearchContainer: FC = ({ portalContent }) => {
hasNextPage = data.datasets.pageInfo.hasNextPage
}
+ const [clickedItemData, setClickedItemData] = useState<
+ SearchResultItemProps["node"] | null
+ >(null)
+
+ const lastOpenedButtonRef = useRef(null)
+
+ useEffect(() => {
+ setClickedItemData(null)
+ }, [JSON.stringify(searchParams)])
+
+ // handleItemClick to accept itemId and event, and store the event.currentTarget
+ const handleItemClick = (
+ itemId: string,
+ event: React.MouseEvent,
+ ) => {
+ const nodeData =
+ resultsList.find((item) => item.node.id === itemId)?.node || null
+
+ if (!nodeData) {
+ Sentry.captureException(`Error: nodeData not found for ID: ${itemId}`)
+ return
+ }
+
+ if (clickedItemData && clickedItemData.id === nodeData.id) {
+ // If the same item is clicked again, close details
+ setClickedItemData(null)
+ // Focus will be returned by handleCloseDetails
+ } else {
+ setClickedItemData(nodeData)
+ lastOpenedButtonRef.current = event.currentTarget
+ }
+ }
+
+ // handleCloseDetails to use setTimeout for focus
+ const handleCloseDetails = () => {
+ setClickedItemData(null)
+ setTimeout(() => {
+ if (lastOpenedButtonRef.current) {
+ lastOpenedButtonRef.current.focus()
+ lastOpenedButtonRef.current = null
+ }
+ }, 0)
+ }
+
+ const labelText = modality ? modalityShortMapping(modality) : null
+
return (
<>
- OpenNeuro - {modality || selected_grant || ""} Search
+ OpenNeuro - {labelText || selected_grant || ""} Search
portalContent.modality
@@ -172,20 +223,27 @@ const SearchContainer: FC = ({ portalContent }) => {
)}
renderSortBy={() => }
renderSearchHeader={() => (
- <>
- {portalContent
- ? "Search " + (modality || selected_grant || "") + " Portal"
- : "Search All Datasets"}
- >
+
+
+ {portalContent
+ ? (
+
+ {"Search " + (labelText || selected_grant || "") +
+ " Portal"}
+
+ )
+ : {"Search All Datasets"}
}
+
+
+
+
+
)}
renderSearchFacets={() => (
<>
-
-
-
- {!searchParams.searchAllDatasets && }
+
{!portalContent
?
: }
@@ -233,18 +291,26 @@ const SearchContainer: FC = ({ portalContent }) => {
{/* TODO: make div below into display component. */}
- {hasNextPage && resultsList.length > 0 &&
- (
-
-
-
- )}
+ {hasNextPage && resultsList.length > 0 && (
+
+
+
+ )}
>
)}
+ renderItemDetails={() =>
+ clickedItemData && (
+
+ )}
/>
>
)
diff --git a/packages/openneuro-app/src/scripts/search/use-search-results.tsx b/packages/openneuro-app/src/scripts/search/use-search-results.tsx
index 6716e9aa26..c134bffe1d 100644
--- a/packages/openneuro-app/src/scripts/search/use-search-results.tsx
+++ b/packages/openneuro-app/src/scripts/search/use-search-results.tsx
@@ -61,8 +61,10 @@ const searchQuery = gql`
}
latestSnapshot {
size
+ readme
summary {
modalities
+ primaryModality
secondaryModalities
sessions
subjects
@@ -94,6 +96,7 @@ const searchQuery = gql`
description {
Name
Authors
+ DatasetDOI
}
}
analytics {
diff --git a/packages/openneuro-app/src/scripts/users/github-auth-button.tsx b/packages/openneuro-app/src/scripts/users/github-auth-button.tsx
index 31f5c22114..440b530d04 100644
--- a/packages/openneuro-app/src/scripts/users/github-auth-button.tsx
+++ b/packages/openneuro-app/src/scripts/users/github-auth-button.tsx
@@ -18,7 +18,7 @@ const GithubSyncDiv = styled.div`
margin: 10px;
&:hover {
- background-color: var(--current-theme-primary-light);
+ background-color: var(--current-theme-primary-ultralight);
color: var(--current-theme-primary);
}
&.active {