Skip to content

Commit

Permalink
feat(components): add mutation comparison venn diagram
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasKellerer committed Apr 4, 2024
1 parent 74308f3 commit c490020
Show file tree
Hide file tree
Showing 12 changed files with 733 additions and 119 deletions.
674 changes: 585 additions & 89 deletions components/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@lit/context": "^1.1.0",
"@lit/task": "^1.0.0",
"chart.js": "^4.4.2",
"chartjs-chart-venn": "^4.3.0",
"dayjs": "^1.11.10",
"flatpickr": "^4.6.13",
"gridjs": "^6.2.0",
Expand Down
10 changes: 7 additions & 3 deletions components/src/preact/components/chart.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { Meta, StoryObj } from '@storybook/preact';
import Chart, { GsChartProps } from './chart';
import GsChart, { GsChartProps } from './chart';
import { getYAxisScale } from '../shared/charts/getYAxisScale';
import { Chart, registerables } from 'chart.js';
import { LogitScale } from '../shared/charts/LogitScale';

const meta: Meta<GsChartProps> = {
title: 'Component/Chart',
component: Chart,
component: GsChart,
parameters: { fetchMock: {} },
};

export default meta;

Chart.register(...registerables, LogitScale);

export const ChartStory: StoryObj<GsChartProps> = {
render: (args) => {
return <Chart configuration={args.configuration} yAxisScaleType={args.yAxisScaleType} />;
return <GsChart configuration={args.configuration} />;
},
args: {
configuration: {
Expand Down
17 changes: 2 additions & 15 deletions components/src/preact/components/chart.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { useEffect, useRef } from 'preact/hooks';
import { Chart, ChartConfiguration, registerables } from 'chart.js';
import { getYAxisScale, ScaleType } from '../shared/charts/getYAxisScale';
import { LogitScale } from '../shared/charts/LogitScale';
import { Chart, ChartConfiguration } from 'chart.js';

export interface GsChartProps {
configuration: ChartConfiguration;
yAxisScaleType: ScaleType;
}

const GsChart = ({ configuration, yAxisScaleType }: GsChartProps) => {
const GsChart = ({ configuration }: GsChartProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const chartRef = useRef<Chart | null>(null);

Expand All @@ -22,23 +19,13 @@ const GsChart = ({ configuration, yAxisScaleType }: GsChartProps) => {
return;
}

Chart.register(...registerables, LogitScale);

chartRef.current = new Chart(ctx, configuration);

return () => {
chartRef.current?.destroy();
};
}, [canvasRef, configuration]);

useEffect(() => {
if (chartRef.current) {
// @ts-expect-error-next-line -- chart.js typings are not complete with custom scales
chartRef.current.options.scales!.y = getYAxisScale(yAxisScaleType);
chartRef.current.update();
}
}, [yAxisScaleType]);

return <canvas ref={canvasRef} />;
};

Expand Down
102 changes: 102 additions & 0 deletions components/src/preact/mutationComparison/mutation-comparison-venn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { FunctionComponent } from 'preact';
import { ArcSlice, extractSets, VennDiagramController } from 'chartjs-chart-venn';
import { useEffect, useRef } from 'preact/hooks';
import { ActiveElement, Chart, ChartConfiguration, ChartEvent, registerables } from 'chart.js';
import GsChart from '../components/chart';
import { MutationData } from './mutation-comparison';
import { Dataset } from '../../operator/Dataset';

Chart.register(...registerables, VennDiagramController, ArcSlice);

export interface MutationComparisonVennProps {
data: Dataset<MutationData>;
}

export const MutationComparisonVenn: FunctionComponent<MutationComparisonVennProps> = ({ data }) => {
const divRef = useRef<HTMLDivElement>(null);
const noElementSelectedMessage = 'You have no elements selected. Click in the venn diagram to select.';
useEffect(() => {
if (divRef.current === null) {
return;
}
divRef.current.innerText = noElementSelectedMessage;
}, [divRef]);

if (data.content.length > 5) {
return <div>Too many variants to display. Maximum are five. </div>;
}

const sets = extractSets(
data.content
.map((mutationData) => {
return {
...mutationData,
data: mutationData.data.filter((mutationEntry) => mutationEntry.type !== 'insertion'),
};
})
.map((mutationData) => {
return {
label: mutationData.displayName,
values: mutationData.data.map((mutationEntry) => mutationEntry.mutation.toString()),
};
}),
);

const config: ChartConfiguration = {
type: 'venn',
data: sets,
options: {
scales: {
x: {
ticks: {
color: 'black',
font: {
size: 20,
},
},
},
y: {
ticks: {
color: 'blue',
font: {
size: 20,
},
},
},
},
events: ['click'],
onClick(_: ChartEvent, elements: ActiveElement[]) {
if (elements.length === 0) {
divRef.current!.innerText = noElementSelectedMessage;
}
},
backgroundColor: '#f5f5f5',
animation: false,
layout: {
padding: 30,
},
plugins: {
legend: {
display: false,
},
tooltip: {
filter: (tooltipItem: { dataset: { data: { values: string[] }[] }; dataIndex: number }) => {
const values = tooltipItem.dataset.data[tooltipItem.dataIndex].values;

divRef.current!.innerText = `Mutations: ${values.join(', ')}` || '';
return false;
},
},
},
},
};

return (
<>
<GsChart configuration={config} />
<div class='flex flex-wrap break-words m-2' ref={divRef}>
{''}
</div>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const meta: Meta<MutationComparisonProps> = {
control: { type: 'radio' },
},
views: {
options: ['table'],
options: ['table', 'venn'],
control: { type: 'check' },
},
},
Expand Down Expand Up @@ -60,7 +60,7 @@ export const TwoVariants = {
},
],
sequenceType: 'nucleotide',
views: ['table'],
views: ['table', 'venn'],
},
parameters: {
fetchMock: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import Info from '../components/info';
import Tabs from '../components/tabs';
import { CheckboxSelector } from '../components/checkbox-selector';
import { MutationComparisonTable } from './mutation-comparison-table';
import { MutationComparisonVenn } from './mutation-comparison-venn';

export type View = 'table';
export type View = 'table' | 'venn';

export interface MutationComparisonVariant {
lapisFilter: LapisFilter;
Expand Down Expand Up @@ -150,6 +151,11 @@ export const MutationComparison: FunctionComponent<MutationComparisonProps> = ({
title: 'Table',
content: <MutationComparisonTable data={{ content: filteredData }} />,
};
case 'venn':
return {
title: 'Venn',
content: <MutationComparisonVenn data={{ content: filteredData }} />,
};
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { ChartConfiguration } from 'chart.js';
import { Chart, ChartConfiguration, registerables } from 'chart.js';
import { addUnit, minusTemporal } from '../../utils/temporal';
import { getMinMaxNumber } from '../../utils/utils';
import { PrevalenceOverTimeData } from '../../query/queryPrevalenceOverTime';
import { getYAxisScale, ScaleType } from '../shared/charts/getYAxisScale';
import GsChart from '../components/chart';
import { LogitScale } from '../shared/charts/LogitScale';

interface PrevalenceOverTimeBubbleChartProps {
data: PrevalenceOverTimeData;
yAxisScaleType: ScaleType;
}

Chart.register(...registerables, LogitScale);

const PrevalenceOverTimeBubbleChart = ({ data, yAxisScaleType }: PrevalenceOverTimeBubbleChartProps) => {
const firstDate = data[0].content[0].dateRange!;
const total = data.map((graphData) => graphData.content.map((dataPoint) => dataPoint.total)).flat();
Expand Down Expand Up @@ -71,7 +74,7 @@ const PrevalenceOverTimeBubbleChart = ({ data, yAxisScaleType }: PrevalenceOverT
},
};

return <GsChart configuration={config} yAxisScaleType={yAxisScaleType} />;
return <GsChart configuration={config} />;
};

export default PrevalenceOverTimeBubbleChart;
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { ChartConfiguration } from 'chart.js';
import { Chart, ChartConfiguration, registerables } from 'chart.js';
import { PrevalenceOverTimeData } from '../../query/queryPrevalenceOverTime';
import { getYAxisScale, ScaleType } from '../shared/charts/getYAxisScale';
import GsChart from '../components/chart';
import { LogitScale } from '../../components/charts/LogitScale';

interface PrevalenceOverTimeLineBarChartProps {
data: PrevalenceOverTimeData;
type: 'line' | 'bar';
yAxisScaleType: ScaleType;
}

Chart.register(...registerables, LogitScale);

const PrevalenceOverTimeLineBarChart = ({ data, type, yAxisScaleType }: PrevalenceOverTimeLineBarChartProps) => {
const config: ChartConfiguration = {
type,
Expand Down Expand Up @@ -39,7 +42,7 @@ const PrevalenceOverTimeLineBarChart = ({ data, type, yAxisScaleType }: Prevalen
},
};

return <GsChart configuration={config} yAxisScaleType={yAxisScaleType} />;
return <GsChart configuration={config} />;
};

export default PrevalenceOverTimeLineBarChart;
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ChartConfiguration } from 'chart.js';
import { Chart, ChartConfiguration, registerables } from 'chart.js';
import { YearMonthDay } from '../../utils/temporal';
import { getYAxisScale, ScaleType } from '../shared/charts/getYAxisScale';
import GsChart from '../components/chart';
import { LogitScale } from '../shared/charts/LogitScale';

interface RelativeGrowthAdvantageChartData {
t: YearMonthDay[];
Expand All @@ -16,6 +17,8 @@ interface RelativeGrowthAdvantageChartProps {
yAxisScaleType: ScaleType;
}

Chart.register(...registerables, LogitScale);

const RelativeGrowthAdvantageChart = ({ data, yAxisScaleType }: RelativeGrowthAdvantageChartProps) => {
const config: ChartConfiguration = {
type: 'line',
Expand Down Expand Up @@ -69,7 +72,7 @@ const RelativeGrowthAdvantageChart = ({ data, yAxisScaleType }: RelativeGrowthAd
},
};

return <GsChart configuration={config} yAxisScaleType={yAxisScaleType} />;
return <GsChart configuration={config} />;
};

export default RelativeGrowthAdvantageChart;
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const meta: Meta<MutationComparisonProps> = {
control: { type: 'radio' },
},
views: {
options: ['table', 'grid'],
options: ['table', 'venn'],
control: { type: 'check' },
},
},
Expand All @@ -44,7 +44,7 @@ const Template: StoryObj<MutationComparisonProps> = {
const dateTo = '2022-01-01';
const dateFrom = '2021-01-01';

export const Default = {
export const Default: StoryObj<MutationComparisonProps> = {
...Template,
args: {
variants: [
Expand All @@ -63,7 +63,7 @@ export const Default = {
},
],
sequenceType: 'nucleotide',
views: ['table'],
views: ['table', 'venn'],
},
parameters: {
fetchMock: {
Expand Down Expand Up @@ -127,3 +127,11 @@ export const Default = {
},
},
};

export const VennDiagramOnly: StoryObj<MutationComparisonProps> = {
...Default,
args: {
...Default.args,
views: ['venn'],
},
};
1 change: 1 addition & 0 deletions components/tests/visualizationStories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const visualizationStories = [
{ id: 'visualization-relative-growth-advantage--default', title: 'Relative growth advantage' },
{ id: 'visualization-mutations--default', title: 'Mutations', testDownloadWithFilename: 'mutations.csv' },
{ id: 'visualization-mutation-comparison--default', title: 'Mutation comparison' },
{ id: 'visualization-mutation-comparison--venn-diagram-only', title: 'Mutation comparison' },
];

0 comments on commit c490020

Please sign in to comment.