diff --git a/.pnp.js b/.pnp.js index fd556db99b..7fad324d23 100755 --- a/.pnp.js +++ b/.pnp.js @@ -117,13 +117,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "SOFT", }], - ["npm:3.3.19", { - "packageLocation": "./.yarn/cache/@apollo-client-npm-3.3.19-39baf9f99e-a7266cfdff.zip/node_modules/@apollo/client/", - "packageDependencies": [ - ["@apollo/client", "npm:3.3.19"] - ], - "linkType": "SOFT", - }], ["virtual:4112afb9dad10978c159910bf10db9840b981b1333117623c8a4a8cf77481344a0a24735a5506e2920c18e3cfa2cc179489824b6a56c988bb070f4f60da40974#npm:3.3.14", { "packageLocation": "./.yarn/$$virtual/@apollo-client-virtual-01c7b6c19d/0/cache/@apollo-client-npm-3.3.14-1ebea52b4f-a08163a998.zip/node_modules/@apollo/client/", "packageDependencies": [ @@ -293,40 +286,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "subscriptions-transport-ws" ], "linkType": "HARD", - }], - ["virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:3.3.19", { - "packageLocation": "./.yarn/$$virtual/@apollo-client-virtual-c2be05e84b/0/cache/@apollo-client-npm-3.3.19-39baf9f99e-a7266cfdff.zip/node_modules/@apollo/client/", - "packageDependencies": [ - ["@apollo/client", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:3.3.19"], - ["@graphql-typed-document-node/core", "virtual:51e032042460c5acb2fee2f0f33950a409d321b7c2d7932ec5edd2d65c6eef219caf38d6fbd40ae889377bab601157a449b02b052ebb0cf87371875d317dc909#npm:3.1.0"], - ["@types/graphql", null], - ["@types/react", null], - ["@types/subscriptions-transport-ws", null], - ["@types/zen-observable", "npm:0.8.2"], - ["@wry/context", "npm:0.6.0"], - ["@wry/equality", "npm:0.4.0"], - ["fast-json-stable-stringify", "npm:2.1.0"], - ["graphql", null], - ["graphql-tag", "virtual:51e032042460c5acb2fee2f0f33950a409d321b7c2d7932ec5edd2d65c6eef219caf38d6fbd40ae889377bab601157a449b02b052ebb0cf87371875d317dc909#npm:2.12.4"], - ["hoist-non-react-statics", "npm:3.3.2"], - ["optimism", "npm:0.16.1"], - ["prop-types", "npm:15.7.2"], - ["react", "npm:17.0.2"], - ["subscriptions-transport-ws", null], - ["symbol-observable", "npm:2.0.3"], - ["ts-invariant", "npm:0.7.3"], - ["tslib", "npm:1.14.1"], - ["zen-observable", "npm:0.8.15"] - ], - "packagePeers": [ - "@types/graphql", - "@types/react", - "@types/subscriptions-transport-ws", - "graphql", - "react", - "subscriptions-transport-ws" - ], - "linkType": "HARD", }] ]], ["@apollo/protobufjs", [ @@ -9424,7 +9383,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./packages/openneuro-components/", "packageDependencies": [ ["@openneuro/components", "workspace:packages/openneuro-components"], - ["@apollo/client", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:3.3.19"], ["@mdx-js/react", "virtual:0f7d3aee8f4ba1ddd800f44d0aa4455a1e4b9ae1a24c9eda9668f886bc29f684a49cc3e1c10c4481252ea0f7eec58c4be3167e18e5c332f257a51ab3e50e4f7a#npm:1.6.22"], ["@storybook/addon-a11y", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:6.2.9"], ["@storybook/addon-actions", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:6.2.9"], @@ -9437,13 +9395,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@storybook/theming", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:6.2.9"], ["@testing-library/jest-dom", "npm:5.12.0"], ["@testing-library/react", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:11.2.7"], + ["@types/bytes", "npm:3.1.0"], ["@types/mdx-js__react", "npm:1.5.3"], ["@types/react-router-dom", "npm:5.1.7"], ["@types/react-slick", "npm:0.23.4"], ["@types/slick-carousel", "npm:1.6.34"], + ["bytes", "npm:3.1.0"], ["css-loader", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:5.2.4"], ["date-fns", "npm:2.21.3"], - ["graphql-hooks", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:5.1.1"], + ["pluralize", "npm:8.0.0"], ["rc-slider", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:9.7.2"], ["react", "npm:17.0.2"], ["react-router-dom", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:5.2.0"], @@ -11855,6 +11815,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["@types/bytes", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/@types-bytes-npm-3.1.0-81383ade8b-184b848178.zip/node_modules/@types/bytes/", + "packageDependencies": [ + ["@types/bytes", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], ["@types/cheerio", [ ["npm:0.22.28", { "packageLocation": "./.yarn/cache/@types-cheerio-npm-0.22.28-da80c0e8f2-a3164dcf1f.zip/node_modules/@types/cheerio/", @@ -20224,15 +20193,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["dequal", [ - ["npm:2.0.2", { - "packageLocation": "./.yarn/cache/dequal-npm-2.0.2-370927eb6c-3b5b019a87.zip/node_modules/dequal/", - "packageDependencies": [ - ["dequal", "npm:2.0.2"] - ], - "linkType": "HARD", - }] - ]], ["des.js", [ ["npm:1.0.1", { "packageLocation": "./.yarn/cache/des.js-npm-1.0.1-9f155eddb6-74cd0aa0c5.zip/node_modules/des.js/", @@ -25831,30 +25791,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["graphql-hooks", [ - ["npm:5.1.1", { - "packageLocation": "./.yarn/cache/graphql-hooks-npm-5.1.1-b58943726d-4d615236e2.zip/node_modules/graphql-hooks/", - "packageDependencies": [ - ["graphql-hooks", "npm:5.1.1"] - ], - "linkType": "SOFT", - }], - ["virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:5.1.1", { - "packageLocation": "./.yarn/$$virtual/graphql-hooks-virtual-e19fdcc460/0/cache/graphql-hooks-npm-5.1.1-b58943726d-4d615236e2.zip/node_modules/graphql-hooks/", - "packageDependencies": [ - ["graphql-hooks", "virtual:bb4ed02b339ed801b02d2ec15b42a5aa7b1afdaf44119aefaab128a59d6e16cc6018880c169f24bf2107550e914562ee9e1780db01a12e1bc3c492ad0a049c36#npm:5.1.1"], - ["@types/react", null], - ["dequal", "npm:2.0.2"], - ["extract-files", "npm:9.0.0"], - ["react", "npm:17.0.2"] - ], - "packagePeers": [ - "@types/react", - "react" - ], - "linkType": "HARD", - }] - ]], ["graphql-iso-date", [ ["npm:3.6.1", { "packageLocation": "./.yarn/cache/graphql-iso-date-npm-3.6.1-bbab129ea7-d33245ccab.zip/node_modules/graphql-iso-date/", @@ -33869,15 +33805,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@wry/trie", "npm:0.3.0"] ], "linkType": "HARD", - }], - ["npm:0.16.1", { - "packageLocation": "./.yarn/cache/optimism-npm-0.16.1-b6a027d092-760b129552.zip/node_modules/optimism/", - "packageDependencies": [ - ["optimism", "npm:0.16.1"], - ["@wry/context", "npm:0.6.0"], - ["@wry/trie", "npm:0.3.0"] - ], - "linkType": "HARD", }] ]], ["optimize-css-assets-webpack-plugin", [ diff --git a/helm/README.md b/helm/README.md index 84743a4049..348e406ea0 100644 --- a/helm/README.md +++ b/helm/README.md @@ -122,6 +122,11 @@ Other values which can be overridden are found in the chart version of [values.y Installing a chart deploys it to a cluster. This creates an initial release by generating the configuration templates and applying them to the cluster. +```bash +# Add an ssh key for GitHub remotes +kubectl create secret generic openneuro-my-dev-ssh-key --from-file=datalad-key=datalad-key +``` + ```bash # Make sure you're in the helm directory and run helm install -f values.yaml -f secrets.yaml openneuro-my-dev openneuro/ diff --git a/helm/eksctl-cluster-prod.yaml b/helm/eksctl-cluster-prod.yaml index 86189eaeac..f74db9d5dc 100644 --- a/helm/eksctl-cluster-prod.yaml +++ b/helm/eksctl-cluster-prod.yaml @@ -34,10 +34,11 @@ nodeGroups: preBootstrapCommands: - apt update - apt install -y nfs-common zfsutils-linux - - name: storage + - name: storage-m5 amiFamily: Ubuntu2004 - instanceType: r5ad.large + instanceType: m5ad.2xlarge desiredCapacity: 2 + labels: { role: storage } availabilityZones: - us-west-2b - us-west-2c diff --git a/helm/manual-old-indexer b/helm/manual-old-indexer new file mode 100644 index 0000000000..0e5efd177b --- /dev/null +++ b/helm/manual-old-indexer @@ -0,0 +1,36 @@ +apiVersion: batch/v1 +kind: Job +metadata: + annotations: + cronjob.kubernetes.io/instantiate: manual + name: openneuro-prod-indexer-manual-1 + namespace: default +spec: + backoffLimit: 6 + completions: 1 + parallelism: 1 + template: + spec: + containers: + - envFrom: + - configMapRef: + name: openneuro-prod-configmap + - secretRef: + name: openneuro-prod-secret + image: openneuro/indexer:v3.31.1 + imagePullPolicy: IfNotPresent + name: openneuro-indexer + resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 100m + memory: 256Mi + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + dnsPolicy: ClusterFirst + restartPolicy: OnFailure + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 diff --git a/helm/openneuro/Chart.yaml b/helm/openneuro/Chart.yaml index 46ecf08baf..e486d362f5 100644 --- a/helm/openneuro/Chart.yaml +++ b/helm/openneuro/Chart.yaml @@ -5,7 +5,7 @@ description: OpenNeuro production deployment chart home: https://openneuro.org sources: - https://github.com/openNeuroOrg/openneuro -appVersion: 3.33.2 +appVersion: 3.33.5 dependencies: - name: redis version: 10.6.17 diff --git a/helm/openneuro/templates/api-deployment.yaml b/helm/openneuro/templates/api-deployment.yaml index 6342494249..5ad69619c3 100644 --- a/helm/openneuro/templates/api-deployment.yaml +++ b/helm/openneuro/templates/api-deployment.yaml @@ -28,7 +28,7 @@ spec: cpu: "1.2" memory: "2Gi" requests: - cpu: ".5" + cpu: ".3" memory: "768Mi" ports: - containerPort: 8111 diff --git a/helm/openneuro/templates/dataset-worker-stateful-set.yaml b/helm/openneuro/templates/dataset-worker-stateful-set.yaml index 8e898f127a..46a3a1187d 100644 --- a/helm/openneuro/templates/dataset-worker-stateful-set.yaml +++ b/helm/openneuro/templates/dataset-worker-stateful-set.yaml @@ -30,6 +30,8 @@ spec: - key: "storage" operator: "Exists" effect: "NoSchedule" + nodeSelector: + role: storage volumes: - name: ssh-key secret: @@ -44,11 +46,11 @@ spec: command: ["gunicorn", "--bind", "0.0.0.0:80", "--reload", "datalad_service.app:create_app('/datasets')", "--workers", "8", "--worker-class", "gevent", "--timeout", "60", "--keep-alive", "30"] resources: limits: - cpu: "2" - memory: "8Gi" + cpu: "3" + memory: "10Gi" requests: - cpu: "0.5" - memory: "1Gi" + cpu: "1" + memory: "4Gi" ports: - containerPort: 80 envFrom: diff --git a/helm/openneuro/templates/ingress.yaml b/helm/openneuro/templates/ingress.yaml index c0bc3cfa65..a19cc21f32 100644 --- a/helm/openneuro/templates/ingress.yaml +++ b/helm/openneuro/templates/ingress.yaml @@ -1,4 +1,4 @@ -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ .Release.Name }} @@ -18,47 +18,74 @@ spec: http: paths: - path: /* + pathType: ImplementationSpecific backend: - serviceName: www-redirect - servicePort: use-annotation + service: + name: www-redirect + port: + name: use-annotation - host: {{ .Values.hostname }} http: paths: - path: /* + pathType: ImplementationSpecific backend: - serviceName: ssl-redirect - servicePort: use-annotation + service: + name: ssl-redirect + port: + name: use-annotation - path: /graphql-subscriptions + pathType: ImplementationSpecific backend: - serviceName: {{ .Release.Name }}-api - servicePort: 8111 + service: + name: {{ .Release.Name }}-api + port: + number: 8111 - path: /crn/* + pathType: ImplementationSpecific backend: - serviceName: {{ .Release.Name }}-api - servicePort: 8111 + service: + name: {{ .Release.Name }}-api + port: + number: 8111 - path: /sitemap.xml + pathType: ImplementationSpecific backend: - serviceName: {{ .Release.Name }}-api - servicePort: 8111 + service: + name: {{ .Release.Name }}-api + port: + number: 8111 {{- $relname := .Release.Name -}} {{- range until ( .Values.dataladWorkers | int ) }} - path: /uploads/{{ . }}/* + pathType: ImplementationSpecific backend: - serviceName: {{ $relname }}-dataset-worker-{{ . }} - servicePort: 80 + service: + name: {{ $relname }}-dataset-worker-{{ . }} + port: + number: 80 {{- end }} {{- $relname := .Release.Name -}} {{- range until ( .Values.dataladWorkers | int ) }} - path: /git/{{ . }}/* + pathType: ImplementationSpecific backend: - serviceName: {{ $relname }}-dataset-worker-{{ . }} - servicePort: 80 + service: + name: {{ $relname }}-dataset-worker-{{ . }} + port: + number: 80 {{- end }} - path: /intake/* + pathType: ImplementationSpecific backend: - serviceName: {{ .Release.Name }}-apm-server - servicePort: 8200 + service: + name: {{ .Release.Name }}-apm-server + port: + number: 8200 - path: /* + pathType: ImplementationSpecific backend: - serviceName: {{ .Release.Name }}-web - servicePort: 80 \ No newline at end of file + service: + name: {{ .Release.Name }}-web + port: + number: 80 \ No newline at end of file diff --git a/helm/openneuro/templates/openebs-pool-statefulset.yaml b/helm/openneuro/templates/openebs-pool-statefulset.yaml deleted file mode 100644 index 2c17e73a74..0000000000 --- a/helm/openneuro/templates/openebs-pool-statefulset.yaml +++ /dev/null @@ -1,68 +0,0 @@ -{{- $relname := .Release.Name -}} -{{- $stripesize := int .Values.storagePools.stripeSize }} -{{- range .Values.storagePools.pools }} -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ $relname }}-storage-pool-{{ .name }} -spec: - replicas: {{ add (div (int .size) $stripesize) 1 }} - selector: - matchLabels: - app: {{ $relname }}-storage-pool-{{ .name }} - serviceName: {{ $relname }}-storage-pool-{{ .name }} - template: - metadata: - labels: - app: {{ $relname }}-storage-pool-{{ .name }} - spec: - tolerations: - - key: "storage" - operator: "Exists" - effect: "NoSchedule" - nodeSelector: - alpha.eksctl.io/nodegroup-name: storage - affinity: - podAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: "app" - operator: In - values: - - {{ $relname }}-storage-pool-{{ .name }} - topologyKey: "kubernetes.io/hostname" - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "app" - operator: NotIn - values: - - {{ $relname }}-storage-pool-{{ .name }} - topologyKey: "kubernetes.io/hostname" - containers: - - name: init-storage-pool - image: alpine - command: - - /bin/sh - args: - - "-c" - - "sleep infinity" - volumeDevices: - - name: storage-pool - devicePath: /dev/pool - volumeClaimTemplates: - - metadata: - name: storage-pool - spec: - accessModes: ["ReadWriteOnce"] - storageClassName: "openebs-pool-block" - volumeMode: Block - resources: - requests: - storage: {{ $stripesize }} ---- -{{- end }} diff --git a/helm/openneuro/templates/openebs-pool-storageclass.yaml b/helm/openneuro/templates/openebs-pool-storageclass.yaml deleted file mode 100644 index 2470273896..0000000000 --- a/helm/openneuro/templates/openebs-pool-storageclass.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: openebs-pool-block -parameters: - type: gp2 -reclaimPolicy: Retain -allowVolumeExpansion: true -provisioner: kubernetes.io/aws-ebs -volumeBindingMode: WaitForFirstConsumer -allowedTopologies: - - matchLabelExpressions: - - key: alpha.eksctl.io/nodegroup-name - values: - - storage diff --git a/helm/openneuro/templates/openebs-volume-storageclass.yaml b/helm/openneuro/templates/openebs-volume-storageclass.yaml index 6cb6f45039..415d4b5d50 100644 --- a/helm/openneuro/templates/openebs-volume-storageclass.yaml +++ b/helm/openneuro/templates/openebs-volume-storageclass.yaml @@ -3,15 +3,17 @@ kind: StorageClass metadata: name: {{ .Release.Name }}-datasets parameters: + scheduler: "VolumeWeighted" fstype: 'zfs' poolname: 'zfspv-pool' compression: 'lz4' recordsize: '16k' reclaimPolicy: Retain +volumeBindingMode: WaitForFirstConsumer allowVolumeExpansion: true provisioner: zfs.csi.openebs.io allowedTopologies: - matchLabelExpressions: - - key: alpha.eksctl.io/nodegroup-name + - key: role values: - storage diff --git a/helm/openneuro/values-production.yaml b/helm/openneuro/values-production.yaml index ffe2a210d9..04634bfa5c 100644 --- a/helm/openneuro/values-production.yaml +++ b/helm/openneuro/values-production.yaml @@ -59,7 +59,7 @@ efs-provisioner: aws-alb-ingress-controller: awsRegion: us-west-2 autoDiscoverAwsVpcID: true - clusterName: openneuro-prod + clusterName: openneuro-prod-2021 # Disable Redis password for testing redis: diff --git a/packages/openneuro-app/src/scripts/components/feature-toggle.tsx b/packages/openneuro-app/src/scripts/components/feature-toggle.tsx new file mode 100644 index 0000000000..de3a78cc78 --- /dev/null +++ b/packages/openneuro-app/src/scripts/components/feature-toggle.tsx @@ -0,0 +1,19 @@ +import React, { FC, ReactNode } from 'react' +import { useCookies } from 'react-cookie' + +interface FeatureToggleProps { + feature: string + renderOnEnabled(): ReactNode + renderOnDisabled(): ReactNode +} + +const FeatureToggle: FC = ({ + feature, + renderOnEnabled, + renderOnDisabled, +}) => { + const [cookies] = useCookies() + return <>{cookies[feature] ? renderOnEnabled() : renderOnDisabled()} +} + +export default FeatureToggle diff --git a/packages/openneuro-app/src/scripts/index.jsx b/packages/openneuro-app/src/scripts/index.jsx index 331de16ba6..6c1e2f260a 100644 --- a/packages/openneuro-app/src/scripts/index.jsx +++ b/packages/openneuro-app/src/scripts/index.jsx @@ -4,17 +4,24 @@ import React from 'react' import Navbar from './nav/navbar.jsx' import Routes from './routes.jsx' import Uploader from './uploader/uploader.jsx' +import FeatureToggle from './components/feature-toggle' const Index = () => { return ( - -
-
- - -
-
-
+

Redesign 2021

} + renderOnDisabled={() => ( + +
+
+ + +
+
+
+ )} + /> ) } diff --git a/packages/openneuro-components/package.json b/packages/openneuro-components/package.json index eed1637854..e59cc4e920 100644 --- a/packages/openneuro-components/package.json +++ b/packages/openneuro-components/package.json @@ -8,7 +8,9 @@ "license": "MIT", "dependencies": { "@mdx-js/react": "^1.6.22", + "bytes": "^3.1.0", "date-fns": "^2.21.1", + "pluralize": "^8.0.0", "rc-slider": "^9.7.2", "react": "^17.0.1", "react-router-dom": "^5.2.0", @@ -27,6 +29,7 @@ "@storybook/theming": "^6.2.8", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", + "@types/bytes": "^3", "@types/mdx-js__react": "^1", "@types/react-router-dom": "^5", "@types/react-slick": "^0", diff --git a/packages/openneuro-components/src/accordion/Accordion.stories.tsx b/packages/openneuro-components/src/accordion/Accordion.stories.tsx index 89b81556c0..2597f1a878 100644 --- a/packages/openneuro-components/src/accordion/Accordion.stories.tsx +++ b/packages/openneuro-components/src/accordion/Accordion.stories.tsx @@ -15,7 +15,7 @@ const singleTabAccordion = ( diff --git a/packages/openneuro-components/src/accordion/AccordionTab.tsx b/packages/openneuro-components/src/accordion/AccordionTab.tsx index 1aa1be0e34..580fbe7665 100644 --- a/packages/openneuro-components/src/accordion/AccordionTab.tsx +++ b/packages/openneuro-components/src/accordion/AccordionTab.tsx @@ -6,11 +6,11 @@ import './accordion.scss' export interface AccordionTabProps { children: React.ReactNode - tabId: string + id: string className: string label: string - plainStyle: boolean - startOpen: boolean + startOpen?: boolean + dropdown?: boolean accordionStyle: 'plain' | 'file-tree' | 'bids-wrappper' } @@ -19,11 +19,12 @@ export interface AccordionTabProps { */ export const AccordionTab: React.FC = ({ children, - tabId, + id, label, className, accordionStyle, startOpen, + dropdown, }) => { const [isOpen, setOpen] = React.useState(startOpen) const fileTreeIcon = accordionStyle == 'file-tree' && ( @@ -35,13 +36,16 @@ export const AccordionTab: React.FC = ({ return ( <> - +
setOpen(!isOpen)}> {fileTreeIcon} {label}
-
+
{children}
diff --git a/packages/openneuro-components/src/accordion/accordion.scss b/packages/openneuro-components/src/accordion/accordion.scss index 58cca28e97..b304678872 100644 --- a/packages/openneuro-components/src/accordion/accordion.scss +++ b/packages/openneuro-components/src/accordion/accordion.scss @@ -1,6 +1,8 @@ /* Accordion styles */ .on-accordion-wrapper { + background-color: #fff; + position: relative; .accordion-item { overflow: hidden; transition: max-height 0.3s cubic-bezier(1, 0, 1, 0); @@ -12,6 +14,15 @@ max-height: 0; transition: max-height 0.35s cubic-bezier(0, 1, 0, 1); } + .accordion-item.dropdown-style { + position: absolute; + background-color: #fff; + left: 0; + right: 0; + .accordion-content { + padding: 10px; + } + } .accordion-title { cursor: pointer; diff --git a/packages/openneuro-components/src/assets/close-button.png b/packages/openneuro-components/src/assets/close-button.png new file mode 100644 index 0000000000..31abc5963b Binary files /dev/null and b/packages/openneuro-components/src/assets/close-button.png differ diff --git a/packages/openneuro-components/src/count-toggle/CountToggle.stories.tsx b/packages/openneuro-components/src/count-toggle/CountToggle.stories.tsx index c1aa27411a..7587538cb2 100644 --- a/packages/openneuro-components/src/count-toggle/CountToggle.stories.tsx +++ b/packages/openneuro-components/src/count-toggle/CountToggle.stories.tsx @@ -13,11 +13,12 @@ const CountToggleTemplate: Story = ({ disabled, tooltip, }) => { - const [displayOptions, setDisplayOptions] = React.useState(false) + const [clicked, showClicked] = React.useState(false) const [count, setCount] = React.useState(1) - const onClick = () => { + const toggleClick = () => { setCount(count === 1 ? 2 : 1) + showClicked(!clicked) } return ( @@ -25,10 +26,10 @@ const CountToggleTemplate: Story = ({ label={label} icon={icon} disabled={disabled} - onClick={onClick} + toggleClick={toggleClick} tooltip={tooltip} - displayOptions={displayOptions} - setDisplayOptions={setDisplayOptions} + clicked={clicked} + showClicked={showClicked} count={count} /> ) diff --git a/packages/openneuro-components/src/count-toggle/CountToggle.tsx b/packages/openneuro-components/src/count-toggle/CountToggle.tsx index 0f4449493a..ef9ffdc851 100644 --- a/packages/openneuro-components/src/count-toggle/CountToggle.tsx +++ b/packages/openneuro-components/src/count-toggle/CountToggle.tsx @@ -10,10 +10,10 @@ export interface CountToggleProps { icon?: string disabled?: boolean tooltip?: string - onClick?: () => void + toggleClick?: () => void lock?: boolean - displayOptions: boolean - setDisplayOptions: (boolean) => void + clicked: boolean + showClicked: (boolean) => void count: number } export const CountToggle = ({ @@ -21,24 +21,19 @@ export const CountToggle = ({ icon, disabled, tooltip, - onClick, - displayOptions, - setDisplayOptions, + toggleClick, + clicked, count, }: CountToggleProps) => { - const toggleClick = () => { - onClick() - setDisplayOptions(!displayOptions) - } const [isOpen, setIsOpen] = React.useState(false) const toggleLogin = () => setIsOpen(!isOpen) const toggleButton = ( @@ -46,26 +41,24 @@ export const CountToggle = ({ const disabledToggleButton = ( ) - + const button = disabled ? disabledToggleButton : toggleButton return ( <>
{tooltip ? ( - {disabled ? disabledToggleButton : toggleButton} + {button} - ) : disabled ? ( - disabledToggleButton ) : ( - toggleButton + button )}
diff --git a/packages/openneuro-components/src/count-toggle/count-toggle.scss b/packages/openneuro-components/src/count-toggle/count-toggle.scss index f7d2b4e7aa..e14da1aa2f 100644 --- a/packages/openneuro-components/src/count-toggle/count-toggle.scss +++ b/packages/openneuro-components/src/count-toggle/count-toggle.scss @@ -24,7 +24,7 @@ &.active { i { - color: $current-theme-primary; + color: var(--current-theme-primary); } } } diff --git a/packages/openneuro-components/src/facets/Facet.stories.tsx b/packages/openneuro-components/src/facets/Facet.stories.tsx index 64d32e5956..ac29bea7dd 100644 --- a/packages/openneuro-components/src/facets/Facet.stories.tsx +++ b/packages/openneuro-components/src/facets/Facet.stories.tsx @@ -1,77 +1,51 @@ import React from 'react' import { Story, Meta } from '@storybook/react' -import { AccordionTab } from '../accordion/AccordionTab' -import { AccordionWrap } from '../accordion/AccordionWrap' - -import { FacetListWrap, FacetListWrapProps } from './FacetListWrap' +import { FacetSelect, FacetSelectProps } from './FacetSelect' +import { modalities } from '../mock-content/facet-content' export default { title: 'Components/Facet', - component: FacetListWrap, + component: FacetSelect, } as Meta -const FacetListWrapTemplate: Story = ({ items }) => { +const FacetSelectTemplate: Story = ({ + items, + startOpen, + label, + accordionStyle, + dropdown, +}) => { const [selected, setSelected] = React.useState() return ( -
- - - - - +
+
) } -const modalities = [ - { - label: 'MRI', - value: 'mri', - count: 30, - children: [ - { - label: 'Structural', - value: 'structural', - count: 30, - }, - { - label: 'Diffusional', - value: 'diffusional', - count: 30, - }, - ], - }, - { - label: 'PET', - value: 'pet', - count: 30, - }, - { - label: 'ASL', - value: 'asl', - count: 30, - }, - { - label: 'EEG', - value: 'eeg', - count: 30, - }, - { - label: 'ECoG', - value: 'ecog', - count: 30, - }, -] -export const FacetExample = FacetListWrapTemplate.bind({}) +export const FacetExample = FacetSelectTemplate.bind({}) FacetExample.args = { items: modalities, + accordionStyle: 'plain', + label: 'Modalities', + startOpen: true, +} + +export const FrontFacetExample = FacetSelectTemplate.bind({}) +FrontFacetExample.args = { + items: modalities, + accordionStyle: 'plain', + label: 'Browse by Modalities', + startOpen: false, + dropdown: true, } diff --git a/packages/openneuro-components/src/facets/FacetListWrap.tsx b/packages/openneuro-components/src/facets/FacetListWrap.tsx deleted file mode 100644 index fbefd8cc1c..0000000000 --- a/packages/openneuro-components/src/facets/FacetListWrap.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react' -import './facet.scss' - -export interface FacetListWrapProps { - items: [ - { - label: string - value: string - count: number - children: React.ReactNode - }, - ] - selected: { - label: string - value: string - count: number - children: React.ReactNode - } - setSelected: (selected: { label: string; value: string }) => void -} - -export const FacetListWrap = ({ - items, - selected, - setSelected, -}: FacetListWrapProps) => { - const setSetter = (e, item) => { - e.stopPropagation() // Stop this click event to trigger click on parent onClick() - setSelected(item) - } - return ( -
-
    - {items.map((item, index) => ( -
  • setSetter(e, item)} - className={ - selected && selected.value == item.value - ? 'selected-facet facet' - : 'facet' - }> - - {item.label} - {item.count && ({item.count})} - - {item.children && ( -
      - {item.children.map((item, index) => ( -
    • setSetter(e, item)} - className={ - selected && selected.value == item.value - ? 'selected-facet facet' - : 'facet' - }> - - {item.label} - {item.count && ({item.count})} - -
    • - ))} -
    - )} -
  • - ))} -
