diff --git a/src/App.tsx b/src/App.tsx index 033403a5..8148a9dd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -29,6 +29,7 @@ import { VariantViewModal } from './ui/VariantViewModal'; import { NavigationButtons } from './ui/NavigationButtons'; import { Track, getTrackDocData } from './ui/getTrackDocData.js'; import { NavigationBar } from './ui/NavigationBar'; +import { ClinicalPanel } from './ui/ClinicalPanel'; import 'bootstrap/dist/css/bootstrap.min.css'; import './css/App.css'; @@ -103,6 +104,9 @@ function App(props: RouteComponentProps) { const currentSpec = useRef(); + const [isClinicalPanelOpen, setIsClinicalPanelOpen] = useState(false); + const CLINICAL_PANEL_WIDTH = isMinimalMode || !demo?.clinicalInfo ? 0 : isClinicalPanelOpen ? 250 : 45; + // interactions const [showSamples, setShowSamples] = useState(urlParams.get('showSamples') !== 'false' && !xDomain); const [showAbout, setShowAbout] = useState(false); @@ -115,7 +119,7 @@ function App(props: RouteComponentProps) { const [showPutativeDriver, setShowPutativeDriver] = useState(true); const [interactiveMode, setInteractiveMode] = useState(isMinimalMode ?? false); const [visPanelWidth, setVisPanelWidth] = useState( - INIT_VIS_PANEL_WIDTH - (isMinimalMode ? 10 : VIS_PADDING.left * 2) + INIT_VIS_PANEL_WIDTH - (isMinimalMode ? 10 : VIS_PADDING.left + VIS_PADDING.right + CLINICAL_PANEL_WIDTH) ); const [overviewChr, setOverviewChr] = useState(''); const [genomeViewChr, setGenomeViewChr] = useState(''); @@ -140,6 +144,10 @@ function App(props: RouteComponentProps) { return (allDrivers as any).filter((d: any) => d.sample_id === demoId && +d.pos); } + useEffect(() => { + setVisPanelWidth(INIT_VIS_PANEL_WIDTH - (VIS_PADDING.left + VIS_PADDING.right + CLINICAL_PANEL_WIDTH)); + }, [isClinicalPanelOpen]); + // update demo useEffect(() => { if (typeof demo.drivers === 'string' && demo.drivers.split('.').pop() === 'json') { @@ -186,6 +194,9 @@ function App(props: RouteComponentProps) { setSelectedSvId(''); leftReads.current = []; rightReads.current = []; + + // Update the appearance of the clinical panel + setIsClinicalPanelOpen(!!demo?.clinicalInfo); }, [demo]); useEffect(() => { @@ -323,13 +334,14 @@ function App(props: RouteComponentProps) { window.addEventListener( 'resize', debounce(() => { - setVisPanelWidth(window.innerWidth - (isMinimalMode ? 10 : VIS_PADDING.left * 2)); + setVisPanelWidth(window.innerWidth - (isMinimalMode ? 10 : VIS_PADDING.left + VIS_PADDING.right)); }, 500) ); - // Lower opacity of legend image as it leaves viewport + // Lower opacity of legend image as it leaves viewport in minimal mode if (isMinimalMode) { const legendElement = document.querySelector('.genome-view-legend'); + const options = { root: document.querySelector('.minimal_mode'), rootMargin: '-250px 0px 0px 0px', @@ -342,6 +354,10 @@ function App(props: RouteComponentProps) { }, options); observer.observe(legendElement); + + return () => { + observer.unobserve(legendElement); + }; } }, []); @@ -740,10 +756,10 @@ function App(props: RouteComponentProps) { const height = window.innerHeight; if (!isMinimalMode) { if ( - VIS_PADDING.top < top && - top < height - VIS_PADDING.top && - VIS_PADDING.left < left && - left < width - VIS_PADDING.left + VIS_PADDING.top < top && // past top margin + top < height - VIS_PADDING.top && // before bottom margin + VIS_PADDING.left < left && // past left margin + left < width - (VIS_PADDING.right + CLINICAL_PANEL_WIDTH) // before right margin ) { setMouseOnVis(true); } else { @@ -939,7 +955,9 @@ function App(props: RouteComponentProps) { id="gosling-panel" className="gosling-panel" style={{ - width: `calc(100% - ${VIS_PADDING.left * 2}px)`, + width: isMinimalMode + ? `calc(100% - ${VIS_PADDING.left + VIS_PADDING.right}px)` + : `calc(100% - ${VIS_PADDING.left + VIS_PADDING.right + CLINICAL_PANEL_WIDTH}px)`, height: `calc(100% - ${VIS_PADDING.top * 2}px)`, padding: `${VIS_PADDING.top}px ${VIS_PADDING.right}px ${VIS_PADDING.bottom}px ${VIS_PADDING.left}px` }} @@ -963,7 +981,7 @@ function App(props: RouteComponentProps) { {!isMinimalMode && (
@@ -1280,13 +1300,13 @@ function App(props: RouteComponentProps) { ? 'visible' : 'collapse', position: 'absolute', - right: `${VIS_PADDING.right}px`, + right: `${VIS_PADDING.right + CLINICAL_PANEL_WIDTH}px`, top: '60px', background: 'lightgray', color: 'black', padding: '6px', pointerEvents: 'none', - zIndex: 9999, + zIndex: 999, boxShadow: '0 0 20px 2px rgba(0, 0, 0, 0.2)' }} > @@ -1302,7 +1322,7 @@ function App(props: RouteComponentProps) { height: '100%', visibility: 'collapse', boxShadow: interactiveMode ? 'inset 0 0 4px 2px #2399DB' : 'none', - zIndex: 9999, + zIndex: 999, background: 'none', position: 'absolute', top: 0, @@ -1441,6 +1461,9 @@ function App(props: RouteComponentProps) { className="move-to-top-btn" tabIndex={showSamples ? -1 : 0} aria-label="Scroll to top." + style={{ + right: isMinimalMode ? '10px' : `${VIS_PADDING.right + CLINICAL_PANEL_WIDTH}px` + }} onClick={() => { setTimeout( () => document.getElementById('gosling-panel')?.scrollTo({ top: 0, behavior: 'smooth' }), @@ -1458,6 +1481,17 @@ function App(props: RouteComponentProps) {
+ {!isMinimalMode && demo?.clinicalInfo && ( + + )}
); diff --git a/src/css/App.css b/src/css/App.css index 254cab7f..c537f7ec 100644 --- a/src/css/App.css +++ b/src/css/App.css @@ -44,7 +44,6 @@ a:hover { text-decoration: 3px underline #5bb6ea; } - .vis-panel { .vis-overview-panel { position: absolute; @@ -68,7 +67,6 @@ a:hover { border-bottom: 1px solid lightgray; background: #f6f6f6; - .config-button { svg { vertical-align: unset; @@ -86,7 +84,7 @@ a:hover { .feedback a:hover { background-color: #eaeaea; } - + .title-github-link:active, .title-doc-link:active, .feedback a:active { @@ -129,7 +127,6 @@ a:hover { top: 12px; } - .vis-overview-panel .overview-container { display: flex; flex-wrap: wrap; @@ -234,7 +231,8 @@ a:hover { cursor: pointer; } -.chromoscope-title:focus, .chromoscope-title:focus-visible { +.chromoscope-title:focus, +.chromoscope-title:focus-visible { /* padding: 5px; */ } @@ -638,7 +636,6 @@ a:hover { position: absolute; box-shadow: 0 0 10px 0px #00000032; bottom: 20px; - right: 20px; cursor: pointer; z-index: 998; opacity: 0.5; @@ -830,7 +827,8 @@ a:hover { .track-tooltip:hover { cursor: pointer; } -.track-tooltip:focus, .track-tooltip:focus-visible { +.track-tooltip:focus, +.track-tooltip:focus-visible { outline-offset: 2px; } @@ -899,11 +897,495 @@ a:hover { background-color: #e6e4e4; } +.clinical-panel-container { + box-sizing: border-box !important; + position: fixed; + top: 50px; + right: 0px; + width: 250px; + height: calc(100% - 50px); + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSc1JyBoZWlnaHQ9JzUnPgo8cmVjdCB3aWR0aD0nNScgaGVpZ2h0PSc1JyBmaWxsPScjZmZmJy8+CjxyZWN0IHdpZHRoPScxJyBoZWlnaHQ9JzEnIGZpbGw9JyNjY2MnLz4KPC9zdmc+); + background-position-x: -1px; + + .clinical-panel { + box-sizing: border-box !important; + display: flex; + flex-direction: column; + margin-top: 10px; + padding: 10px 10px 20px; + border-radius: 10px 0px 0px 10px; + height: 100%; + background-color: #eef2f6; + border: 1px solid #d7dfe4; + } + + .panel-header { + display: flex; + justify-content: space-between; + align-items: baseline; + padding: 0px 0px 6px 0px; + border-bottom: 1px solid #cad3da; + margin-bottom: 10px; + + .panel-icon { + color: #434d5e; + height: 20px; + margin: 0px; + } + + h2 { + font-size: 1rem; + font-weight: 600; + color: #2c3648; + margin-bottom: 0px; + } + + .collapse-panel { + box-sizing: border-box !important; + display: flex; + justify-content: center; + width: 20px; + height: 20px; + background-color: #f5f5f5; + border: 1px solid #cad3da; + border-radius: 100%; + padding: 3px; + svg { + margin: auto; + color: #4d565c; + } + } + } + + .content { + display: flex; + flex: 1; + flex-direction: column; + justify-content: flex-start; + padding: 0px; + overflow-y: auto; + + .panel-section { + box-sizing: border-box !important; + display: flex; + flex: 1; + flex-direction: column; + justify-content: flex-start; + background-color: #ffffff; + /* border: 1px solid #CAD3DA; */ + border-radius: 8px; + overflow: hidden; + margin-bottom: 16px; + + .section-header { + display: flex; + justify-content: space-between; + background-color: #202f48; + padding: 10px; + border: none; + + h3 { + font-weight: 600; + color: white; + font-size: 0.875rem; + margin: 0px; + } + + .section-toggle { + display: flex; + justify-content: center; + padding: 0px; + height: 12px; + width: 12px; + border: none; + background-color: transparent; + margin: auto 0px; + + svg { + margin: auto; + color: white; + } + } + } + .callout { + padding: 4px; + border-bottom: 1px solid #e0e6eb; + + .content { + display: flex; + flex-direction: row; + justify-content: center; + background-color: #e9edf2; + padding: 5px 8px; + border-radius: 5px; + + svg { + margin: auto 8px auto 0px; + color: #464e55; + height: 10px; + width: 10px; + } + span { + color: #464e55; + font-size: 0.625rem; + } + } + } + + .section:focus-visible, + .section:focus { + outline-offset: -1px; + } + .section-body { + display: flex; + flex-direction: column; + flex: 1; + box-sizing: border-box !important; + overflow-y: scroll; + padding: 0px 8px; + font-size: 0.75rem; + border: solid #cad3da; + border-width: 0px 1px 1px 1px; + border-radius: 0px 0px 8px 8px; + + .data-list { + padding: 0px; + margin-bottom: 0px; + + .data-row { + box-sizing: border-box !important; + display: flex; + justify-content: flex-start; + padding: 8px 0px; + border-bottom: 1px solid #e0e6eb; + + .data-label { + flex: 3; + color: #565961; + font-weight: 600; + } + + .data-value { + flex: 2; + color: #000000; + } + } + + .data-row:last-of-type { + border-bottom: none; + } + + .data-row.clickable:hover { + background-color: #d5dce2 !important; + cursor: pointer; + } + } + } + } + + /* Style individual sections */ + .panel-section.summary { + flex: 3; + + .section-body { + .callout { + padding: 5px 0px; + .content { + padding: 4px 12px; + display: flex; + background-color: #ffefdb; + border: 1px solid #f6e4cd; + border-radius: 10px; + + span { + font-size: 0.75rem; + color: #502d07; + margin: auto; + } + } + } + } + } + .panel-section.variants { + flex: 2; + .section-body { + padding: 0px; + + .data-list { + overflow: scroll; + .data-row { + padding: 8px; + } + .data-row:nth-of-type(even) { + background-color: #f5f5f5; + } + } + } + } + .panel-section.signatures { + flex: 4; + + .callout.hrdetect { + display: flex; + justify-content: space-between; + padding: 10px; + border: solid #cad3da; + border-width: 0px 1px; + + .label { + flex: 1; + color: #565961; + font-size: 0.75rem; + font-weight: 500; + } + + .value { + flex: 1; + color: #000000; + font-size: 0.75rem; + font-weight: 500; + + .circle-icon { + width: 8px; + height: 8px; + } + .circle-icon.positive { + color: #1d9819; + } + .circle-icon.negative { + color: lightgray; + } + + span { + margin-left: 5px; + } + } + } + + .section-body { + padding: 0px; + overflow: hidden; + + .data-list { + .data-row:nth-of-type(even) { + background-color: #f5f5f5; + } + + .data-row { + display: inline-block; + width: 100%; + padding: 10px 8px; + + .data-label { + display: inline; + flex: none; + color: #000000; + font-weight: 600; + margin-right: 0.25rem; + } + .data-value { + } + } + } + } + } + + /* Expanded and collapsed */ + .panel-section.expanded { + max-height: fit-content; + .section-header { + .section-toggle { + svg { + transform: rotate(180deg); + } + } + } + } + .panel-section.collapsed { + flex: none; + max-height: none; + .section-body { + flex: none; + padding: 0px; + height: 0px; + border: none; + } + + .callout.hrdetect { + display: none; + } + + .section-header { + .section-toggle { + svg { + transform: none; + } + } + } + } + + /* Styles for toggle-able rows */ + .dropdown-row { + display: flex; + flex-direction: column; + overflow: scroll; + + .header { + box-sizing: border-box !important; + display: flex; + width: 100%; + justify-content: space-between; + background-color: #e0e6eb; + padding: 10px; + border: 1px solid #cad3da; + border-width: 1px 0px; + + h4 { + font-weight: 600; + color: #2c3648; + font-size: 0.75rem; + margin: 0px; + } + + .toggle { + display: flex; + justify-content: center; + padding: 0px; + height: 12px; + width: 12px; + border: none; + background-color: transparent; + margin: auto 0px; + + svg { + margin: auto; + color: #55606b; + } + } + } + .header:hover { + background-color: #d5dce2; + } + + .body { + overflow: scroll; + } + } + .dropdown-row.expanded { + flex: 1; + max-height: fit-content; + + .header { + .toggle { + svg { + transform: rotate(180deg); + } + } + } + .body { + flex: 1; + } + } + .dropdown-row.collapsed { + flex: none; + max-height: none; + + .header { + .toggle { + border-bottom: 0px; + svg { + transform: none; + } + } + } + .callout { + display: none; + } + .body { + height: 0px; + } + } + .dropdown-row.disabled { + .header { + background-color: #f5f5f5; + pointer-events: none; + border-bottom: none; + h4 { + color: gray; + } + .toggle { + svg { + transform: none; + color: gray; + } + } + } + } + } +} + +.clinical-panel-container.open { + box-sizing: border-box !important; + position: fixed; + top: 50px; + right: 0px; + width: 250px; + height: calc(100% - 50px); + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSc1JyBoZWlnaHQ9JzUnPgo8cmVjdCB3aWR0aD0nNScgaGVpZ2h0PSc1JyBmaWxsPScjZmZmJy8+CjxyZWN0IHdpZHRoPScxJyBoZWlnaHQ9JzEnIGZpbGw9JyNjY2MnLz4KPC9zdmc+); + background-position-x: -1px; +} +.clinical-panel-container.closed { + width: 45px; + + .clinical-panel { + width: 100%; + } + + .panel-header { + flex-direction: column-reverse; + justify-content: center; + align-items: center; + gap: 8px; + border: none; + + h2 { + display: none; + } + + .collapse-panel { + svg { + transform: rotate(180deg); + } + } + } + + .panel-section { + display: none; + } +} + +.clinical-panel-container.closed.disabled { + .clinical-panel { + background-color: #f5f5f5; + + .panel-header { + .panel-icon { + color: gray; + } + + .collapse-panel { + display: none; + } + } + } +} + /* Minimal Mode styles */ .minimal_mode { .gosling-panel { overflow-y: scroll; overflow-x: hidden; + padding: 60px 300px 60px 60px; } .sample-label { @@ -1149,6 +1631,7 @@ a:hover { .instructions-modals-container { .modal { .modal-dialog { + display: flex; width: 100%; } .modal-body { @@ -1156,8 +1639,8 @@ a:hover { max-height: 80vh; overflow-y: scroll; display: flex; - justify-content: center; - padding: 24px 36px; + justify-content: start; + padding: 0px; width: 100%; .modal-body-content { diff --git a/src/css/NavigationBar.css b/src/css/NavigationBar.css index 839656b8..15075550 100644 --- a/src/css/NavigationBar.css +++ b/src/css/NavigationBar.css @@ -117,7 +117,7 @@ border-radius: 4px; background-color: transparent; } - + .config-button:hover { background-color: #f6f6f6; } @@ -125,7 +125,8 @@ background-color: #e6e4e4; } - .config-button:focus-visible, .config-button:focus { + .config-button:focus-visible, + .config-button:focus { outline-offset: 0px; } @@ -198,7 +199,7 @@ border: none; background-color: transparent; } - + .title-btn.clipboard { border: none; background-color: transparent; @@ -217,4 +218,4 @@ justify-content: end; align-items: center; } -} \ No newline at end of file +} diff --git a/src/data/samples.ts b/src/data/samples.ts index 53bea3f1..3b154694 100644 --- a/src/data/samples.ts +++ b/src/data/samples.ts @@ -1,4 +1,6 @@ import { Assembly } from 'gosling.js/dist/src/gosling-schema'; +import { DataRowProps } from '../ui/ClinicalPanel'; + import _7a921087 from '../script/img/7a921087-8e62-4a93-a757-fd8cdbe1eb8f.jpeg'; import _84ca6ab0 from '../script/img/84ca6ab0-9edc-4636-9d27-55cdba334d7d.jpeg'; import _7d332cb1 from '../script/img/7d332cb1-ba25-47e4-8bf8-d25e14f40d59.jpeg'; @@ -31,6 +33,7 @@ export type SampleType = { cnFields?: [string, string, string]; thumbnail?: string; note?: string; + clinicalInfo?: { [key: string]: DataRowProps[] }; }; // const samples: SampleType[] = (pcawg as SampleType[]).map(d => { return { group: 'default', ...d }}); diff --git a/src/icon.ts b/src/icon.ts index 29bd8e16..cb69f2c6 100644 --- a/src/icon.ts +++ b/src/icon.ts @@ -361,5 +361,33 @@ export const ICONS: Record = { ], stroke: 'currentColor', fill: 'none' + }, + CLIPBOARD: { + width: 11, + height: 14, + viewBox: '0 0 11 14', + path: [ + 'M1.375 1.75H3.66667C3.66667 0.784766 4.4888 0 5.5 0C6.5112 0 7.33333 0.784766 7.33333 1.75H9.625C10.3841 1.75 11 2.33789 11 3.0625V12.6875C11 13.4121 10.3841 14 9.625 14H1.375C0.615885 14 0 13.4121 0 12.6875V3.0625C0 2.33789 0.615885 1.75 1.375 1.75ZM5.5 1.09375C5.11901 1.09375 4.8125 1.38633 4.8125 1.75C4.8125 2.11367 5.11901 2.40625 5.5 2.40625C5.88099 2.40625 6.1875 2.11367 6.1875 1.75C6.1875 1.38633 5.88099 1.09375 5.5 1.09375ZM2.75 9.40625C2.75 9.52656 2.85312 9.625 2.97917 9.625H4.58333V11.1562C4.58333 11.2766 4.68646 11.375 4.8125 11.375H6.1875C6.31354 11.375 6.41667 11.2766 6.41667 11.1562V9.625H8.02083C8.14687 9.625 8.25 9.52656 8.25 9.40625V8.09375C8.25 7.97344 8.14687 7.875 8.02083 7.875H6.41667V6.34375C6.41667 6.22344 6.31354 6.125 6.1875 6.125H4.8125C4.68646 6.125 4.58333 6.22344 4.58333 6.34375V7.875H2.97917C2.85312 7.875 2.75 7.97344 2.75 8.09375V9.40625ZM2.75 4.15625C2.75 4.27656 2.85312 4.375 2.97917 4.375H8.02083C8.14687 4.375 8.25 4.27656 8.25 4.15625V3.71875C8.25 3.59844 8.14687 3.5 8.02083 3.5H2.97917C2.85312 3.5 2.75 3.59844 2.75 3.71875V4.15625Z' + ], + stroke: 'currentColor', + fill: 'none' + }, + ARROW_RIGHT: { + width: 7, + height: 6, + viewBox: '0 0 7 6', + path: [ + 'M6.85351 2.64701C7.04883 2.84225 7.04883 3.15931 6.85351 3.35455L4.35337 5.85357C4.15804 6.04881 3.84084 6.04881 3.64552 5.85357C3.45019 5.65834 3.45019 5.34127 3.64552 5.14604L5.29404 3.49981L0.500028 3.4998C0.22345 3.4998 4.762e-07 3.27645 5.24537e-07 3C5.72874e-07 2.72354 0.223451 2.50019 0.500028 2.50019L5.29248 2.5002L3.64708 0.853964C3.45176 0.658727 3.45176 0.341663 3.64708 0.146426C3.8424 -0.0488099 4.15961 -0.0488098 4.35493 0.146426L6.85507 2.64545L6.85351 2.64701Z' + ], + stroke: 'currentColor', + fill: 'none' + }, + CIRCLE: { + width: 10, + height: 10, + viewBox: '0 0 512 512', + path: ['M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512z'], + stroke: 'currentColor', + fill: 'none' } }; diff --git a/src/ui/ClinicalPanel/index.tsx b/src/ui/ClinicalPanel/index.tsx new file mode 100644 index 00000000..2afe3ab2 --- /dev/null +++ b/src/ui/ClinicalPanel/index.tsx @@ -0,0 +1,300 @@ +import React, { useState, useEffect } from 'react'; + +import { ICONS } from '../../icon'; + +export type DataRowProps = { + label?: string; + value?: string; + cDNA?: string; + chr?: string; + gene?: string; + position?: string; + type?: string; + count?: string; + hrDetect?: boolean; + handleClick?: () => void; +}; + +// Data row with label and value +const DataRow = ({ handleClick, label, value }: DataRowProps) => { + // Format label to be capitalized + let capitalizedLabel: string; + if (label) { + capitalizedLabel = label.charAt(0).toUpperCase() + label.slice(1); + } + + return ( +
  • handleClick() : null} + role={handleClick ? 'button' : ''} + > + {label ? {capitalizedLabel} : null} + {value} +
  • + ); +}; + +type ToggleRowGroupProps = { + header: string; + callout?: string; + data: DataRowProps[]; +}; + +// Group of rows with a toggle-able header +const ToggleRowGroup = ({ callout = null, header, data }: ToggleRowGroupProps) => { + const [isExpanded, setIsExpanded] = useState(false); + + return ( +
    + + {callout && ( +
    +
    + + INFO CIRCLE + {ICONS.INFO_CIRCLE.path.map(p => ( + + ))} + + {callout} +
    +
    + )} +
    +
      + {data.map((row: DataRowProps, i: number) => { + return ; + })} +
    +
    +
    + ); +}; + +type PanelSectionProps = { + data: DataRowProps[]; + handleZoomToGene?: (gene: string) => void; +}; + +// Panel section for Clinical Summary data +const ClinicalSummary = ({ data }: PanelSectionProps) => { + const [isExpanded, setIsExpanded] = useState(true); + + return ( +
    + +
    +
    +
    + Invasive Ductal Carcinoma +
    +
    +
      + {data.map((row: DataRowProps, i: number) => { + return ; + })} +
    +
    +
    + ); +}; + +// Panel section for Clinically Relevant Variants data +const ClinicallyRelevantVariants = ({ handleZoomToGene, data }: PanelSectionProps) => { + const [isExpanded, setIsExpanded] = useState(true); + + return ( +
    + +
    +
    +
    + + INFO CIRCLE + {ICONS.INFO_CIRCLE.path.map(p => ( + + ))} + + Click to show in the visualization +
    +
    +
      + {data.map((row: DataRowProps, i: number) => { + const gene = row?.gene ?? ''; + const type = row?.type ?? ''; + const cDNA = row?.cDNA ?? ''; + const variantString = gene + ' ' + type + ' ' + cDNA; + + const handleClick = () => { + handleZoomToGene(gene); + }; + + return ; + })} +
    +
    +
    + ); +}; + +// Panel section for Mutational Signatures data +const MutationalSignatures = ({ data }: PanelSectionProps) => { + const [isExpanded, setIsExpanded] = useState(true); + + // Check if HRDetect is positive + const isHrDetect = data.some((row: DataRowProps) => row.hrDetect); + + // Split data into HRDetect and other data + const hrDetectData = []; + const otherData = []; + + data.forEach((row: DataRowProps) => { + const formattedData = { + label: row.count, + value: row.label + }; + + if (row.hrDetect) { + hrDetectData.push(formattedData); + } else { + otherData.push(formattedData); + } + }); + + return ( +
    + +
    + HRDetect +
    + + CIRCLE + + + {isHrDetect ? 'Positive' : 'Negative'} +
    +
    +
    + + +
    +
    + ); +}; + +type ClinicalPanelProps = { + demo: any; + gosRef: any; + filteredSamples: any; + isClinicalPanelOpen: boolean; + hasClinicalInfo: boolean; + setInteractiveMode: (interactiveMode: boolean) => void; + setIsClinicalPanelOpen: (isClinicalPanelOpen: boolean) => void; +}; + +export const ClinicalPanel = ({ + hasClinicalInfo, + demo, + gosRef, + isClinicalPanelOpen, + setInteractiveMode, + setIsClinicalPanelOpen +}: ClinicalPanelProps) => { + const [clinicalInformation, setClinicalInformation] = useState(null); + + const handleZoomToGene = (gene: string) => { + setInteractiveMode(true); + setTimeout(() => { + document.getElementById('variant-view')?.scrollIntoView({ + block: 'start', + inline: 'nearest', + behavior: 'smooth' + }), + 0; + }); + + gosRef.current.api.zoomToGene(`${demo.id}-mid-ideogram`, `${gene}`, 1000); + }; + + useEffect(() => { + if (hasClinicalInfo && demo?.clinicalInfo) { + console.log('demo.clinicalInfo', demo.clinicalInfo); + setClinicalInformation(demo.clinicalInfo); + } + }, [demo]); + + return ( +
    +
    +
    + + Clipboard + + +

    Genome Interpretation

    + +
    + + {hasClinicalInfo && clinicalInformation ? ( +
    + + + +
    + ) : null} +
    +
    + ); +}; diff --git a/src/ui/NavigationBar.tsx b/src/ui/NavigationBar.tsx index 37e49d0a..ccadf569 100644 --- a/src/ui/NavigationBar.tsx +++ b/src/ui/NavigationBar.tsx @@ -37,17 +37,14 @@ export const NavigationBar = ({ return (
    - -
    {!isChrome() ? ( GitHub - diff --git a/src/ui/NavigationButtons.tsx b/src/ui/NavigationButtons.tsx index 487330c8..65890569 100644 --- a/src/ui/NavigationButtons.tsx +++ b/src/ui/NavigationButtons.tsx @@ -4,10 +4,9 @@ import { ICONS } from '../icon'; type NavigationButtonsProps = { showSamples: boolean; isMinimalMode: boolean; -} - -export const NavigationButtons = ({ showSamples, isMinimalMode } : NavigationButtonsProps) => { +}; +export const NavigationButtons = ({ showSamples, isMinimalMode }: NavigationButtonsProps) => { return (