From 3bc74c42247dcddcec3afc0e4b711b316e0771ea Mon Sep 17 00:00:00 2001 From: yukthiw Date: Sat, 30 Dec 2023 14:24:57 -0500 Subject: [PATCH 01/15] feat: Added citation components --- Eplant/UI/Layout/ViewContainer/index.tsx | 27 ++++++------ Eplant/View/index.ts | 2 +- Eplant/state/index.tsx | 52 +++++++++++++++--------- Eplant/util/citations/index.tsx | 8 ++++ Eplant/views/PlantEFP/index.tsx | 4 +- Eplant/views/eFP/Viewer/index.tsx | 25 ++++++++++-- 6 files changed, 80 insertions(+), 38 deletions(-) create mode 100644 Eplant/util/citations/index.tsx diff --git a/Eplant/UI/Layout/ViewContainer/index.tsx b/Eplant/UI/Layout/ViewContainer/index.tsx index daef2a6d..62c75293 100644 --- a/Eplant/UI/Layout/ViewContainer/index.tsx +++ b/Eplant/UI/Layout/ViewContainer/index.tsx @@ -194,7 +194,10 @@ export function ViewContainer({ {view.citation ? ( - + ) : ( No information provided for this view )} @@ -215,17 +218,17 @@ export function ViewContainer({ flexDirection: 'column', ...(printing == viewId ? { - display: 'block !important', - padding: 0, - position: 'fixed', - left: 0, - top: 0, - margin: 0, - zIndex: 1e9, - background: theme.palette.background.paper, - width: '100%', - minHeight: '100%', - } + display: 'block !important', + padding: 0, + position: 'fixed', + left: 0, + top: 0, + margin: 0, + zIndex: 1e9, + background: theme.palette.background.paper, + width: '100%', + minHeight: '100%', + } : {}), })} > diff --git a/Eplant/View/index.ts b/Eplant/View/index.ts index 339b5662..eba2a6bc 100644 --- a/Eplant/View/index.ts +++ b/Eplant/View/index.ts @@ -58,7 +58,7 @@ export interface View { /** * The react component that renders citations for a gene */ - citation?: (props: { gene: GeneticElement | null }) => JSX.Element + citation?: (props: { state?: State, activeData?: Data, gene?: GeneticElement | null }) => JSX.Element /** * Return the title of this view's tab */ diff --git a/Eplant/state/index.tsx b/Eplant/state/index.tsx index 54500814..178c6f2c 100644 --- a/Eplant/state/index.tsx +++ b/Eplant/state/index.tsx @@ -56,17 +56,17 @@ export function atomWithStorage( if (e) setAtom(e) else setAtom(initialValue) } - ;(async () => { - const finished = loading?.() - try { - const val = await loadedValue - if (val) { - setAtom(val) + ; (async () => { + const finished = loading?.() + try { + const val = await loadedValue + if (val) { + setAtom(val) + } + } finally { + if (finished) finished() } - } finally { - if (finished) finished() - } - })() + })() return storage.watch(key, listener) } const a = atom( @@ -101,17 +101,17 @@ function atomWithOptionalStorage( if (e) setAtom(deserialize(e)) else setAtom(initialValue) } - ;(async () => { - pageLoad.start() - try { - const val = await loadedValue - if (val) { - setAtom(deserialize(val)) + ; (async () => { + pageLoad.start() + try { + const val = await loadedValue + if (val) { + setAtom(deserialize(val)) + } + } finally { + pageLoad.done() } - } finally { - pageLoad.done() - } - })() + })() return storage.watch(key, listener) } const a = atom( @@ -147,6 +147,18 @@ export const genesAtom = atomWithOptionalStorage( export const useGeneticElements = () => useAtom(genesAtom) export const useSetGeneticElements = () => useSetAtom(genesAtom) +export const fetchCitations = async () => { + try { + const response = await fetch('https://bar.utoronto.ca/eplant/data/citations.json') + const data = await response.json() + return data + } catch (error) { + console.error('Error fetching citations:', error) + } +} +export const citationsAtom = atom(await fetchCitations()) +export const useCitations = () => useAtom(citationsAtom) + export const collectionsAtom = atomWithOptionalStorage< { genes: string[]; name: string; open: boolean }[] >( diff --git a/Eplant/util/citations/index.tsx b/Eplant/util/citations/index.tsx new file mode 100644 index 00000000..c341386d --- /dev/null +++ b/Eplant/util/citations/index.tsx @@ -0,0 +1,8 @@ +import GeneticElement from "@eplant/GeneticElement" +import { citationsAtom, useCitations } from "@eplant/state" +import { useAtomValue } from "jotai" + +export const getCitation = (viewName: string) => { + const [citations] = useCitations() + return citations.find((citation: { [key: string]: string }) => citation.view === viewName) +} diff --git a/Eplant/views/PlantEFP/index.tsx b/Eplant/views/PlantEFP/index.tsx index 96fa1c6a..2e107523 100644 --- a/Eplant/views/PlantEFP/index.tsx +++ b/Eplant/views/PlantEFP/index.tsx @@ -8,7 +8,7 @@ import { EFPViewerData } from '../eFP/Viewer/types' const views: EFPViewerData['views'] = [ { - name: 'AtGenExpress', + name: 'AtGenExpress eFP', id: 'atgenexpress', svgURL: 'https://bar.utoronto.ca/eplant/data/plant/AtGenExpress/Arabidopsis_thaliana.svg', @@ -16,7 +16,7 @@ const views: EFPViewerData['views'] = [ 'https://bar.utoronto.ca/eplant/data/plant/AtGenExpress/Arabidopsis_thaliana.xml', }, { - name: 'Klepikova', + name: 'Klepikova eFP', id: 'klepikova', svgURL: 'https://bar.utoronto.ca/eplant/data/plant/Klepikova/Arabidopsis_thaliana.svg', diff --git a/Eplant/views/eFP/Viewer/index.tsx b/Eplant/views/eFP/Viewer/index.tsx index 53fc54c5..88cd18af 100644 --- a/Eplant/views/eFP/Viewer/index.tsx +++ b/Eplant/views/eFP/Viewer/index.tsx @@ -18,6 +18,8 @@ import Legend from './legend' import NotSupported from '@eplant/UI/Layout/ViewNotSupported' import Dropdown from '@eplant/UI/Dropdown' import GeneDistributionChart from './GeneDistributionChart' +import { useCitations } from '@eplant/state' +import { getCitation } from '@eplant/util/citations' type EFPListProps = { geneticElement: GeneticElement @@ -126,7 +128,7 @@ export default class EFPViewer public icon: () => JSX.Element, public description?: string, public thumbnail?: string, - ) {} + ) { } getInitialData = async ( gene: GeneticElement | null, loadEvent: (progress: number) => void, @@ -240,7 +242,7 @@ export default class EFPViewer renderAsThumbnail: false, }} geneticElement={props.geneticElement} - dispatch={() => {}} + dispatch={() => { }} /> ) }, [ @@ -289,7 +291,7 @@ export default class EFPViewer }} > {/* Dropdown menus for selecting a view and sort options - + //TODO: Make the dropdown menus appear closer to the button, left aligned and with a max height */} ) + + citation = (props: { activeData?: EFPViewerData, state?: EFPViewerState, gene?: GeneticElement | null }) => { + const viewID = props.activeData?.views.find((v) => v.id == props.state?.activeView)?.name + if (viewID) { + const citation = getCitation(viewID) + console.log(citation) + return ( +
+

{citation.source ? citation.source : ""} +

{citation.notes ? citation.notes : ""} +

{citation.URL ? citation.notes : ""} +
+ ) + } else { + return
+ } + } } From 68081a15c3fe511ef64f54f8a582f9e04b2341fe Mon Sep 17 00:00:00 2001 From: yukthiw Date: Tue, 2 Jan 2024 13:52:26 -0500 Subject: [PATCH 02/15] changed view names to match those used by BAR api, added citations for efps --- Eplant/views/CellEFP/index.tsx | 2 +- Eplant/views/PlantEFP/index.tsx | 2 +- Eplant/views/eFP/Viewer/index.tsx | 57 +++++++++++++++++++++++++++---- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Eplant/views/CellEFP/index.tsx b/Eplant/views/CellEFP/index.tsx index a446c980..8bbc7ab7 100644 --- a/Eplant/views/CellEFP/index.tsx +++ b/Eplant/views/CellEFP/index.tsx @@ -8,7 +8,7 @@ import { makeCellEfps } from '../eFP/Viewer/util' const views: EFPViewerData['views'] = [ { - name: 'cellEFP', + name: 'Cell eFP', id: 'cellefp', svgURL: 'https://bar.utoronto.ca/eplant/data/cell/Arabidopsis_thaliana.svg', xmlURL: 'https://bar.utoronto.ca/eplant/data/cell/Arabidopsis_thaliana.xml', diff --git a/Eplant/views/PlantEFP/index.tsx b/Eplant/views/PlantEFP/index.tsx index 2e107523..cda6dec8 100644 --- a/Eplant/views/PlantEFP/index.tsx +++ b/Eplant/views/PlantEFP/index.tsx @@ -16,7 +16,7 @@ const views: EFPViewerData['views'] = [ 'https://bar.utoronto.ca/eplant/data/plant/AtGenExpress/Arabidopsis_thaliana.xml', }, { - name: 'Klepikova eFP', + name: 'Klepikova eFP (RNA-Seq data)', id: 'klepikova', svgURL: 'https://bar.utoronto.ca/eplant/data/plant/Klepikova/Arabidopsis_thaliana.svg', diff --git a/Eplant/views/eFP/Viewer/index.tsx b/Eplant/views/eFP/Viewer/index.tsx index 88cd18af..95439ba5 100644 --- a/Eplant/views/eFP/Viewer/index.tsx +++ b/Eplant/views/eFP/Viewer/index.tsx @@ -3,7 +3,7 @@ import PanZoom from '@eplant/util/PanZoom' import { View, ViewProps } from '@eplant/View' import { ViewDataError } from '@eplant/View/viewData' import { Box, MenuItem, Tooltip, Typography } from '@mui/material' -import React, { startTransition } from 'react' +import React, { startTransition, useEffect, useMemo, useState } from 'react' import EFP from '..' import EFPPreview from '../EFPPreview' import { EFPViewerAction, EFPViewerData, EFPViewerState } from './types' @@ -442,19 +442,64 @@ export default class EFPViewer ) citation = (props: { activeData?: EFPViewerData, state?: EFPViewerState, gene?: GeneticElement | null }) => { + const [xmlData, setXMLData] = useState([]) + const viewID = props.activeData?.views.find((v) => v.id == props.state?.activeView)?.name + const viewXML = props.activeData?.views.find((v) => v.id == props.state?.activeView)?.xmlURL + useEffect(() => { + const xmlLoad = async () => { + let xmlString = "" + if (viewXML) { + try { + const response = await fetch(viewXML) + const data = await response.text() + xmlString = data + } catch (error) { + console.error('Error fetching xmlData:', error) + } + } + + // Extract
  • tags + if (xmlString !== "") { + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(xmlString, 'text/xml'); + const listItems = xmlDoc.querySelectorAll('info li'); + const itemsArray = Array.from(listItems).map((liElement) => liElement.textContent ? liElement.textContent : ""); + setXMLData(itemsArray) + } else { + setXMLData([]) + } + } + xmlLoad() + }, [viewXML]) + if (viewID) { const citation = getCitation(viewID) - console.log(citation) + // Need to set inner HTML to capture nested HTML tags in some citations return (
    -

    {citation.source ? citation.source : ""} -

    {citation.notes ? citation.notes : ""} -

    {citation.URL ? citation.notes : ""} +
    +

    +

    {citation.URL ? citation.URL : ""} + { + xmlData.length > 0 ? + (
      + {xmlData.map((item, index) => { + if (item) { + return (
    • {item}
    • ) + } + } + )} +
    + ) : + <> + } +

    This image was generated with the {viewID} at bar.utoronto.ca/eplant by Waese et al. 2017. + Creative Commons License
    ) } else { - return
    + return
    No Citation information provided.
    } } } From c596622c5c4b8742da1199980bb41bb35700171b Mon Sep 17 00:00:00 2001 From: yukthiw Date: Tue, 2 Jan 2024 20:39:50 -0500 Subject: [PATCH 03/15] Updated Link tag --- Eplant/views/eFP/Viewer/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Eplant/views/eFP/Viewer/index.tsx b/Eplant/views/eFP/Viewer/index.tsx index 95439ba5..b4ed15f6 100644 --- a/Eplant/views/eFP/Viewer/index.tsx +++ b/Eplant/views/eFP/Viewer/index.tsx @@ -2,7 +2,7 @@ import GeneticElement from '@eplant/GeneticElement' import PanZoom from '@eplant/util/PanZoom' import { View, ViewProps } from '@eplant/View' import { ViewDataError } from '@eplant/View/viewData' -import { Box, MenuItem, Tooltip, Typography } from '@mui/material' +import { Box, Link, MenuItem, Tooltip, Typography } from '@mui/material' import React, { startTransition, useEffect, useMemo, useState } from 'react' import EFP from '..' import EFPPreview from '../EFPPreview' @@ -495,7 +495,7 @@ export default class EFPViewer <> }

    This image was generated with the {viewID} at bar.utoronto.ca/eplant by Waese et al. 2017. - Creative Commons License + Creative Commons License ) } else { From 7a74efa024f1792b9f7a7094e9b921628301a0ee Mon Sep 17 00:00:00 2001 From: yukthiw Date: Tue, 2 Jan 2024 21:01:55 -0500 Subject: [PATCH 04/15] small changes --- Eplant/state/index.tsx | 40 ++++++++++++++++++--------------- Eplant/util/citations/index.tsx | 4 +--- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Eplant/state/index.tsx b/Eplant/state/index.tsx index 178c6f2c..c3e87b4a 100644 --- a/Eplant/state/index.tsx +++ b/Eplant/state/index.tsx @@ -2,9 +2,9 @@ import GeneticElement from '@eplant/GeneticElement' import { Species } from '@eplant/GeneticElement' import arabidopsis from '@eplant/Species/arabidopsis' import Storage from '@eplant/util/Storage' -import { atom, useAtom, useAtomValue, useSetAtom, WritableAtom } from 'jotai' -import * as React from 'react' +import { atom, SetStateAction, useAtom, useAtomValue, useSetAtom, WritableAtom } from 'jotai' import * as FlexLayout from 'flexlayout-react' +import { useState, useEffect, createContext, useContext } from 'react' const persistAtom = atom(true) export const useSetPersist = () => useSetAtom(persistAtom) @@ -38,8 +38,8 @@ export const pageLoad = (() => { })() export const usePageLoad = () => { - const [progress, setProgress] = React.useState(0) - React.useEffect(() => pageLoad.watch(setProgress), []) + const [progress, setProgress] = useState(0) + useEffect(() => pageLoad.watch(setProgress), []) return [progress, progress == 1] as [number, boolean] } @@ -74,7 +74,7 @@ export function atomWithStorage( //throw loadedValue return get(val) }, - (get, set, x: React.SetStateAction) => { + (get, set, x: SetStateAction) => { const newValue = typeof x == 'function' ? (x as (prev: T) => T)(get(val)) : x if (get(persistAtom)) { @@ -118,7 +118,7 @@ function atomWithOptionalStorage( (get) => { return get(val) }, - (get, set, x: React.SetStateAction) => { + (get, set, x: SetStateAction) => { const newValue = typeof x == 'function' ? (x as (prev: T) => T)(get(val)) : x if (get(persistAtom)) { @@ -131,7 +131,7 @@ function atomWithOptionalStorage( } function useAtomReducer( - atom: WritableAtom], void>, + atom: WritableAtom], void>, reducer: (x: T, action: A) => T, ): (action: A) => void { const setValue = useSetAtom(atom) @@ -147,15 +147,19 @@ export const genesAtom = atomWithOptionalStorage( export const useGeneticElements = () => useAtom(genesAtom) export const useSetGeneticElements = () => useSetAtom(genesAtom) -export const fetchCitations = async () => { - try { - const response = await fetch('https://bar.utoronto.ca/eplant/data/citations.json') - const data = await response.json() - return data - } catch (error) { - console.error('Error fetching citations:', error) - } -} +const fetchCitations = () => { + return fetch('https://bar.utoronto.ca/eplant/data/citations.json') + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }) + .catch(error => { + console.error('Error fetching citations:', error); + throw error; + }); +}; export const citationsAtom = atom(await fetchCitations()) export const useCitations = () => useAtom(citationsAtom) @@ -272,8 +276,8 @@ export const usePanes = () => ] export const usePanesDispatch = () => useAtomReducer(panesAtom, panesReducer) -export const ViewIDContext = React.createContext('') -export const useViewID = () => React.useContext(ViewIDContext) +export const ViewIDContext = createContext('') +export const useViewID = () => useContext(ViewIDContext) export const printingAtom = atom(null) export const usePrinting = () => useAtom(printingAtom) diff --git a/Eplant/util/citations/index.tsx b/Eplant/util/citations/index.tsx index c341386d..69938c6a 100644 --- a/Eplant/util/citations/index.tsx +++ b/Eplant/util/citations/index.tsx @@ -1,6 +1,4 @@ -import GeneticElement from "@eplant/GeneticElement" -import { citationsAtom, useCitations } from "@eplant/state" -import { useAtomValue } from "jotai" +import { useCitations } from "@eplant/state" export const getCitation = (viewName: string) => { const [citations] = useCitations() From 5cd624da9020365dd2ff42e545b097fa6a898885 Mon Sep 17 00:00:00 2001 From: Yukthiw <59380056+Yukthiw@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:55:25 -0500 Subject: [PATCH 05/15] Update Eplant/views/eFP/Viewer/index.tsx Co-authored-by: Alex <16113308+alexwaeseperlman@users.noreply.github.com> --- Eplant/views/eFP/Viewer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Eplant/views/eFP/Viewer/index.tsx b/Eplant/views/eFP/Viewer/index.tsx index 5d026dc7..387a0fe5 100644 --- a/Eplant/views/eFP/Viewer/index.tsx +++ b/Eplant/views/eFP/Viewer/index.tsx @@ -509,7 +509,7 @@ export default class EFPViewer


    -

    {citation.URL ? citation.URL : ""} +{citation.URL ?

    citation.URL

    : ""} { xmlData.length > 0 ? (
      From a659c94e99545190de297315e3022bf0b1c2f38f Mon Sep 17 00:00:00 2001 From: Yukthiw <59380056+Yukthiw@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:55:35 -0500 Subject: [PATCH 06/15] Update Eplant/views/eFP/Viewer/index.tsx Co-authored-by: Alex <16113308+alexwaeseperlman@users.noreply.github.com> --- Eplant/views/eFP/Viewer/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Eplant/views/eFP/Viewer/index.tsx b/Eplant/views/eFP/Viewer/index.tsx index 387a0fe5..3274084d 100644 --- a/Eplant/views/eFP/Viewer/index.tsx +++ b/Eplant/views/eFP/Viewer/index.tsx @@ -508,7 +508,7 @@ export default class EFPViewer return (
      -

      +{citation.notes ?
      : ""} {citation.URL ?

      citation.URL

      : ""} { xmlData.length > 0 ? From 2cf7b706d84c82211918b5cedf9f82ac3f7bd3c0 Mon Sep 17 00:00:00 2001 From: Yukthi Date: Sun, 7 Jan 2024 11:53:55 -0500 Subject: [PATCH 07/15] Extracted EFPViewerCitation component + ran prettier on a bunch of stuff --- Eplant/UI/Layout/ViewContainer/index.tsx | 27 +++--- Eplant/View/index.ts | 6 +- Eplant/state/index.tsx | 65 ++++++++------- Eplant/util/citations/index.tsx | 8 +- Eplant/views/eFP/Viewer/EFPViewerCitation.tsx | 57 +++++++++++++ Eplant/views/eFP/Viewer/index.tsx | 83 +++++++++---------- 6 files changed, 156 insertions(+), 90 deletions(-) create mode 100644 Eplant/views/eFP/Viewer/EFPViewerCitation.tsx diff --git a/Eplant/UI/Layout/ViewContainer/index.tsx b/Eplant/UI/Layout/ViewContainer/index.tsx index 62c75293..5afb2af5 100644 --- a/Eplant/UI/Layout/ViewContainer/index.tsx +++ b/Eplant/UI/Layout/ViewContainer/index.tsx @@ -194,10 +194,7 @@ export function ViewContainer({ {view.citation ? ( - + ) : ( No information provided for this view )} @@ -218,17 +215,17 @@ export function ViewContainer({ flexDirection: 'column', ...(printing == viewId ? { - display: 'block !important', - padding: 0, - position: 'fixed', - left: 0, - top: 0, - margin: 0, - zIndex: 1e9, - background: theme.palette.background.paper, - width: '100%', - minHeight: '100%', - } + display: 'block !important', + padding: 0, + position: 'fixed', + left: 0, + top: 0, + margin: 0, + zIndex: 1e9, + background: theme.palette.background.paper, + width: '100%', + minHeight: '100%', + } : {}), })} > diff --git a/Eplant/View/index.ts b/Eplant/View/index.ts index eba2a6bc..a3770972 100644 --- a/Eplant/View/index.ts +++ b/Eplant/View/index.ts @@ -58,7 +58,11 @@ export interface View { /** * The react component that renders citations for a gene */ - citation?: (props: { state?: State, activeData?: Data, gene?: GeneticElement | null }) => JSX.Element + citation?: (props: { + state?: State + activeData?: Data + gene?: GeneticElement | null + }) => JSX.Element /** * Return the title of this view's tab */ diff --git a/Eplant/state/index.tsx b/Eplant/state/index.tsx index c3e87b4a..6ffa632b 100644 --- a/Eplant/state/index.tsx +++ b/Eplant/state/index.tsx @@ -2,7 +2,14 @@ import GeneticElement from '@eplant/GeneticElement' import { Species } from '@eplant/GeneticElement' import arabidopsis from '@eplant/Species/arabidopsis' import Storage from '@eplant/util/Storage' -import { atom, SetStateAction, useAtom, useAtomValue, useSetAtom, WritableAtom } from 'jotai' +import { + atom, + SetStateAction, + useAtom, + useAtomValue, + useSetAtom, + WritableAtom, +} from 'jotai' import * as FlexLayout from 'flexlayout-react' import { useState, useEffect, createContext, useContext } from 'react' @@ -56,17 +63,17 @@ export function atomWithStorage( if (e) setAtom(e) else setAtom(initialValue) } - ; (async () => { - const finished = loading?.() - try { - const val = await loadedValue - if (val) { - setAtom(val) - } - } finally { - if (finished) finished() + ;(async () => { + const finished = loading?.() + try { + const val = await loadedValue + if (val) { + setAtom(val) } - })() + } finally { + if (finished) finished() + } + })() return storage.watch(key, listener) } const a = atom( @@ -101,17 +108,17 @@ function atomWithOptionalStorage( if (e) setAtom(deserialize(e)) else setAtom(initialValue) } - ; (async () => { - pageLoad.start() - try { - const val = await loadedValue - if (val) { - setAtom(deserialize(val)) - } - } finally { - pageLoad.done() + ;(async () => { + pageLoad.start() + try { + const val = await loadedValue + if (val) { + setAtom(deserialize(val)) } - })() + } finally { + pageLoad.done() + } + })() return storage.watch(key, listener) } const a = atom( @@ -149,17 +156,17 @@ export const useSetGeneticElements = () => useSetAtom(genesAtom) const fetchCitations = () => { return fetch('https://bar.utoronto.ca/eplant/data/citations.json') - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); + throw new Error(`HTTP error! Status: ${response.status}`) } - return response.json(); + return response.json() }) - .catch(error => { - console.error('Error fetching citations:', error); - throw error; - }); -}; + .catch((error) => { + console.error('Error fetching citations:', error) + throw error + }) +} export const citationsAtom = atom(await fetchCitations()) export const useCitations = () => useAtom(citationsAtom) diff --git a/Eplant/util/citations/index.tsx b/Eplant/util/citations/index.tsx index 69938c6a..aa34299a 100644 --- a/Eplant/util/citations/index.tsx +++ b/Eplant/util/citations/index.tsx @@ -1,6 +1,8 @@ -import { useCitations } from "@eplant/state" +import { useCitations } from '@eplant/state' export const getCitation = (viewName: string) => { - const [citations] = useCitations() - return citations.find((citation: { [key: string]: string }) => citation.view === viewName) + const [citations] = useCitations() + return citations.find( + (citation: { [key: string]: string }) => citation.view === viewName, + ) } diff --git a/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx b/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx new file mode 100644 index 00000000..77f67f60 --- /dev/null +++ b/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx @@ -0,0 +1,57 @@ +import GeneticElement from '@eplant/GeneticElement' +import { getCitation } from '@eplant/util/citations' +import React from 'react' +import { useState, useEffect } from 'react' +import { EFPViewerData, EFPViewerState } from './types' +import { Link } from '@mui/material' + +const EFPViewerCitation = (props: { + viewID: string + citation: { [key: string]: string } + xmlData: string[] +}) => { + const { viewID, citation, xmlData } = props + return ( + // Need to set inner HTML to capture nested HTML tags in some citations +
      +
      + {citation.notes ? ( +
      + ) : ( + '' + )} + {citation.URL ? ( +

      + citation.URL +

      + ) : ( + '' + )} + {xmlData.length > 0 ? ( +
        + {xmlData.map((item, index) => { + if (item) { + return
      • {item}
      • + } + })} +
      + ) : ( + <> + )} +

      This image was generated with the {viewID} at + bar.utoronto.ca/eplant by Waese et al. 2017. + + Creative Commons License + +
      + ) +} + +export default EFPViewerCitation diff --git a/Eplant/views/eFP/Viewer/index.tsx b/Eplant/views/eFP/Viewer/index.tsx index 3274084d..6173dfd8 100644 --- a/Eplant/views/eFP/Viewer/index.tsx +++ b/Eplant/views/eFP/Viewer/index.tsx @@ -21,6 +21,7 @@ import GeneDistributionChart from './GeneDistributionChart' import { useCitations } from '@eplant/state' import { getCitation } from '@eplant/util/citations' import MaskModal from './MaskModal' +import EFPViewerCitation from './EFPViewerCitation' type EFPListProps = { geneticElement: GeneticElement @@ -133,7 +134,7 @@ export default class EFPViewer public icon: () => JSX.Element, public description?: string, public thumbnail?: string, - ) { } + ) {} getInitialData = async ( gene: GeneticElement | null, loadEvent: (progress: number) => void, @@ -164,7 +165,6 @@ export default class EFPViewer viewData: viewData, efps: this.efps, colorMode: 'absolute' as const, - } } reducer = (state: EFPViewerState, action: EFPViewerAction) => { @@ -203,12 +203,12 @@ export default class EFPViewer case 'toggle-mask-modal': return { ...state, - maskModalVisible: state.maskModalVisible ? false : true + maskModalVisible: state.maskModalVisible ? false : true, } case 'set-mask-threshold': return { ...state, - maskThreshold: action.threshold + maskThreshold: action.threshold, } default: return state @@ -256,10 +256,10 @@ export default class EFPViewer state={{ colorMode: props.state.colorMode, renderAsThumbnail: false, - maskThreshold: props.state.maskThreshold + maskThreshold: props.state.maskThreshold, }} geneticElement={props.geneticElement} - dispatch={() => { }} + dispatch={() => {}} /> ) }, [ @@ -268,7 +268,7 @@ export default class EFPViewer props.dispatch, sortedViewData[activeViewIndex], props.state.colorMode, - props.state.maskThreshold + props.state.maskThreshold, ]) const ref = React.useRef(null) const dimensions = useDimensions(ref) @@ -389,7 +389,12 @@ export default class EFPViewer props.dispatch({ type: 'toggle-mask-modal' })} - onSubmit={(threshold) => props.dispatch({ type: 'set-mask-threshold', threshold: threshold })} + onSubmit={(threshold) => + props.dispatch({ + type: 'set-mask-threshold', + threshold: threshold, + }) + } /> ({ @@ -404,7 +409,7 @@ export default class EFPViewer state={{ colorMode: props.state.colorMode, renderAsThumbnail: false, - maskThreshold: props.state.maskThreshold + maskThreshold: props.state.maskThreshold, }} /> <>Mask data - } + render: () => <>Mask data, + }, ] header: View['header'] = ( props, @@ -470,14 +475,22 @@ export default class EFPViewer ) - citation = (props: { activeData?: EFPViewerData, state?: EFPViewerState, gene?: GeneticElement | null }) => { + citation = (props: { + activeData?: EFPViewerData + state?: EFPViewerState + gene?: GeneticElement | null + }) => { const [xmlData, setXMLData] = useState([]) - const viewID = props.activeData?.views.find((v) => v.id == props.state?.activeView)?.name - const viewXML = props.activeData?.views.find((v) => v.id == props.state?.activeView)?.xmlURL + const viewID = props.activeData?.views.find( + (v) => v.id == props.state?.activeView, + )?.name + const viewXML = props.activeData?.views.find( + (v) => v.id == props.state?.activeView, + )?.xmlURL useEffect(() => { const xmlLoad = async () => { - let xmlString = "" + let xmlString = '' if (viewXML) { try { const response = await fetch(viewXML) @@ -489,11 +502,13 @@ export default class EFPViewer } // Extract
    • tags - if (xmlString !== "") { - const parser = new DOMParser(); - const xmlDoc = parser.parseFromString(xmlString, 'text/xml'); - const listItems = xmlDoc.querySelectorAll('info li'); - const itemsArray = Array.from(listItems).map((liElement) => liElement.textContent ? liElement.textContent : ""); + if (xmlString !== '') { + const parser = new DOMParser() + const xmlDoc = parser.parseFromString(xmlString, 'text/xml') + const listItems = xmlDoc.querySelectorAll('info li') + const itemsArray = Array.from(listItems).map((liElement) => + liElement.textContent ? liElement.textContent : '', + ) setXMLData(itemsArray) } else { setXMLData([]) @@ -503,29 +518,13 @@ export default class EFPViewer }, [viewXML]) if (viewID) { - const citation = getCitation(viewID) - // Need to set inner HTML to capture nested HTML tags in some citations + const citation = getCitation(viewID) as { [key: string]: string } return ( -
      -
      -{citation.notes ?
      : ""} -{citation.URL ?

      citation.URL

      : ""} - { - xmlData.length > 0 ? - (
        - {xmlData.map((item, index) => { - if (item) { - return (
      • {item}
      • ) - } - } - )} -
      - ) : - <> - } -

      This image was generated with the {viewID} at bar.utoronto.ca/eplant by Waese et al. 2017. - Creative Commons License -
      + ) } else { return
      No Citation information provided.
      From fe3e4ee89cc52ef2e9bae3004eeb0b70940a9266 Mon Sep 17 00:00:00 2001 From: Yukthi Date: Tue, 23 Jan 2024 18:17:14 -0500 Subject: [PATCH 08/15] Fixed React imports, added import for citations --- Eplant/views/eFP/Viewer/EFPViewerCitation.tsx | 44 ++++++++---------- Eplant/views/eFP/Viewer/index.tsx | 46 +++++++++++-------- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx b/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx index 77f67f60..c8aaa60f 100644 --- a/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx +++ b/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx @@ -1,36 +1,32 @@ -import GeneticElement from '@eplant/GeneticElement' -import { getCitation } from '@eplant/util/citations' import React from 'react' -import { useState, useEffect } from 'react' -import { EFPViewerData, EFPViewerState } from './types' import { Link } from '@mui/material' -const EFPViewerCitation = (props: { +interface IEFPViewerCitationProps { viewID: string citation: { [key: string]: string } xmlData: string[] -}) => { - const { viewID, citation, xmlData } = props +} +const EFPViewerCitation = ({ + viewID, + citation, + xmlData, +}: IEFPViewerCitationProps) => { return ( // Need to set inner HTML to capture nested HTML tags in some citations
      -
      - {citation.notes ? ( -
      + {citation.notes && ( +

      - ) : ( - '' + /> )} - {citation.URL ? ( -

      - citation.URL -

      - ) : ( - '' + {citation.URL && ( + +

      citation.URL

      +
      )} - {xmlData.length > 0 ? ( + {xmlData.length > 0 && (
        {xmlData.map((item, index) => { if (item) { @@ -38,11 +34,11 @@ const EFPViewerCitation = (props: { } })}
      - ) : ( - <> )} -

      This image was generated with the {viewID} at - bar.utoronto.ca/eplant by Waese et al. 2017. +

      + This image was generated with the {viewID} at bar.utoronto.ca/eplant by + Waese et al. 2017. +

      Creative Commons License{data.views[i].name}
      }> @@ -78,7 +90,7 @@ const EFPListItem = React.memo( }, ) -const EFPListRow = React.memo(function EFPListRow({ +const EFPListRow = memo(function EFPListRow({ style, index, data, @@ -235,7 +247,7 @@ export default class EFPViewer const sortedViewData = viewIndices.map((i) => props.activeData.viewData[i]) const sortedEfps = viewIndices.map((i) => this.efps[i]) - let activeViewIndex = React.useMemo( + let activeViewIndex = useMemo( () => sortedEfps.findIndex((v) => v.id == props.state.activeView), [props.state.activeView, ...sortedEfps.map((v) => v.id)], ) @@ -246,7 +258,7 @@ export default class EFPViewer id: sortedEfps[0].id, }) } - const efp = React.useMemo(() => { + const efp = useMemo(() => { const Component = sortedEfps[activeViewIndex].component return ( (null) + const ref = useRef(null) const dimensions = useDimensions(ref) if (!props.geneticElement) return <> @@ -475,19 +487,13 @@ export default class EFPViewer ) - citation = (props: { - activeData?: EFPViewerData - state?: EFPViewerState - gene?: GeneticElement | null - }) => { + citation = ({ activeData, state, gene }: ICitationProps) => { const [xmlData, setXMLData] = useState([]) - const viewID = props.activeData?.views.find( - (v) => v.id == props.state?.activeView, - )?.name - const viewXML = props.activeData?.views.find( - (v) => v.id == props.state?.activeView, - )?.xmlURL + const viewID = activeData?.views.find((v) => v.id == state?.activeView) + ?.name + const viewXML = activeData?.views.find((v) => v.id == state?.activeView) + ?.xmlURL useEffect(() => { const xmlLoad = async () => { let xmlString = '' @@ -527,7 +533,7 @@ export default class EFPViewer > ) } else { - return
      No Citation information provided.
      + return

      No Citation information provided.

      } } } From 2a7fb20a33a9b5278f90f9f1131d52d318af8120 Mon Sep 17 00:00:00 2001 From: Yukthi Date: Tue, 23 Jan 2024 21:40:53 -0500 Subject: [PATCH 09/15] Updated eslint and tsconfig, changed double quotes to single --- .eslintrc.cjs | 1 + Eplant/views/eFP/Viewer/EFPViewerCitation.tsx | 1 - tsconfig.json | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 331f0117..5f79a61c 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -23,5 +23,6 @@ module.exports = { '@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unused-vars': 'off', + 'react/react-in-jsx-scope': 'off', }, } diff --git a/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx b/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx index c8aaa60f..8a2d2f45 100644 --- a/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx +++ b/Eplant/views/eFP/Viewer/EFPViewerCitation.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Link } from '@mui/material' interface IEFPViewerCitationProps { diff --git a/tsconfig.json b/tsconfig.json index a88995e6..feef5f8d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "baseUrl": ".", - "jsx": "react", + "jsx": "react-jsx", "paths": { "@eplant/*": ["Eplant/*"], "@stories/*": ["stories/*"] From c3721a034fc2e97611a4ede618ce9c9270e365e6 Mon Sep 17 00:00:00 2001 From: Yukthi Date: Tue, 23 Jan 2024 21:52:29 -0500 Subject: [PATCH 10/15] Changed prettier settings to use single quotes in JSX --- .prettierrc | 3 ++- Eplant/UI/Layout/ViewContainer/index.tsx | 20 +++++++++---------- Eplant/views/eFP/Viewer/EFPViewerCitation.tsx | 8 ++++---- Eplant/views/eFP/Viewer/index.tsx | 16 +++++++-------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/.prettierrc b/.prettierrc index 6fed3d0f..c6838a94 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,6 @@ "tabWidth": 2, "useTabs": false, "semi": false, - "singleQuote": true + "singleQuote": true, + "jsxSingleQuote": true } diff --git a/Eplant/UI/Layout/ViewContainer/index.tsx b/Eplant/UI/Layout/ViewContainer/index.tsx index 5afb2af5..29d48cc4 100644 --- a/Eplant/UI/Layout/ViewContainer/index.tsx +++ b/Eplant/UI/Layout/ViewContainer/index.tsx @@ -69,21 +69,21 @@ export function ViewContainer({ const topBar = React.useMemo( () => ( ({ background: theme.palette.background.active, })} - position="sticky" + position='sticky' elevation={0} > {/* View selector dropdown */} - + {/* View */}