-
- ) -} diff --git a/packages/openneuro-components/src/facets/FacetRadio.tsx b/packages/openneuro-components/src/facets/FacetRadio.tsx new file mode 100644 index 0000000000..a6a890f163 --- /dev/null +++ b/packages/openneuro-components/src/facets/FacetRadio.tsx @@ -0,0 +1,53 @@ +import React from 'react' +import { AccordionTab } from '../accordion/AccordionTab' +import { AccordionWrap } from '../accordion/AccordionWrap' +import { RadioGroup } from '../radio/RadioGroup' +import './facet.scss' + +export interface FacetRadioProps { + radioArr: { + label: string + onChange?: React.MouseEventHandler + value: string + }[] + layout: string + name: string + accordionStyle: string + startOpen: boolean + label: string + dropdown?: boolean + selected: string + setSelected: (value) => void +} + +export const FacetRadio = ({ + radioArr, + layout, + name, + startOpen, + label, + accordionStyle, + dropdown, + selected, + setSelected, +}: FacetRadioProps) => { + return ( + + +
+ +
+
+
+ ) +} diff --git a/packages/openneuro-components/src/facets/FacetRange.tsx b/packages/openneuro-components/src/facets/FacetRange.tsx new file mode 100644 index 0000000000..24eb291ae0 --- /dev/null +++ b/packages/openneuro-components/src/facets/FacetRange.tsx @@ -0,0 +1,61 @@ +import React from 'react' +import { AccordionTab } from '../accordion/AccordionTab' +import { AccordionWrap } from '../accordion/AccordionWrap' +import { TwoHandleRange } from '../range/TwoHandleRange' +import './facet.scss' + +export interface FacetRangeProps { + accordionStyle: string + startOpen: boolean + label: string + dropdown?: boolean + min: number + max: number + step?: number + dots?: boolean + pushable?: boolean + defaultValue: [number, number] + marks?: { number: string } + newvalue: [number, number] + setNewValue: (newvalue) => void +} + +export const FacetRange = ({ + startOpen, + label, + accordionStyle, + dropdown, + min, + max, + step, + dots, + pushable, + defaultValue, + marks, + newvalue, + setNewValue, +}: FacetRangeProps) => { + return ( + + +
+ +
+
+
+ ) +} diff --git a/packages/openneuro-components/src/facets/FacetSelect.tsx b/packages/openneuro-components/src/facets/FacetSelect.tsx new file mode 100644 index 0000000000..6bc5c7108c --- /dev/null +++ b/packages/openneuro-components/src/facets/FacetSelect.tsx @@ -0,0 +1,87 @@ +import React from 'react' +import { AccordionTab } from '../accordion/AccordionTab' +import { AccordionWrap } from '../accordion/AccordionWrap' +import './facet.scss' + +export interface FacetSelectProps { + items: { + label: string + value: string + count?: number + children?: null | { label: string; value: string; count: number }[] + }[] + accordionStyle: string + startOpen: boolean + label: string + dropdown?: boolean + selected: { + label: string + value: string + count?: number + children?: React.ReactNode + } + setSelected: (selected: { label: string; value: string }) => void +} + +export const FacetSelect = ({ + items, + selected, + setSelected, + startOpen, + label, + accordionStyle, + dropdown, +}: FacetSelectProps) => { + const setSelectorNoPropagation = (e, item) => { + e.stopPropagation() + setSelected(item) + } + return ( + + +
+
    + {items.map((item, index) => ( +
  • setSelectorNoPropagation(e, item)} + className={ + selected && selected.value == item.value + ? 'selected-facet facet' + : 'facet' + }> + + {item.label} + {item.count && ({item.count})} + + {item.children && ( +
      + {item.children.map((item, index) => ( +
    • setSelectorNoPropagation(e, item)} + className={ + selected && selected.value == item.value + ? 'selected-facet facet' + : 'facet' + }> + + {item.label} + {item.count && ({item.count})} + +
    • + ))} +
    + )} +
  • + ))} +
