diff --git a/src/components/explorer/IndividualBiosamples.js b/src/components/explorer/IndividualBiosamples.js
index 0ca23c750..5f11baff0 100644
--- a/src/components/explorer/IndividualBiosamples.js
+++ b/src/components/explorer/IndividualBiosamples.js
@@ -17,6 +17,7 @@ import JsonView from "./JsonView";
import OntologyTerm from "./OntologyTerm";
import "./explorer.css";
+import TimeElement from "./TimeElement";
// TODO: Only show biosamples from the relevant dataset, if specified;
// highlight those found in search results, if specified
@@ -30,6 +31,12 @@ const BiosampleProcedure = ({ resourcesTuple, procedure }) => (
) : null}
+ {procedure.performed ? (
+
+ Time Performed:{" "}
+
+
+ ): null}
);
BiosampleProcedure.propTypes = {
@@ -62,6 +69,7 @@ ExperimentsClickList.propTypes = {
const BiosampleDetail = ({ individual, biosample, handleExperimentClick }) => {
const resourcesTuple = useIndividualResources(individual);
+ console.log(biosample.procedure);
return (
@@ -85,12 +93,8 @@ const BiosampleDetail = ({ individual, biosample, handleExperimentClick }) => {
-
- {biosample.individual_age_at_collection
- ? biosample.individual_age_at_collection.age ??
- `Between ${biosample.individual_age_at_collection.start.age}` +
- `and ${biosample.individual_age_at_collection.end.age}`
- : EM_DASH}
+
+
{biosample.hasOwnProperty("measurements") &&
diff --git a/src/components/explorer/TimeElement.js b/src/components/explorer/TimeElement.js
new file mode 100644
index 000000000..b8a0463c4
--- /dev/null
+++ b/src/components/explorer/TimeElement.js
@@ -0,0 +1,82 @@
+import React, { memo, useEffect } from "react";
+import PropTypes from "prop-types";
+
+import { EM_DASH } from "../../constants";
+import OntologyTerm from "./OntologyTerm";
+
+const TIME_ELEMENT_TYPES_LABELS = {
+ "age": "Age",
+ "gestational_age": "Gestational Age",
+ "age_range": "Age Range",
+ "ontology_class": "Ontology Class",
+ "timestamp": "Timestamp",
+ "interval": "Interval",
+}
+
+const getTimeElementTypeLabel = (timeElement) => {
+ const keys = Object.keys(timeElement);
+ if (keys ?? keys.length === 1) {
+ // A Phenopacket TimeElement should only have 1 property
+ const type = keys[0];
+ if (type in TIME_ELEMENT_TYPES_LABELS) {
+ const label = TIME_ELEMENT_TYPES_LABELS[type];
+ return [type, label];
+ }
+ }
+ return [null, "NOT_SUPPORTED"];
+}
+
+const renderTimeElement = (type, timeElement) => {
+ switch (type) {
+ case "age":
+ return <>{timeElement.age.iso8601duration}>;
+ case "gestational_age":
+ return <>
+ Weeks:{" "}{timeElement.gestationalAge.weeks}
+ Days:{" "}{timeElement.gestationalAge.days}
+ >;
+ case "age_range":
+ return <>
+ Start:{" "}<>{timeElement.age_range.start.iso8601duration}>
+ End:{" "}<>{timeElement.age_range.end.iso8601duration}>
+ >;
+ case "ontology_class":
+ return <>
+ ID:{" "}{timeElement.ontology_class.id}
+ Label:{" "}{timeElement.ontology_class.label}
+ >;
+ case "timestamp":
+ return <>
+ Timestamp:{" "}{timeElement.timestamp}
+ >;
+ case "interval":
+ return <>
+ Start:{" "}<>{timeElement.interval.start}>
+ End:{" "}<>{timeElement.interval.end}>
+ >;
+ default:
+ return EM_DASH;
+ }
+}
+
+const TimeElement = ({timeElement}) => {
+ const [timeType, label] = getTimeElementTypeLabel(timeElement);
+
+ if (!timeType) {
+ // Unexpected TimeElement type
+ return EM_DASH;
+ }
+
+ return (
+
+ {label}:
+ {renderTimeElement(timeType, timeElement)}
+
+ );
+}
+
+TimeElement.propTypes = {
+ timeElement: PropTypes.object,
+};
+
+export default TimeElement;