Skip to content

Commit

Permalink
[Platform]: Insilico predictors chart (#594)
Browse files Browse the repository at this point in the history
  • Loading branch information
carcruz authored Dec 6, 2024
1 parent 9f2b8b2 commit 17db5ee
Show file tree
Hide file tree
Showing 7 changed files with 3,430 additions and 1,652 deletions.
19 changes: 17 additions & 2 deletions packages/sections/src/variant/InSilicoPredictors/Body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { Typography } from "@mui/material";
import { SectionItem, Tooltip, OtTable } from "ui";
import { definition } from "../InSilicoPredictors";
import Description from "../InSilicoPredictors/Description";
import { naLabel, sectionsBaseSizeQuery } from "../../constants";
import { naLabel } from "../../constants";
import IN_SILICO_PREDICTORS_QUERY from "./InSilicoPredictorsQuery.gql";
import InSilicoPredictorsVisualisation from "./InSilicoPredictorsPlot";
import { VIEW } from "ui/src/constants";

const columns = [
{
Expand Down Expand Up @@ -37,6 +39,11 @@ const columns = [
label: "Score",
renderCell: ({ score }) => score ?? naLabel,
},
{
id: "normalisedScore",
label: "Normalised score",
renderCell: ({ normalisedScore }) => normalisedScore ?? naLabel,
},
];

type BodyProps = {
Expand All @@ -47,7 +54,6 @@ type BodyProps = {
export function Body({ id, entity }: BodyProps) {
const variables = {
variantId: id,
// size: sectionsBaseSizeQuery,
};
const request = useQuery(IN_SILICO_PREDICTORS_QUERY, {
variables,
Expand All @@ -58,13 +64,22 @@ export function Body({ id, entity }: BodyProps) {
definition={definition}
request={request}
entity={entity}
defaultView={VIEW.chart}
renderDescription={() => (
<Description
variantId={request.data?.variant.id}
referenceAllele={request.data?.variant.referenceAllele}
alternateAllele={request.data?.variant.alternateAllele}
/>
)}
renderChart={() => (
<InSilicoPredictorsVisualisation
data={request.data.variant.inSilicoPredictors}
query={IN_SILICO_PREDICTORS_QUERY.loc.source.body}
variables={variables}
columns={columns}
/>
)}
renderBody={() => {
let rows = [];
if (request.data)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { useRef, useEffect } from "react";
import * as PlotLib from "@observablehq/plot";
import { rgb } from "d3";
import { useMeasure } from "@uidotdev/usehooks";
import { Box, Fade } from "@mui/material";
import { grey } from "@mui/material/colors";
import { DataDownloader } from "ui";

const PRIORITISATION_COLORS = [
rgb("#bc3a19"),
rgb("#d65a1f"),
rgb("#e08145"),
rgb("#e3a772"),
rgb("#e6ca9c"),
rgb("#eceada"),
rgb("#c5d2c1"),
rgb("#9ebaa8"),
rgb("#78a290"),
rgb("#528b78"),
rgb("#2f735f"),
];

function InSilicoPredictorsPlot({ data, query, variables, columns }) {
const [ref, { width }] = useMeasure();
return (
<div>
<Box>
<ChartControls data={data} query={query} variables={variables} columns={columns} />
</Box>
<Box sx={{ width: "90%", margin: "0 auto", mb: 6 }} ref={ref}>
<Fade in>
<div>
<Plot data={data} width={width} />
</div>
</Fade>
</Box>
</div>
);
}

function ChartControls({ data, query, variables, columns }) {
return (
<Box
sx={{
borderColor: grey[300],
borderRadius: 1,
display: "flex",
justifyContent: "flex-end",
gap: 1,
}}
>
<DataDownloader
btnLabel="Export"
rows={data}
query={query}
variables={variables}
columns={columns}
/>
</Box>
);
}

const colorScale = PRIORITISATION_COLORS.reverse();
const getXLabel = (tick: number) => {
if (tick === -1) return "Likely benign";
if (tick === 0) return "Uncertain";
if (tick === 1) return "Likely deleterious";
return "";
};

function Plot({ data, width }) {
const headerRef = useRef();

useEffect(() => {
if (data === undefined || width === null) return;
const chart = PlotLib.plot({
width: width,
height: 250,
label: null,
marginLeft: 120,
marginRight: 100,
x: {
axis: "bottom",
ticks: 2,
labelAnchor: "center",
tickFormat: d => getXLabel(d),
tickSize: 0,
domain: [-1, 1],
},

color: {
legend: false,
type: "linear",
range: colorScale,
domain: [-1, 1],
interpolate: "hsl",
},
style: {
fontSize: "15px",
},
marks: [
PlotLib.ruleY(data, {
x1: -0.99,
x2: 0.99,
y: "method",
stroke: grey[400],
strokeWidth: 1,
strokeDasharray: 6,
}),
PlotLib.dot(data, {
x: "normalisedScore",
y: "method",
r: 12,
fill: "normalisedScore",
stroke: grey[100],
strokeWidth: 4,
tip: {
fontSize: 14,
textPadding: 20,
format: {
x: false,
y: false,
fill: false,
normalisedScore: false,
},
},
channels: {
method: {
value: "method",
label: "",
},
assessment: {
value: "assessment",
label: "Assessment:",
},
score: {
value: "score",
label: "Score:",
},
assessmentFlag: {
value: "assessmentFlag",
label: "Assessment Flag:",
},
normalisedScore: {
value: "normalisedScore",
label: "Normalised Score:",
},
},
}),
],
});
headerRef.current.append(chart);
return () => chart.remove();
}, [data, width]);

return <Box ref={headerRef}></Box>;
}

export default InSilicoPredictorsPlot;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ query InSilicoPredictorsQuery($variantId: String!) {
assessment
score
assessmentFlag
normalisedScore
}
referenceAllele
alternateAllele
Expand Down
12 changes: 7 additions & 5 deletions packages/ui/src/components/Section/SectionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ function SectionItem({
<CardContent className={classes.cardContent}>
<>
{error && <SectionError error={error} />}
{showContentLoading && loading && (
loadingMessage
? <Box
{showContentLoading &&
loading &&
(loadingMessage ? (
<Box
width="100%"
height={390}
bgcolor={theme => theme.palette.grey[100]}
Expand All @@ -124,8 +125,9 @@ function SectionItem({
>
<SummaryLoader message={loadingMessage} />
</Box>
: <Skeleton sx={{ height: 390 }} variant="rectangular" />
)}
) : (
<Skeleton sx={{ height: 390 }} variant="rectangular" />
))}
{hasData && selectedView === VIEW.table && renderBody()}
{hasData && selectedView === VIEW.chart && renderChart()}
{showEmptySection && (
Expand Down
67 changes: 34 additions & 33 deletions packages/ui/src/components/Section/SectionViewToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,55 @@
import { faChartPie, faTableColumns } from "@fortawesome/free-solid-svg-icons";
import { FormControl, MenuItem, Select, SelectChangeEvent, Box } from "@mui/material";
import { ReactElement, useState } from "react";
import { faChartLine, faTableColumns } from "@fortawesome/free-solid-svg-icons";
import { ToggleButtonGroup, ToggleButton, styled } from "@mui/material";
import { ReactElement, useState, MouseEvent } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { VIEW } from "../../constants";

const menuItemStyles = { display: "flex", gap: 1 };
const menuActiveStyles = { display: "flex", gap: 1, alignItems: "center" };

type SectionViewToggleProps = {
defaultValue: string;
viewChange: (s: string) => void;
};

const StyledToggleButtonGroup = styled(ToggleButtonGroup)`
padding: 0;
`;

const StyledToggleButton = styled(ToggleButton)(({ theme }) => ({
paddingTop: theme.spacing(0.5),
paddingBottom: theme.spacing(0.5),
display: "flex",
alignItems: "center",
gap: theme.spacing(0.5),
}));

function SectionViewToggle({
defaultValue = VIEW.table,
viewChange,
}: SectionViewToggleProps): ReactElement {
const [alignment, setAlignment] = useState(defaultValue);
const [sectionView, setView] = useState(defaultValue);

const handleAlignment = (event: SelectChangeEvent) => {
const handleViewChange = (event: MouseEvent) => {
event.preventDefault();
if (event.target.value) {
setAlignment(event.target.value);
viewChange(event.target.value);
setView(event.target.value);
if (viewChange) viewChange(event.target.value);
}
};

return (
<FormControl size="small">
<Select
sx={{ typography: "body2" }}
value={alignment}
onChange={handleAlignment}
renderValue={view => {
const iconView = VIEW.table ? faTableColumns : faChartPie;
return (
<Box sx={menuActiveStyles}>
<FontAwesomeIcon icon={iconView} /> {view} view
</Box>
);
}}
>
<MenuItem value={VIEW.table} sx={menuItemStyles}>
<FontAwesomeIcon icon={faTableColumns} />
{VIEW.table}
</MenuItem>
<MenuItem value={VIEW.chart} sx={menuItemStyles}>
<FontAwesomeIcon icon={faChartPie} /> {VIEW.chart}
</MenuItem>
</Select>
</FormControl>
<StyledToggleButtonGroup
sx={{ typography: "body2" }}
value={sectionView}
onChange={handleViewChange}
>
<StyledToggleButton aria-label="Switch to table view" value={VIEW.table}>
<FontAwesomeIcon icon={faTableColumns} />
{VIEW.table} view
</StyledToggleButton>
<StyledToggleButton aria-label="Switch to chartn view" value={VIEW.chart}>
<FontAwesomeIcon icon={faChartLine} />
{VIEW.chart}
</StyledToggleButton>
</StyledToggleButtonGroup>
);
}
export default SectionViewToggle;
7 changes: 1 addition & 6 deletions packages/ui/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,11 +335,6 @@ export const publicationSummaryQuery = ({ pmcId, symbol, name }) => {
};

export const VIEW = {
chart: "Chart",
chart: "Visualisation",
table: "Table",
};

// export enum DISPLAY_TYPE {
// CHART = "chart",
// TABLE = "table",
// }
Loading

0 comments on commit 17db5ee

Please sign in to comment.