+
+
+
+ ) +} diff --git a/packages/openneuro-components/src/facets/facet.scss b/packages/openneuro-components/src/facets/facet.scss index 681b86abee..0e7dc24972 100644 --- a/packages/openneuro-components/src/facets/facet.scss +++ b/packages/openneuro-components/src/facets/facet.scss @@ -3,6 +3,7 @@ .facet-accordion.on-accordion-wrapper { padding: 0 15px; border-bottom: 1px solid #aaa; + .accordion-title { font-size: 14px; font-weight: 400; @@ -15,6 +16,7 @@ color: #000; } } + .facet-list { ul { margin: 0; @@ -23,11 +25,12 @@ li { cursor: pointer; font-weight: 300; - + color: #666; + font-size: 14px; &.selected-facet { > .label, > .label span { - color: $current-theme-primary; + color: var(--current-theme-primary); } } .label { @@ -36,7 +39,7 @@ span { margin-left: 10px; display: inline-block; - color: #444; + color: #666; font-size: 14px; } } @@ -44,6 +47,8 @@ } .level-1 > li { margin-bottom: 10px; + border-top: 1px solid #dfdfdf; + padding: 10px 10px 0; } .level-2 > li { display: flex; @@ -57,4 +62,23 @@ } } } + + .facet-list ul li { + > .label { + font-weight: 500; + } + &.selected-facet > .label { + color: var(--current-theme-primary); + font-weight: bold; + + span { + color: var(--current-theme-primary); + font-weight: 400; + } + } + } +} + +.facet-range { + margin: 20px 0; } diff --git a/packages/openneuro-components/src/header/Header.tsx b/packages/openneuro-components/src/header/Header.tsx index c9172d4a78..ff9f5d889a 100644 --- a/packages/openneuro-components/src/header/Header.tsx +++ b/packages/openneuro-components/src/header/Header.tsx @@ -82,12 +82,6 @@ export const Header: React.FC = ({
{expanded ? : null}
-
- {' '} -
-
-
-
{!profile ? ( diff --git a/packages/openneuro-components/src/header/LandingExpandedHeader.tsx b/packages/openneuro-components/src/header/LandingExpandedHeader.tsx index 4008346330..93da6de19b 100644 --- a/packages/openneuro-components/src/header/LandingExpandedHeader.tsx +++ b/packages/openneuro-components/src/header/LandingExpandedHeader.tsx @@ -1,12 +1,14 @@ -import React, { useEffect, useRef } from 'react' +import React from 'react' import { Button } from '../button/Button' import { ModalityCube } from '../modality-cube/ModalityCube' +import { Input } from '../input/Input' import { cubeData } from '../mock-content/modality-cube-content.jsx' import orcidIcon from '../assets/orcid_24x24.png' import { AggregateCount } from '../aggregate-count/AggregateCount' import { frontPage } from '../mock-content/front-page-content.jsx' +import { FrontFacetExample } from '../facets/Facet.stories' export interface LandingExpandedHeaderProps { user?: {} @@ -45,6 +47,25 @@ export const LandingExpandedHeader: React.FC = ({ +
+ +
+ Or +
+
+ +
+ +
+
+
+ {!user ? (
diff --git a/packages/openneuro-components/src/header/header.scss b/packages/openneuro-components/src/header/header.scss index d7c0a227a7..5895d868bb 100644 --- a/packages/openneuro-components/src/header/header.scss +++ b/packages/openneuro-components/src/header/header.scss @@ -1,7 +1,6 @@ @import '../scss/variables'; header { position: relative; - padding-bottom: 100px; } .header-upload-btn { @@ -21,7 +20,7 @@ header { display: flex; flex-wrap: wrap; justify-content: space-between; - padding: 10px 25px 0; + padding: 10px 25px; align-items: center; background-color: $primary; @@ -184,3 +183,40 @@ header { } } } + +.header-input-wrap { + width: 90%; + margin: 20px 0; + display: flex; + justify-content: space-between; + .header-input { + flex-grow: 1; + input { + width: 100%; + padding: 16px; + } + } + .header-input-button { + width: 55px; + margin-left: 15px; + button.on-button--primary { + background-color: $on-light-aqua; + max-width: 100%; + padding: 17px; + } + } +} + +.header-or-text { + color: #fff; + font-size: 18px; + font-weight: 600; +} + +.header-modality-wrap { + width: 90%; + margin: 60px 0 20px; + .facet-accordion.on-accordion-wrapper .accordion-title { + padding: 15px 0; + } +} diff --git a/packages/openneuro-components/src/icon/icon.scss b/packages/openneuro-components/src/icon/icon.scss index a7c27e7aa0..0c08c71389 100644 --- a/packages/openneuro-components/src/icon/icon.scss +++ b/packages/openneuro-components/src/icon/icon.scss @@ -2,3 +2,7 @@ display: inline-flex; padding: 5px; } + +.on-icon img { + image-rendering: -webkit-optimize-contrast; +} diff --git a/packages/openneuro-components/src/input/Input.tsx b/packages/openneuro-components/src/input/Input.tsx index 71beb8a5e3..5dba5fe783 100644 --- a/packages/openneuro-components/src/input/Input.tsx +++ b/packages/openneuro-components/src/input/Input.tsx @@ -19,6 +19,7 @@ export const Input: React.FC = ({ name, labelStyle, setValue, + value, }) => { return ( <> @@ -27,6 +28,7 @@ export const Input: React.FC = ({ setValue(e.target.value)} /> @@ -36,6 +38,7 @@ export const Input: React.FC = ({
{label ? : null} = ({
{label ? : null} { /> ORCID users are identified and connected to their diff --git a/packages/openneuro-components/src/modal/modal.scss b/packages/openneuro-components/src/modal/modal.scss index 370f174c6d..3b565ddd39 100644 --- a/packages/openneuro-components/src/modal/modal.scss +++ b/packages/openneuro-components/src/modal/modal.scss @@ -9,14 +9,13 @@ display: flex; justify-content: center; align-items: flex-start; - transition: opacity 0.5s; + transition: opacity 0.3s; z-index: 9000; .modal { background-color: #fff; min-width: 500px; max-width: 90%; transition: transform 0.3s; - transition-delay: 0.2s; } .modal-close-x { @@ -41,8 +40,8 @@ &.hide-modal { opacity: 0; - transition-delay: 0.2s; - animation: hideModal 0.6s 1; + transition-delay: 0; + animation: hideModal 0.4s 1; animation-fill-mode: forwards; .modal { transform: translateY(-100vh); diff --git a/packages/openneuro-components/src/modality-cube/modality-cube.scss b/packages/openneuro-components/src/modality-cube/modality-cube.scss index a8b65960a5..7c0f35bae3 100644 --- a/packages/openneuro-components/src/modality-cube/modality-cube.scss +++ b/packages/openneuro-components/src/modality-cube/modality-cube.scss @@ -1,31 +1,31 @@ .modality-cube-wrap { // perspective: 2000px; // perspective-origin: 50% 50%; - margin-top: 60px; - width: 183px; - height: 160px; + margin-top: 40px; + width: 153px; + height: 130px; position: relative; .hexagon { position: absolute; - width: 180px; - height: 103.92px; - margin: -8px 0 0; - background-size: auto 207.8461px; - background-position: center; + width: 170px; + height: 98.15px; + margin: -7px 0 0; + background-size: auto 197.2991px; + background-position: center 53%; } .hexTop, .hexBottom { position: absolute; z-index: 1; - width: 127.28px; - height: 127.28px; + width: 120.21px; + height: 120.21px; overflow: hidden; -webkit-transform: scaleY(0.5774) rotate(-45deg); -ms-transform: scaleY(0.5774) rotate(-45deg); transform: scaleY(0.5774) rotate(-45deg); background: inherit; - left: 26.36px; + left: 24.9px; } /*counter transform the bg image on the caps*/ @@ -33,8 +33,8 @@ .hexBottom:after { content: ''; position: absolute; - width: 180px; - height: 103.92304845413264px; + width: 170px; + height: 98.14954576223639px; -webkit-transform: rotate(45deg) scaleY(1.7321) translateY(-51.9615px); -ms-transform: rotate(45deg) scaleY(1.7321) translateY(-51.9615px); transform: rotate(45deg) scaleY(1.7321) translateY(-51.9615px); @@ -45,7 +45,7 @@ } .hexTop { - top: -63.6396px; + top: -60.1041px; } .hexTop:after { @@ -53,7 +53,7 @@ } .hexBottom { - bottom: -63.6396px; + bottom: -60.1041px; } .hexBottom:after { @@ -65,8 +65,8 @@ position: absolute; top: 0px; left: 0; - width: 180px; - height: 103.923px; + width: 170px; + height: 98.1495px; z-index: 2; background: inherit; } @@ -89,7 +89,7 @@ transition: transform 0.4s, opacity 0.4s; transition-delay: 0s; transform: translateY(45px); - + margin-top: -20px; opacity: 0; font-weight: 600; font-size: 19px; @@ -109,8 +109,8 @@ .modality-cube { margin: 0; position: relative; - height: 123px; - width: 122px; + height: 115px; + width: 115px; transform-style: preserve-3d; transform: scale3d(1.05, 1.04, 1.04) rotateX(-36deg) rotateY(-45deg) rotateZ(0deg) translate3d(0px, 0px, 0px) skew(0deg, 0deg); @@ -125,7 +125,7 @@ color: #000; font-weight: bold; &.front { - transform: translateZ(23px); + transform: translateZ(22px); display: flex; flex-direction: column; justify-content: center; @@ -137,12 +137,12 @@ } } &.top { - transform: rotateX(-270deg) translateY(-100px); + transform: rotateX(-270deg) translateY(-93px); transform-origin: top center; background-color: rgba(0, 0, 0, 0.4); } &.right { - transform: rotateY(-270deg) translateX(100px); + transform: rotateY(-270deg) translateX(94px); transform-origin: top right; background-color: rgba(0, 0, 0, 0.2); } @@ -176,44 +176,45 @@ justify-content: space-between; .cube-block { + width: 170px; height: 240px; position: relative; &:nth-child(3n + 2) { - margin: 0 50px; + margin: 0 30px; } &:first-child { - left: -55px; + left: -5px; top: 30px; position: relative; } &:nth-child(2) { - left: -25px; + left: -5px; top: -5px; } &:nth-child(3) { top: 60px; - left: -15px; + left: 5px; } &:nth-child(4) { - left: 40px; - top: -5px; + left: 80px; + top: -25px; } &:nth-child(5) { - left: 30px; - top: -5px; + left: 55px; + top: -35px; } &:nth-child(6) { - left: 25px; + left: 5px; top: 80px; } &:nth-child(7) { - left: 0px; - top: -15px; + left: 160px; + top: -85px; } &:last-child { - left: 75px; - top: 0; + left: 155px; + top: -30px; margin-right: auto; } @media (max-width: 1400px) { diff --git a/packages/openneuro-components/src/page/Page.stories.tsx b/packages/openneuro-components/src/page/Page.stories.tsx index 2a272ed492..9020b98860 100644 --- a/packages/openneuro-components/src/page/Page.stories.tsx +++ b/packages/openneuro-components/src/page/Page.stories.tsx @@ -3,10 +3,11 @@ import { Story, Meta } from '@storybook/react' import { Page, PageProps } from './Page' import { FrontPage } from '../front-page/FrontPage' -import { SearchPage } from '../search-page/SearchPage' +import { SearchPageContainerExample } from '../search-page/SearchPageContainerExample' import * as HeaderStories from '../header/Header.stories' import { MRIPortalContent } from '../mock-content/portal-content' +import { mri } from '../mock-content/mri-search-results' export default { title: 'Example/Page', @@ -24,13 +25,20 @@ FrontPageExample.args = { export const SearchPageExample = Template.bind({}) SearchPageExample.args = { - children: , + children: ( + + ), headerArgs: HeaderStories.LoggedOut.args, className: 'search-page', } export const MRIPortalPageExample = Template.bind({}) MRIPortalPageExample.args = { - children: , + children: ( + + ), headerArgs: HeaderStories.LoggedOut.args, - className: 'search-page', + className: 'search-page search-page-mri', } diff --git a/packages/openneuro-components/src/radio/Radio.stories.tsx b/packages/openneuro-components/src/radio/Radio.stories.tsx index c966caac66..7398f84437 100644 --- a/packages/openneuro-components/src/radio/Radio.stories.tsx +++ b/packages/openneuro-components/src/radio/Radio.stories.tsx @@ -3,7 +3,7 @@ import { Story, Meta } from '@storybook/react' import { RadioGroup, RadioGroupProps } from './RadioGroup' -import { RadioContent } from '../mock-content/radio-content.jsx' +import { show_available } from '../mock-content/facet-content' export default { title: 'Components/Form/Radio', @@ -11,11 +11,11 @@ export default { } as Meta const RadioTemplate: Story = ({ radioArr, layout, name }) => { - const [active, setActive] = React.useState(0) + const [selected, setSelected] = React.useState(0) return ( = ({ radioArr, layout, name }) => { export const RowRadio = RadioTemplate.bind({}) RowRadio.args = { - radioArr: RadioContent, + radioArr: show_available, layout: 'row', name: 'radio-row', } export const ColumnRadio = RadioTemplate.bind({}) ColumnRadio.args = { - radioArr: RadioContent, + radioArr: show_available, layout: 'column', name: 'radio-column', } diff --git a/packages/openneuro-components/src/radio/RadioGroup.tsx b/packages/openneuro-components/src/radio/RadioGroup.tsx index fc6559abf0..cb4359c4ca 100644 --- a/packages/openneuro-components/src/radio/RadioGroup.tsx +++ b/packages/openneuro-components/src/radio/RadioGroup.tsx @@ -1,29 +1,26 @@ -import React, { useState } from 'react' +import React from 'react' import { Radio } from './Radio' import './radio.scss' export interface RadioGroupProps { layout: string - radioArr: [ - { - label: string - onChange?: React.MouseEventHandler - checked: boolean - value: string - }, - ] + radioArr: { + label: string + onChange?: React.MouseEventHandler + value: string + }[] name: string - active: number - setActive: (index) => void + selected: string + setSelected: (value) => void } export const RadioGroup = ({ radioArr, layout, name, - active, - setActive, + selected, + setSelected, }: RadioGroupProps) => { return (
@@ -33,8 +30,8 @@ export const RadioGroup = ({ name={name} value={item.value} label={item.label} - checked={active === index} - onChange={() => setActive(index)} + checked={selected === item.value} + onChange={e => setSelected(e.target.value)} /> ))}
diff --git a/packages/openneuro-components/src/radio/radio.scss b/packages/openneuro-components/src/radio/radio.scss index 723c592682..269a272ca0 100644 --- a/packages/openneuro-components/src/radio/radio.scss +++ b/packages/openneuro-components/src/radio/radio.scss @@ -1,6 +1,7 @@ @import '../scss/variables'; .custom-radio { + margin: 10px 0 20px; [type='radio']:checked, [type='radio']:not(:checked) { position: absolute; @@ -8,8 +9,9 @@ } [type='radio']:checked + label, [type='radio']:not(:checked) + label { + font-size: 14px; position: relative; - padding-left: 28px; + padding-left: 25px; cursor: pointer; line-height: 20px; display: inline-block; @@ -32,7 +34,7 @@ content: ''; width: 12px; height: 12px; - background: $current-theme-primary; + background: var(--current-theme-primary); position: absolute; top: 4px; left: 4px; diff --git a/packages/openneuro-components/src/range/TwoHandleRange.tsx b/packages/openneuro-components/src/range/TwoHandleRange.tsx index 85534ebe26..76012154ed 100644 --- a/packages/openneuro-components/src/range/TwoHandleRange.tsx +++ b/packages/openneuro-components/src/range/TwoHandleRange.tsx @@ -13,7 +13,7 @@ export interface TwoHandleRangeProps { defaultValue: [number, number] marks: { number: string } newvalue: [number, number] - setNewValue: [number, number] + setNewValue: (newvalue) => void } const Range = Slider.createSliderWithTooltip(Slider.Range) diff --git a/packages/openneuro-components/src/range/range.scss b/packages/openneuro-components/src/range/range.scss index d5e791da3b..870720fb87 100644 --- a/packages/openneuro-components/src/range/range.scss +++ b/packages/openneuro-components/src/range/range.scss @@ -13,14 +13,14 @@ label { font-size: 14px; margin: 40px 0 0; - color: $current-theme-primary; + color: var(--current-theme-primary); } .rc-slider-handle, .rc-slider-dot-active { - border-color: $current-theme-primary; + border-color: var(--current-theme-primary); } .rc-slider-track { - background-color: $current-theme-primary; + background-color: var(--current-theme-primary); } } diff --git a/packages/openneuro-components/src/scss/_variables.scss b/packages/openneuro-components/src/scss/_variables.scss index 4ada6fb6ee..c1cbf112d9 100644 --- a/packages/openneuro-components/src/scss/_variables.scss +++ b/packages/openneuro-components/src/scss/_variables.scss @@ -29,6 +29,8 @@ $primary: $on-dark-aqua; --asl-theme: #{$asl-theme}; --meg-theme: #{$meg-theme}; --on-light-green: #{on-light-green}; + --current-theme-primary: #{$on-dark-aqua}; + --current-theme-primary-hover: #{$on-light-aqua}; } .mri-theme { @@ -77,4 +79,7 @@ $screen-sm: 480px; $screen-md: 767px; $screen-lg: 989px; -$current-theme-primary: $on-dark-aqua; +.search-page-mri { + --current-theme-primary: #{$mri-theme}; + --current-theme-primary-hover: lighten(#{$mri-theme}, 10%); +} diff --git a/packages/openneuro-components/src/scss/global.scss b/packages/openneuro-components/src/scss/global.scss index 5730222650..2551aa8e7b 100644 --- a/packages/openneuro-components/src/scss/global.scss +++ b/packages/openneuro-components/src/scss/global.scss @@ -26,11 +26,11 @@ body { a, a:link { - color: $current-theme-primary; + color: var(--current-theme-primary); transition: color 0.3s; cursor: pointer; &:hover { - color: lighten($current-theme-primary, 15%); + color: var(--current-theme-primary-hover); } } .container { diff --git a/packages/openneuro-components/src/search-page/CommunitySwoop.tsx b/packages/openneuro-components/src/search-page/CommunitySwoop.tsx index 6a8c71822a..0dd0f92cbe 100644 --- a/packages/openneuro-components/src/search-page/CommunitySwoop.tsx +++ b/packages/openneuro-components/src/search-page/CommunitySwoop.tsx @@ -22,9 +22,6 @@ export const CommunitySwoop = ({
-
-
-
) } diff --git a/packages/openneuro-components/src/search-page/FacetBlockContainerExample.tsx b/packages/openneuro-components/src/search-page/FacetBlockContainerExample.tsx new file mode 100644 index 0000000000..501b40f314 --- /dev/null +++ b/packages/openneuro-components/src/search-page/FacetBlockContainerExample.tsx @@ -0,0 +1,5 @@ +import React from 'react' + +export const FacetBlockContainerExample = ({ children }) => { + return <>{children} +} diff --git a/packages/openneuro-components/src/search-page/FiltersBlock.tsx b/packages/openneuro-components/src/search-page/FiltersBlock.tsx new file mode 100644 index 0000000000..134470f0c7 --- /dev/null +++ b/packages/openneuro-components/src/search-page/FiltersBlock.tsx @@ -0,0 +1,66 @@ +import React from 'react' +import { Button } from '../button/Button' +import './filters-block.scss' + +export interface FiltersBlockProps { + modality?: string + datasetsType?: string + datasetStatus?: string + ageRange?: [number, number] + subjectRange?: [number, number] + author_pi?: string + gender?: string + task?: string + diagnosis?: string + section?: string + species?: string + domain?: string +} + +export const FiltersBlock = ({ + modality, + datasetsType, + datasetStatus, + ageRange, + subjectRange, + author_pi, + gender, + task, + diagnosis, + section, + species, + domain, +}: FiltersBlockProps) => { + const _listItem = (type, item) => { + return ( + <> +
  • + {type}: + {type === 'Age' || type === 'Subjects' + ? item[0] + ' - ' + item[1] + : item} + × +
  • + + ) + } + return ( +
    +
      + {modality && _listItem('Modality', modality)} + {datasetsType && _listItem('Type', datasetsType)} + {datasetStatus && _listItem('Status', datasetStatus)} + {ageRange && _listItem('Age', ageRange)} + {subjectRange && _listItem('Subjects', subjectRange)} + {author_pi && _listItem('Author/PI', author_pi)} + {gender && _listItem('Gender', gender)} + {task && _listItem('Task', task)} + {diagnosis && _listItem('Diagnosis', diagnosis)} + {section && _listItem('Section', section)} + {species && _listItem('Species', species)} + {domain && _listItem('Domain', domain)} +
    +
    + ) +} diff --git a/packages/openneuro-components/src/search-page/KeywordInputContainerExample.tsx b/packages/openneuro-components/src/search-page/KeywordInputContainerExample.tsx new file mode 100644 index 0000000000..5b7e1f38bf --- /dev/null +++ b/packages/openneuro-components/src/search-page/KeywordInputContainerExample.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { Input } from '../input/Input' + +export const KeywordInputContainerExample = ({ searchValue }) => { + const [value, setValue] = React.useState(searchValue) + return ( +
    + +
    + ) +} diff --git a/packages/openneuro-components/src/search-page/ModalityHeader.tsx b/packages/openneuro-components/src/search-page/ModalityHeader.tsx index 888a65fe63..788277d3d4 100644 --- a/packages/openneuro-components/src/search-page/ModalityHeader.tsx +++ b/packages/openneuro-components/src/search-page/ModalityHeader.tsx @@ -20,9 +20,13 @@ export const ModalityHeader = ({ swoopBackgroundColorLight, swoopBackgroundColorDark, }: ModalityHeaderProps) => { - console.log(hexBackgroundImage) return ( -
    +
    @@ -50,16 +54,6 @@ export const ModalityHeader = ({
    -
    -
    -
    -
    -
    ) } diff --git a/packages/openneuro-components/src/search-page/SearchPage.tsx b/packages/openneuro-components/src/search-page/SearchPage.tsx index ae3d5d1290..e76cb08798 100644 --- a/packages/openneuro-components/src/search-page/SearchPage.tsx +++ b/packages/openneuro-components/src/search-page/SearchPage.tsx @@ -1,16 +1,28 @@ import React from 'react' import { ModalityHeader } from './ModalityHeader' import { CommunitySwoop } from './CommunitySwoop' + +import { FacetExample } from '../facets/Facet.stories' +import { SearchResults } from './SearchResults.stories' +import { SortBy } from './SearchSort.stories' import './search-page.scss' export interface SearchPageProps { portalContent?: Record + renderSearchFacets: () => React.ReactNode + renderSearchResults: () => React.ReactNode + renderSortBy: () => React.ReactNode } -export const SearchPage = ({ portalContent }: SearchPageProps) => { +export const SearchPage = ({ + portalContent, + renderSearchFacets, + renderSearchResults, + renderSortBy, +}: SearchPageProps) => { return ( <> -
    +
    {portalContent ? ( <> {portalContent.portalName ? ( @@ -38,15 +50,22 @@ export const SearchPage = ({ portalContent }: SearchPageProps) => { ) : null} -
    -
    -
    -
    facets Todo
    -
    results todo
    +
    +
    +

    + {portalContent ? 'Search MRI Portal' : 'Search all Dataset'} +

    +
    + +
    +
    + {renderSortBy()}
    -
    -
    +
    {renderSearchFacets()}
    +
    {renderSearchResults()}
    +
    + ) } diff --git a/packages/openneuro-components/src/search-page/SearchPageContainerExample.tsx b/packages/openneuro-components/src/search-page/SearchPageContainerExample.tsx new file mode 100644 index 0000000000..b40190e6c7 --- /dev/null +++ b/packages/openneuro-components/src/search-page/SearchPageContainerExample.tsx @@ -0,0 +1,221 @@ +import React from 'react' +import { FacetBlockContainerExample } from './FacetBlockContainerExample' +import { SearchResults } from './SearchResults' +import { FiltersBlock } from './FiltersBlock' +import { SearchPage } from './SearchPage' +import { SearchSortContainerExample } from './SearchSortContainerExample' +import { KeywordInputContainerExample } from './KeywordInputContainerExample' +import { sortBy } from '../mock-content/sortby-list' +import { FacetSelect } from '../facets/FacetSelect' +import { FacetRadio } from '../facets/FacetRadio' +import { FacetRange } from '../facets/FacetRange' +import { Button } from '../button/Button' + +import { + modalities, + show_available, + dataset_type, + diagnosis_list, + task_list, + author_pi_list, + gender_list, + species_list, + section_list, + domain_list, +} from '../mock-content/facet-content' + +import './search-page.scss' + +export interface SearchContainereProps { + portalContent?: Record + searchResults + profile?: Record +} + +export const SearchPageContainerExample = ({ + searchResults, + portalContent, + profile, +}: SearchContainereProps) => { + const [modality, setModality] = React.useState() + const [datasetsType, setDatasetsType] = React.useState('all') + const [datasetStatus, setDatasetStatus] = React.useState() + const [ageRange, setAgeRange] = React.useState([0, 20]) + const [subjectRange, setSubjectRange] = React.useState([0, 20]) + const [author_pi, setAuthor_pi] = React.useState() + const [gender, setGender] = React.useState('all') + const [task, setTask] = React.useState() + const [diagnosis, setDiagnosis] = React.useState() + const [section, setSection] = React.useState(0) + const [species, setSpecies] = React.useState() + const [domain, setDomain] = React.useState() + + return ( +
    + ( + <> +
    + + 100 Datasets found for "Forrest Gump" + +
    +
    +
    + +
    +
    + + )} + renderSearchFacets={() => ( + <> + + + + + + + + {datasetsType == 2 ? ( + + ) : null} + + + + + + + + + + + + + + + + + )} + renderSearchResults={() => ( + <> + +
    + + Showing 25 of 100 Datasets found for "Forrest Gump" + +
    +
    +
    + + )} + /> +
    + ) +} diff --git a/packages/openneuro-components/src/search-page/SearchResult.stories.tsx b/packages/openneuro-components/src/search-page/SearchResult.stories.tsx new file mode 100644 index 0000000000..013138d488 --- /dev/null +++ b/packages/openneuro-components/src/search-page/SearchResult.stories.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import { Story, Meta } from '@storybook/react' +import { mri } from '../mock-content/mri-search-results' + +import { SearchResult, SearchResultProps } from './SearchResult' + +export default { + title: 'Components/SearchResult', + component: SearchResult, +} as Meta + +const SearchResultTemplate: Story = ({ node, profile }) => { + return +} + +export const Result = SearchResultTemplate.bind({}) +Result.args = { + node: mri.data.datasets.edges[0].node, +} +Result.parameters = { + layout: 'centered', +} diff --git a/packages/openneuro-components/src/search-page/SearchResult.tsx b/packages/openneuro-components/src/search-page/SearchResult.tsx new file mode 100644 index 0000000000..b4fb476b16 --- /dev/null +++ b/packages/openneuro-components/src/search-page/SearchResult.tsx @@ -0,0 +1,288 @@ +import React 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 { formatDate } from '../../../openneuro-app/src/scripts/utils/date.js' + +import { Tooltip } from '../tooltip/Tooltip' +import { Icon } from '../icon/Icon' + +import './search-result.scss' + +import activityPulseIcon from '../assets/activity-icon.png' + +export interface SearchResultProps { + node: { + id: string + created: string + uploader: { + id: string + name: string + } + public: boolean + permissions: { + id: string + userPermissions: [ + { + userId: string + level: string + access: string + user: { + id: string + name: string + email: string + provider: string + } + }, + ] + } + draft: { + id: string + summary: { + modalities: string[] + sessions: [] + subjects: string[] + subjectMetadata: [ + { + participantId: string + age: number + sex: string + group: null + }, + ] + tasks: string[] + size: number + totalFiles: number + dataProcessed: boolean + } + issues: [ + { + severity: string + }, + ] + description: { + Name: string + } + } + analytics: { + views: number + downloads: number + } + stars: [ + { + userId: string + datasetId: string + }, + ] + followers: [ + { + userId: string + datasetId: string + }, + ] + snapshots: [ + { + id: string + created: string + tag: string + }, + ] + } + profile: Record +} + +export const SearchResult = ({ node, profile }: SearchResultProps) => { + const heading = node.draft.description.Name + const summary = node.draft.summary + const numSessions = summary.sessions.length > 0 ? summary.sessions.length : 1 + const numSubjects = summary.subjects.length > 0 ? summary.subjects.length : 1 + + const accessionNumber = ( + + Openneuro Acession Number: + {node.id} + + ) + const sessions = ( + + Sessions: + {numSessions} + + ) + const subjects = ( + + Subjects: + {numSubjects} + + ) + const size = ( + + Size: + {bytes(summary.size)} + + ) + const files = ( + + Files: + {summary.totalFiles} + + ) + + const uploader = ( +
    + Uploaded by: + {node.uploader.name} +
    + ) + const dateAdded = formatDate(node.created) + const dateAddedDifference = formatDistanceToNow(parseISO(node.created)) + const dateUpdated = formatDate( + node.snapshots[node.snapshots.length - 1].created, + ) + const dateUpdatedDifference = formatDistanceToNow( + parseISO(node.snapshots[node.snapshots.length - 1].created), + ) + + const lastUpdatedDate = ( +
    + {node.snapshots.length ? ( + <> + Updated: + {dateUpdated} - {dateUpdatedDifference} ago + + ) : null} +
    + ) + const addedDate = ( +
    + Uploaded: + {dateAdded} - {dateAddedDifference} ago +
    + ) + const downloads = node.analytics.downloads + ? node.analytics.downloads + ' Downloads' + : null + const views = node.analytics.views ? node.analytics.views + ' Views' : null + const following = node.followers.length + ? node.followers.length + ' Follower' + : null + const stars = node.stars.length ? node.stars.length + ' Bookmarked' : null + + const activtyTooltip = + downloads + '\n' + views + '\n' + following + '\n' + stars + + const activityIcon = ( + + + + ) + + const sharedWithIcon = ( + + + + ) + const publicIcon = ( + + + + ) + + const errorsIcon = ( + + + + ) + + const _list = (type, items) => { + if (items && items.length > 0) { + return ( + <> + {type}: +
    + {items.map((item, index) => ( + + {item} + + ))} +
    + + ) + } else { + return null + } + } + const invalid = + !node.draft.issues || + node.draft.issues.some(issue => issue.severity === 'error') + const shared = !node.public && node.uploader.id !== profile.sub + + const datasetOwenerIcons = ( +
    + {node.public ? publicIcon : null} + {shared ? sharedWithIcon : null} + {invalid ? errorsIcon : null} +
    + ) + + const modalityList = summary.modalities ? ( +
    + {_list(<>Modalities, summary.modalities)} +
    + ) : null + + const taskList = summary.modalities ? ( +
    {_list(<>Tasks, summary.tasks)}
    + ) : null + + return ( + <> +
    +
    +

    + {heading} +

    +
    + {modalityList} + {taskList} +
    +
    +
    +
    + {datasetOwenerIcons} + {activityIcon} +
    +
    + {uploader} + {lastUpdatedDate} + {addedDate} +
    +
    +
    + {accessionNumber} + {sessions} + {subjects} + {size} + {files} +
    +
    + + ) +} diff --git a/packages/openneuro-components/src/search-page/SearchResults.stories.tsx b/packages/openneuro-components/src/search-page/SearchResults.stories.tsx new file mode 100644 index 0000000000..e11bd23674 --- /dev/null +++ b/packages/openneuro-components/src/search-page/SearchResults.stories.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { Story, Meta } from '@storybook/react' +import { mri } from '../mock-content/mri-search-results' + +import { SearchResult } from './SearchResult' + +export default { + title: 'Components/SearchResults', + component: SearchResult, +} as Meta + +const SearchResultsTemplate: Story = ({ items, profile }) => { + return ( +
    + {items.map(({ node }) => ( + + ))} +
    + ) +} + +export const SearchResults = SearchResultsTemplate.bind({}) +SearchResults.args = { + items: mri.data.datasets.edges, +} diff --git a/packages/openneuro-components/src/search-page/SearchResults.tsx b/packages/openneuro-components/src/search-page/SearchResults.tsx new file mode 100644 index 0000000000..2a62312489 --- /dev/null +++ b/packages/openneuro-components/src/search-page/SearchResults.tsx @@ -0,0 +1,19 @@ +import React from 'react' + +import { SearchResult } from './SearchResult' + +import './search-page.scss' + +export interface SearchResultsProps { + items + profile?: Record +} +export const SearchResults = ({ items, profile }: SearchResultsProps) => { + return ( +
    + {items.map(({ node }, index) => ( + + ))} +
    + ) +} diff --git a/packages/openneuro-components/src/search-page/SearchSort.stories.tsx b/packages/openneuro-components/src/search-page/SearchSort.stories.tsx index 31cf8d03d9..cedab8ae1d 100644 --- a/packages/openneuro-components/src/search-page/SearchSort.stories.tsx +++ b/packages/openneuro-components/src/search-page/SearchSort.stories.tsx @@ -1,19 +1,15 @@ import React from 'react' import { Story, Meta } from '@storybook/react' -import { SearchSort, SearchSortProps } from './SearchSort' +import { SearchSortContainerExample } from './SearchSortContainerExample' export default { title: 'Components/Search', - component: SearchSort, + component: SearchSortContainerExample, } as Meta -const SearchSortTemplate: Story = ({ items }) => { - const [selected, setSelected] = React.useState(items[0]) - - return ( - - ) +const SearchSortTemplate: Story = ({ items }) => { + return } const menuItems = [ diff --git a/packages/openneuro-components/src/search-page/SearchSort.tsx b/packages/openneuro-components/src/search-page/SearchSort.tsx index 170ac242c6..253ec55f1d 100644 --- a/packages/openneuro-components/src/search-page/SearchSort.tsx +++ b/packages/openneuro-components/src/search-page/SearchSort.tsx @@ -4,12 +4,11 @@ import '../dropdown/dropdown.scss' import './search-sort.scss' export interface SearchSortProps { - items: [ - { - label: string - value: string - }, - ] + items: { + label: string + value: string + }[] + selected: { label: string value: string diff --git a/packages/openneuro-components/src/search-page/SearchSortContainerExample.tsx b/packages/openneuro-components/src/search-page/SearchSortContainerExample.tsx new file mode 100644 index 0000000000..db9418e176 --- /dev/null +++ b/packages/openneuro-components/src/search-page/SearchSortContainerExample.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import { SearchSort } from './SearchSort' + +export const SearchSortContainerExample = ({ items }) => { + const [selected, setSelected] = React.useState(items[0]) + return ( + + ) +} diff --git a/packages/openneuro-components/src/search-page/filters-block.scss b/packages/openneuro-components/src/search-page/filters-block.scss new file mode 100644 index 0000000000..938ab9dbe2 --- /dev/null +++ b/packages/openneuro-components/src/search-page/filters-block.scss @@ -0,0 +1,47 @@ +@import '../scss/variables'; +.search-facet-wrapper { + .on-button { + display: block; + border: 1px solid; + width: 100%; + background-color: #fff; + transition: background-color 0.3s; + &:hover { + text-decoration: none; + background-color: #dfdfdf; + } + } + + .filters-block { + background-color: #eee; + padding: 30px 20px; + margin: 0 0 30px; + .active-filters { + padding: 0; + margin: 0 0 20px; + list-style: none; + display: flex; + flex-wrap: wrap; + li { + display: flex; + align-items: center; + margin-right: 30px; + margin-bottom: 10px; + font-size: 13px; + line-height: 1.7em; + + strong { + margin-right: 10px; + text-transform: uppercase; + color: #777; + } + span { + color: var(--current-theme-primary); + font-size: 22px; + margin-left: 8px; + margin-top: -1px; + } + } + } + } +} diff --git a/packages/openneuro-components/src/search-page/search-page.scss b/packages/openneuro-components/src/search-page/search-page.scss index 9ce1e30b55..edb34d34c4 100644 --- a/packages/openneuro-components/src/search-page/search-page.scss +++ b/packages/openneuro-components/src/search-page/search-page.scss @@ -1,28 +1,17 @@ +@import '../scss/variables'; + .search-page-portal-header { - margin: -200px 0 0; - padding: 145px 0 30px; - height: 290px; + // margin: -200px 0 0; + // padding: 145px 0 30px; + // height: 290px; position: relative; color: #fff; .hex-col { margin-top: 50px; + margin-bottom: -30px; position: relative; } - .swoop-wrap { - overflow: hidden; - max-width: 100%; - } - .search-swoop { - background: #444; - width: calc(100% + 40vw); - height: 550px; - border-radius: 100%; - margin: 0 0 0 -300px; - top: -100px; - position: absolute; - transform: scale(1.05) rotate(-3deg); - z-index: 1; - } + .primary-content, .secondary-content { font-weight: 300; @@ -41,23 +30,9 @@ } .search-page-coms { - overflow: hidden; position: relative; - margin: -160px 0 0; - padding: 180px 0 0; - height: 320px; - position: relative; - .search-swoop { - background: #f3f3f3; - width: calc(100% + 40vw); - height: 620px; - border-radius: 100%; - margin: 0 0 0 -160px; - top: -250px; - position: absolute; - transform: scale(1.05) rotate(-7.1deg); - z-index: -1; - } + background-color: #f3f7f6; + padding: 20px 0 40px; h2 { font-size: 19px; } @@ -172,3 +147,58 @@ } } } + +.search { + padding: 0 0 50px; +} +.search-results { + border: 1px solid #ddd; + border-radius: 4px; + .search-result { + border-bottom: 1px solid #ddd; + } +} +.search-facet-wrapper { + max-width: 500px; + .facet-accordion.on-accordion-wrapper { + padding: 0; + } +} +.search-sort { + display: flex; + justify-content: flex-end; +} + +.results-count { + padding: 20px 0; + + span { + color: var(--current-theme-primary); + } +} + +.on-accordion-wrapper .search-facets-more { + .accordion-title { + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + } +} + +.search-keyword { + margin-bottom: 30px; +} + +.load-more { + .on-button { + display: block; + border: 1px solid; + width: 100%; + background-color: #fff; + transition: background-color 0.3s; + &:hover { + text-decoration: none; + background-color: #dfdfdf; + } + } +} diff --git a/packages/openneuro-components/src/search-page/search-result.scss b/packages/openneuro-components/src/search-page/search-result.scss new file mode 100644 index 0000000000..c3e087c399 --- /dev/null +++ b/packages/openneuro-components/src/search-page/search-result.scss @@ -0,0 +1,78 @@ +@import '../scss/variables'; +.search-result { + padding: 20px; + h3 { + margin: 0 0 20px; + a { + color: var(--current-theme-primary); + &:hover { + color: var(--current-theme-primary-hover); + } + } + } + + .result-icon-wrap, + .owner-icon-wrap { + display: flex; + justify-content: flex-end; + .result-icon { + margin-left: 15px; + } + } + .result-activity-icon { + width: 18px; + height: 19px; + + &[data-tooltip]::after { + width: 142px; + line-height: 1.6em; + text-align: center; + white-space: break-spaces; + } + } + + .result-meta-body > div { + margin-bottom: 20px; + font-size: 14px; + display: flex; + + strong { + text-transform: uppercase; + color: #777; + margin-right: 20px; + } + .list-item { + color: #c83fad; + display: inline-block; + padding: 0px 12px 0 0; + margin: 0 12px 10px 0; + border-right: 1px solid #cacaca; + &:last-child { + border: 0; + } + } + } + + .result-meta-footer { + display: flex; + .result-summary-meta { + margin-right: 10px; + padding: 5px 40px 5px 0; + font-size: 14px; + font-weight: 600; + strong { + text-transform: uppercase; + color: #777; + } + } + } + + .result-upload-info { + text-align: right; + font-size: 12px; + margin-top: 10px; + span { + color: #777; + } + } +} diff --git a/packages/openneuro-components/src/search-page/search-sort.scss b/packages/openneuro-components/src/search-page/search-sort.scss index da23b4e91e..3446033f1b 100644 --- a/packages/openneuro-components/src/search-page/search-sort.scss +++ b/packages/openneuro-components/src/search-page/search-sort.scss @@ -31,6 +31,7 @@ border-radius: 6px; max-width: 400px; min-width: 150px; + background-color: #fff; li { cursor: pointer; diff --git a/packages/openneuro-components/src/select/Select.stories.tsx b/packages/openneuro-components/src/select/Select.stories.tsx index 1a448626dc..8b24203bf3 100644 --- a/packages/openneuro-components/src/select/Select.stories.tsx +++ b/packages/openneuro-components/src/select/Select.stories.tsx @@ -16,6 +16,14 @@ const SelectContent = [ { label: 'Option-4', value: 'option4' }, ] +const SelectFrontContent = [ + { label: '--Select a modality--', value: '' }, + { label: 'Option-1', value: 'option1' }, + { label: 'Option-2', value: 'option2' }, + { label: 'Option-3', value: 'option3' }, + { label: 'Option-4', value: 'option4' }, +] + const SelectTemplate: Story = ({ options, id, diff --git a/packages/openneuro-components/src/select/select.scss b/packages/openneuro-components/src/select/select.scss index e03f3f9ff3..3e739128ca 100644 --- a/packages/openneuro-components/src/select/select.scss +++ b/packages/openneuro-components/src/select/select.scss @@ -8,9 +8,10 @@ } select { padding: 20px; + background-color: #fff; background: linear-gradient(45deg, transparent 50%, #333 50%), linear-gradient(135deg, #333 50%, transparent 50%), - linear-gradient(to right, transparent, transparent); + linear-gradient(to right, #fff, #fff); background-position: calc(100% - 21px) calc(1em + 2px), calc(100% - 16px) calc(1em + 2px), 100% 0; background-size: 5px 5px, 5px 5px, 2.5em 2.5em; diff --git a/packages/openneuro-components/src/tooltip/Tooltip.tsx b/packages/openneuro-components/src/tooltip/Tooltip.tsx index 35c0881108..f1839b942d 100644 --- a/packages/openneuro-components/src/tooltip/Tooltip.tsx +++ b/packages/openneuro-components/src/tooltip/Tooltip.tsx @@ -5,9 +5,15 @@ export interface TooltipProps { tooltip: string flow: 'up' | 'down' | 'left' | 'right' children: React.ReactNode + className?: string } -export const Tooltip = ({ children, tooltip, flow }: TooltipProps) => { +export const Tooltip = ({ + children, + tooltip, + flow, + className, +}: TooltipProps) => { const reference = useRef() useEffect(() => { const placement = @@ -16,7 +22,11 @@ export const Tooltip = ({ children, tooltip, flow }: TooltipProps) => { }, []) return ( - + {children} ) diff --git a/packages/openneuro-server/src/handlers/feature-flags.js b/packages/openneuro-server/src/handlers/feature-flags.js new file mode 100644 index 0000000000..345c50fd72 --- /dev/null +++ b/packages/openneuro-server/src/handlers/feature-flags.js @@ -0,0 +1,12 @@ +const feature = { + REDESIGN_2021: 'redesign-2021', +} + +export const setFlagRedesign2021 = (req, res) => { + res.cookie(feature.REDESIGN_2021, true).redirect('/') +} + +export const unsetFlagRedesign2021 = (req, res) => { + res.clearCookie(feature.REDESIGN_2021) + res.redirect('/') +} diff --git a/packages/openneuro-server/src/routes.js b/packages/openneuro-server/src/routes.js index 11f0319f6f..d284c052de 100644 --- a/packages/openneuro-server/src/routes.js +++ b/packages/openneuro-server/src/routes.js @@ -6,6 +6,10 @@ import * as datalad from './handlers/datalad' import * as comments from './handlers/comments' import { clientConfig } from './handlers/config.js' import * as subscriptions from './handlers/subscriptions' +import { + setFlagRedesign2021, + unsetFlagRedesign2021, +} from './handlers/feature-flags' import verifyUser from './libs/authentication/verifyUser.js' import * as google from './libs/authentication/google.js' import * as orcid from './libs/authentication/orcid.js' @@ -156,6 +160,17 @@ const routes = [ url: '/sitemap', handler: sitemapHandler, }, + // feature flag setters and unsetters + { + method: 'get', + url: '/feature/redesign-2021/enable', + handler: setFlagRedesign2021, + }, + { + method: 'get', + url: '/feature/redesign-2021/disable', + handler: unsetFlagRedesign2021, + }, ] // initialize routes ------------------------------- diff --git a/yarn.lock b/yarn.lock index 155997f077..5618701100 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,36 +35,6 @@ __metadata: languageName: node linkType: hard -"@apollo/client@npm:^3.3.19": - version: 3.3.19 - resolution: "@apollo/client@npm:3.3.19" - dependencies: - "@graphql-typed-document-node/core": ^3.0.0 - "@types/zen-observable": ^0.8.0 - "@wry/context": ^0.6.0 - "@wry/equality": ^0.4.0 - fast-json-stable-stringify: ^2.0.0 - graphql-tag: ^2.12.0 - hoist-non-react-statics: ^3.3.2 - optimism: ^0.16.0 - prop-types: ^15.7.2 - symbol-observable: ^2.0.0 - ts-invariant: ^0.7.0 - tslib: ^1.10.0 - zen-observable: ^0.8.14 - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 - react: ^16.8.0 || ^17.0.0 - subscriptions-transport-ws: ^0.9.0 - peerDependenciesMeta: - react: - optional: true - subscriptions-transport-ws: - optional: true - checksum: a7266cfdff900ef71adece4d24c853fc6179dfe3d3b89905b468b0a487959a435ef84219fb1266c86e2edb0747ff0e52b6ac49dba72bdac7cdbc55e1d0a2e357 - languageName: node - linkType: hard - "@apollo/protobufjs@npm:1.2.2, @apollo/protobufjs@npm:^1.0.3": version: 1.2.2 resolution: "@apollo/protobufjs@npm:1.2.2" @@ -4880,7 +4850,6 @@ __metadata: version: 0.0.0-use.local resolution: "@openneuro/components@workspace:packages/openneuro-components" dependencies: - "@apollo/client": ^3.3.19 "@mdx-js/react": ^1.6.22 "@storybook/addon-a11y": ^6.2.8 "@storybook/addon-actions": ^6.2.8 @@ -4893,13 +4862,15 @@ __metadata: "@storybook/theming": ^6.2.8 "@testing-library/jest-dom": ^5.11.4 "@testing-library/react": ^11.1.0 + "@types/bytes": ^3 "@types/mdx-js__react": ^1 "@types/react-router-dom": ^5 "@types/react-slick": ^0 "@types/slick-carousel": ^1 + bytes: ^3.1.0 css-loader: ^5.2.1 date-fns: ^2.21.1 - graphql-hooks: ^5.1.1 + pluralize: ^8.0.0 rc-slider: ^9.7.2 react: ^17.0.1 react-router-dom: ^5.2.0 @@ -6553,6 +6524,13 @@ __metadata: languageName: node linkType: hard +"@types/bytes@npm:^3": + version: 3.1.0 + resolution: "@types/bytes@npm:3.1.0" + checksum: 184b8481789417872c8055811b4af7a41e1f5a30488ae0be8468ce0ca86f29a36a7175db9cc434599d50658ae0b709331323d97eb2a721e4ca1d2199da53a0a4 + languageName: node + linkType: hard + "@types/cheerio@npm:^0.22.22": version: 0.22.28 resolution: "@types/cheerio@npm:0.22.28" @@ -10567,7 +10545,7 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.0, bytes@npm:^3.0.0": +"bytes@npm:3.1.0, bytes@npm:^3.0.0, bytes@npm:^3.1.0": version: 3.1.0 resolution: "bytes@npm:3.1.0" checksum: c3f64645ef37922c8194fef88a052de2a28101882dfdf8a225493888c4941a26ea15164957e7492e5c5e3a8e98ee6276f4834efacb68e2d8ad4d91f903250b6c @@ -13355,13 +13333,6 @@ __metadata: languageName: node linkType: hard -"dequal@npm:^2.0.0": - version: 2.0.2 - resolution: "dequal@npm:2.0.2" - checksum: 3b5b019a873da282a25512c632c077e5e8e2a019e71c5b6968836443b861e44176ee3934facfecdc14994d553c1ff4a6c7232278d6aef87baf65e2413d41760c - languageName: node - linkType: hard - "des.js@npm:^1.0.0": version: 1.0.1 resolution: "des.js@npm:1.0.1" @@ -15644,7 +15615,7 @@ __metadata: languageName: node linkType: hard -"extract-files@npm:9.0.0, extract-files@npm:^9.0.0": +"extract-files@npm:9.0.0": version: 9.0.0 resolution: "extract-files@npm:9.0.0" checksum: 021b10787718a2f81847e58bd5eea3b09d636e8bb3cf281dad2aeeafc439c32f9e7d2353b2b4b30f6a7febf93be9d1edd76706ea56cff6850b3f39e1594e5bb3 @@ -17957,18 +17928,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"graphql-hooks@npm:^5.1.1": - version: 5.1.1 - resolution: "graphql-hooks@npm:5.1.1" - dependencies: - dequal: ^2.0.0 - extract-files: ^9.0.0 - peerDependencies: - react: ^17.0.0 - checksum: 4d615236e206e70d6f334e513243385c10c9524d50542e6ca9eee85c843c0effea7d07fa5ff84b8b15619598c46a93a5cf7d6ec979c95118147473414525e1e9 - languageName: node - linkType: hard - "graphql-iso-date@npm:^3.6.1": version: 3.6.1 resolution: "graphql-iso-date@npm:3.6.1" @@ -25038,16 +24997,6 @@ fsevents@^1.2.7: languageName: node linkType: hard -"optimism@npm:^0.16.0": - version: 0.16.1 - resolution: "optimism@npm:0.16.1" - dependencies: - "@wry/context": ^0.6.0 - "@wry/trie": ^0.3.0 - checksum: 760b1295525ba86592edf245736ed704670330a8997d91144c1b45bc9f76e392b53c170fb1eb3b265f67584a4f7e205bc777d0c8ed97774326dba9fbfe899e27 - languageName: node - linkType: hard - "optimize-css-assets-webpack-plugin@npm:^5.0.3": version: 5.0.6 resolution: "optimize-css-assets-webpack-plugin@npm:5.0.6"