From 47260eb06781e6fe4a107a4a883a27694ec1fe5c Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Thu, 7 Mar 2024 11:33:53 +0100 Subject: [PATCH] Use KeyValueTable component to display metadata --- .../__tests__/components/DatasetCard.test.jsx | 4 +- .../src/components/CellPatchingCard.jsx | 54 ++++++---------- apps/nar-v3/src/components/DataFileCard.jsx | 34 +++++----- apps/nar-v3/src/components/KeyValueTable.jsx | 42 +++++++++++++ .../nar-v3/src/components/PatchedCellCard.jsx | 9 +-- apps/nar-v3/src/components/RecordingCard.jsx | 62 ++++++++----------- apps/nar-v3/src/components/SliceCard.jsx | 11 ++-- .../src/components/SlicePreparationCard.jsx | 35 +++++------ apps/nar-v3/src/components/SubjectCard.jsx | 40 +++++------- 9 files changed, 148 insertions(+), 143 deletions(-) create mode 100644 apps/nar-v3/src/components/KeyValueTable.jsx diff --git a/apps/nar-v3/__tests__/components/DatasetCard.test.jsx b/apps/nar-v3/__tests__/components/DatasetCard.test.jsx index 5862354..9a4634b 100644 --- a/apps/nar-v3/__tests__/components/DatasetCard.test.jsx +++ b/apps/nar-v3/__tests__/components/DatasetCard.test.jsx @@ -2,10 +2,8 @@ import { describe, test, expect } from "vitest"; import { render, screen } from "@testing-library/react"; import "@testing-library/jest-dom"; - import DatasetCard from "../../src/components/DatasetCard"; - describe("DatasetCard component", () => { test("should render without errors", async () => { const dataset = { @@ -545,7 +543,7 @@ describe("DatasetCard component", () => { ], biologicalSex: "male", }, - ] + ], }; render(); diff --git a/apps/nar-v3/src/components/CellPatchingCard.jsx b/apps/nar-v3/src/components/CellPatchingCard.jsx index cc70a42..886860d 100644 --- a/apps/nar-v3/src/components/CellPatchingCard.jsx +++ b/apps/nar-v3/src/components/CellPatchingCard.jsx @@ -21,12 +21,31 @@ import Box from "@mui/material/Box"; import Paper from "@mui/material/Paper"; import Connection from "./Connection"; +import KeyValueTable from "./KeyValueTable"; import styles from "../styles"; import { formatQuant } from "../utility"; function CellPatchingCard(props) { const activity = props.activity; + const data = { + "Electrode description": activity.device[0].device.description, + "Pipette solution (more details to come)": activity.device[0].pipetteSolution.name, + "Seal resistance": activity.device[0].sealResistance.value + .map((item) => formatQuant(item)) + .join(", "), + "Series resistance": activity.device[0].seriesResistance.value + .map((item) => formatQuant(item)) + .join(", "), + "Holding potential": activity.device[0].holdingPotential.value + .map((item) => formatQuant(item)) + .join(", "), + "Bath solution (more details to come)": activity.tissueBathSolution.name, + "Bath temperature": formatQuant(activity.bathTemperature), + Description: activity.description, + Type: activity.variation, + }; + if (activity) { return ( <> @@ -34,40 +53,7 @@ function CellPatchingCard(props) {

Cell patching

{activity.label}

- -
-
Electrode description
-
{activity.device[0].device.description}
- {/* activity.device[0].device.deviceType.name */} - {/* activity.device[0].device.manufacturer.fullName */} -
Pipette solution (more details to come)
-
{activity.device[0].pipetteSolution.name}
-
Seal resistance
-
- {activity.device[0].sealResistance.value.map((item) => formatQuant(item)).join(", ")} -
-
Series resistance
-
- {activity.device[0].seriesResistance.value - .map((item) => formatQuant(item)) - .join(", ")} -
-
Holding potential
-
- {activity.device[0].holdingPotential.value - .map((item) => formatQuant(item)) - .join(", ")} -
- -
Bath solution (more details to come)
-
{activity.tissueBathSolution.name}
-
Bath temperature
-
{formatQuant(activity.bathTemperature)}
-
Description
-
{activity.description}
-
Type
-
{activity.variation}
-
+
); diff --git a/apps/nar-v3/src/components/DataFileCard.jsx b/apps/nar-v3/src/components/DataFileCard.jsx index 5fe8f99..78c99e0 100644 --- a/apps/nar-v3/src/components/DataFileCard.jsx +++ b/apps/nar-v3/src/components/DataFileCard.jsx @@ -21,33 +21,35 @@ import Paper from "@mui/material/Paper"; import { formatQuant } from "../utility"; import Connection from "./Connection"; +import KeyValueTable from "./KeyValueTable"; import styles from "../styles"; function DataFileCard(props) { const fileObj = props.fileObj; + const data = { + "Data type": fileObj.dataType ? fileObj.dataType.name : "unknown", + Format: fileObj.format ? fileObj.format.name : "unknown", + Hash: ( + <> + {fileObj.hash.map((item) => ( + + {item.algorithm}: {item.digest}  + + ))} + + ), + Size: formatQuant(fileObj.storageSize), + }; + if (fileObj) { return ( <>

File {fileObj.name}

-
-
Data type
-
{fileObj.dataType ? fileObj.dataType.name : "unknown"}
-
Format
-
{fileObj.format ? fileObj.format.name : "unknown"}
-
Hash
-
- {fileObj.hash.map((item) => ( - - {item.algorithm}: {item.digest}  - - ))} -
-
Size
-
{formatQuant(fileObj.storageSize)}
-
+ +
); diff --git a/apps/nar-v3/src/components/KeyValueTable.jsx b/apps/nar-v3/src/components/KeyValueTable.jsx new file mode 100644 index 0000000..e7d6415 --- /dev/null +++ b/apps/nar-v3/src/components/KeyValueTable.jsx @@ -0,0 +1,42 @@ +import { isValidElement } from "react"; +import { Table, TableBody, TableRow, TableCell } from "@mui/material"; + +function KeyValueTable(props) { + let formatKey = (key) => { + return key; + }; + if (props.boldKeys) { + formatKey = (key) => { + return {key}; + }; + } + + let rows = []; + if (props.data) { + for (const [key, value] of Object.entries(props.data)) { + let valueStr = value; + if (!isValidElement(value)) { + // allow passing JSX as values + valueStr = String(value); + if (Array.isArray(value)) { + valueStr = value.join(", "); + } + } + + rows.push( + + {formatKey(key)} + {valueStr} + + ); + } + } + + return ( + + {rows} +
+ ); +} + +export default KeyValueTable; diff --git a/apps/nar-v3/src/components/PatchedCellCard.jsx b/apps/nar-v3/src/components/PatchedCellCard.jsx index f6634dc..4fb75c3 100644 --- a/apps/nar-v3/src/components/PatchedCellCard.jsx +++ b/apps/nar-v3/src/components/PatchedCellCard.jsx @@ -20,6 +20,7 @@ import Box from "@mui/material/Box"; import Paper from "@mui/material/Paper"; import Connection from "./Connection"; +import KeyValueTable from "./KeyValueTable"; import styles from "../styles"; function PatchedCellCard(props) { @@ -27,15 +28,15 @@ function PatchedCellCard(props) { const cell = props.cell.cell; if (cell) { + const data = { + Location: cell.anatomicalLocation.map((item) => item.name).join(", "), + }; return ( <>

Patched cell #{cell.internalIdentifier}

-
-
Location
-
{cell.anatomicalLocation.map((item) => item.name).join(", ")}
-
+
); diff --git a/apps/nar-v3/src/components/RecordingCard.jsx b/apps/nar-v3/src/components/RecordingCard.jsx index 4beef45..9c50a38 100644 --- a/apps/nar-v3/src/components/RecordingCard.jsx +++ b/apps/nar-v3/src/components/RecordingCard.jsx @@ -17,11 +17,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Fragment } from "react"; import Box from "@mui/material/Box"; import Paper from "@mui/material/Paper"; import Connection from "./Connection"; +import KeyValueTable from "./KeyValueTable"; import styles from "../styles"; import { formatQuant, formatUnits } from "../utility"; @@ -30,6 +30,26 @@ function RecordingCard(props) { const stimulation = props.stimulation; if (recording) { + const recordingData = { + Description: recording.description, + "Additional remarks": recording.device.metadata.additionalRemarks, + "Sampling frequency": formatQuant(recording.device.metadata.samplingFrequency), + Channels: ( +
    + {recording.device.metadata.channel.map((item) => ( +
  • + {item.internalIdentifier} ({formatUnits(item.unit)}) +
  • + ))} +
+ ), + }; + const stimulationData = { + Type: "Current injection", + Description: stimulation.stimulus[0].lookupLabel, + "Epoch duration": formatQuant(stimulation.stimulus[0].epoch), + Identifier: stimulation.stimulus[0].internalIdentifier, + }; const stimulusSpec = JSON.parse(stimulation.stimulus[0].specification.configuration); return ( @@ -38,46 +58,14 @@ function RecordingCard(props) {

Recording

{recording.label}

-
-
Description
-
{recording.description}
-
Additional remarks
-
{recording.device.metadata.additionalRemarks}
-
Sampling frequency
-
{formatQuant(recording.device.metadata.samplingFrequency)}
-
Channels
-
-
    - {recording.device.metadata.channel.map((item) => ( -
  • - {item.internalIdentifier} ({formatUnits(item.unit)}) -
  • - ))} -
-
-
+

Stimulation

{stimulation.label}

-
-
Type
-
Current injection
-
Description
-
{stimulation.stimulus[0].lookupLabel}
-
Epoch duration
-
{formatQuant(stimulation.stimulus[0].epoch)}
-
Identifier
-
{stimulation.stimulus[0].internalIdentifier}
-
+ +

Specification

-
- {Object.entries(stimulusSpec).map((item, index) => ( - -
{item[0]}
-
{item[1]}
-
- ))} -
+
); diff --git a/apps/nar-v3/src/components/SliceCard.jsx b/apps/nar-v3/src/components/SliceCard.jsx index 1ef2375..49e2e87 100644 --- a/apps/nar-v3/src/components/SliceCard.jsx +++ b/apps/nar-v3/src/components/SliceCard.jsx @@ -21,6 +21,7 @@ import Paper from "@mui/material/Paper"; import Stack from "@mui/material/Stack"; import Connection from "./Connection"; +import KeyValueTable from "./KeyValueTable"; import { NavigateNext, NavigatePrevious } from "./Navigation"; import styles from "../styles"; @@ -29,6 +30,11 @@ function SliceCard(props) { const slice = props.slices[props.index].slice; if (slice) { + const data = { + "Location (todo: add link outs)": slice.anatomicalLocation + .map((item) => item.name) + .join(", "), + }; return ( <> @@ -45,10 +51,7 @@ function SliceCard(props) {

({props.index + 1} of {props.slices.length})

-
-
Location (todo: add link outs)
-
{slice.anatomicalLocation.map((item) => item.name).join(", ")}
-
+ {props.index < props.slices.length - 1 ? ( diff --git a/apps/nar-v3/src/components/SlicePreparationCard.jsx b/apps/nar-v3/src/components/SlicePreparationCard.jsx index 44f8c86..5a19cfb 100644 --- a/apps/nar-v3/src/components/SlicePreparationCard.jsx +++ b/apps/nar-v3/src/components/SlicePreparationCard.jsx @@ -22,39 +22,32 @@ import Paper from "@mui/material/Paper"; import { formatQuant } from "../utility"; import Connection from "./Connection"; +import KeyValueTable from "./KeyValueTable"; import styles from "../styles"; function SlicePreparationCard(props) { const activity = props.activity; if (activity) { + const data = { + "Device name": activity.device[0].device.name, + "Device type": activity.device[0].device.deviceType, + Manufacturer: + activity.device[0].device.manufacturer.fullName || + activity.device[0].device.manufacturer.shortName, + "Slice thickness": formatQuant(activity.device[0].sliceThickness), + "Slicing plane": activity.device[0].slicingPlane, + "Study targets": activity.studyTarget.join(", "), + Temperature: formatQuant(activity.temperature), + "Dissecting solution (full details to come)": activity.tissueBathSolution.name, + }; return ( <>

Slice preparation

{activity.label}

-
-
Device name
-
{activity.device[0].device.name}
-
Device type
-
{activity.device[0].device.deviceType}
-
Manufacturer
-
- {activity.device[0].device.manufacturer.fullName || - activity.device[0].device.manufacturer.shortName} -
-
Slice thickness
-
{formatQuant(activity.device[0].sliceThickness)}
-
Slicing plane
-
{activity.device[0].slicingPlane}
-
Study targets
-
{activity.studyTarget.join(", ")}
-
Temperature
-
{formatQuant(activity.temperature)}
-
Dissecting solution (full details to come)
-
{activity.tissueBathSolution.name}
-
+
); diff --git a/apps/nar-v3/src/components/SubjectCard.jsx b/apps/nar-v3/src/components/SubjectCard.jsx index 6b247d8..e9a676b 100644 --- a/apps/nar-v3/src/components/SubjectCard.jsx +++ b/apps/nar-v3/src/components/SubjectCard.jsx @@ -22,6 +22,7 @@ import Stack from "@mui/material/Stack"; import { formatUnits } from "../utility"; import { NavigateNext, NavigatePrevious } from "./Navigation"; +import KeyValueTable from "./KeyValueTable"; import styles from "../styles"; function AgeDisplay(props) { @@ -60,6 +61,20 @@ function SubjectCard(props) { } } + const data = { + Species: species, + Strain: strain, + Age: , + "Age category": subject.studiedState[0].ageCategory, + Pathologies: + subject.studiedState[0].pathology.length > 0 + ? subject.studiedState[0].pathology[0].name + : "none", + }; + if (!strain) { + delete data.Strain; + } + return ( @@ -75,30 +90,7 @@ function SubjectCard(props) { ({props.index + 1} of {props.subjects.length})

{/* todo: add subject group information */} -
-
Species
-
{species}
- {strain ? ( - <> -
Strain
-
{strain}
- - ) : ( - "" - )} -
Age
-
- -
-
Age category
-
{subject.studiedState[0].ageCategory}
-
Pathologies
-
- {subject.studiedState[0].pathology.length > 0 - ? subject.studiedState[0].pathology[0].name - : "none"} -
-
+ {props.index < props.subjects.length - 1 ? (