Skip to content

Commit 8772cea

Browse files
authored
Merge pull request #121 from bcgsc/release/v6.6.1
Release/v6.6.1
2 parents 917f6f5 + bffd140 commit 8772cea

File tree

10 files changed

+372
-36
lines changed

10 files changed

+372
-36
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// eslint-disable-next-line import/no-extraneous-dependencies
2+
import '@ag-grid-community/core/dist/styles/ag-grid.css';
3+
// eslint-disable-next-line import/no-extraneous-dependencies
4+
import '@ag-grid-community/core/dist/styles/ag-theme-material.min.css';
5+
import React from 'react';
6+
import { Story } from '@storybook/react/types-6-0';
7+
import { AgGridReact } from '@ag-grid-community/react/lib/agGridReact';
8+
import { ClientSideRowModelModule } from '@ag-grid-community/client-side-row-model';
9+
import CivicCellRenderer, { CivicCellRendererProps } from '.';
10+
11+
export default {
12+
title: 'renderers/CivicCellRenderer',
13+
component: CivicCellRenderer,
14+
};
15+
16+
const Template = (args) => <CivicCellRenderer {...args} />;
17+
18+
export const WithinGrid = ({
19+
data = [
20+
{
21+
externalSource: ['IPRKB', 'GraphKB', 'CIViC'],
22+
externalStatementId: ['uuid-iprkb', 'uuid-kb', '1100', '1110', '1111'],
23+
}, {
24+
externalSource: 'CIViC',
25+
externalStatementId: '1100',
26+
},
27+
],
28+
}: CivicCellRendererProps): JSX.Element => (
29+
<div className="ag-theme-material">
30+
<AgGridReact
31+
columnDefs={[
32+
{
33+
headerName: 'External Source',
34+
colId: 'externalSource',
35+
cellRenderer: 'CivicCellRenderer',
36+
},
37+
]}
38+
frameworkComponents={{
39+
CivicCellRenderer,
40+
}}
41+
suppressAnimationFrame
42+
suppressColumnVirtualisation
43+
disableStaticMarkup
44+
modules={[ClientSideRowModelModule]}
45+
rowData={data}
46+
domLayout="autoHeight"
47+
/>
48+
</div>
49+
);
50+
51+
export const CivicSingle: Story<CivicCellRendererProps> = Template.bind({});
52+
CivicSingle.args = {
53+
data: {
54+
externalSource: 'CIViC',
55+
externalStatementId: '1100',
56+
},
57+
};
58+
59+
export const CivicArray: Story<CivicCellRendererProps> = Template.bind({});
60+
CivicArray.args = {
61+
data: {
62+
externalSource: 'CIViC',
63+
externalStatementId: ['1000', '1100', '1110', '1111'],
64+
},
65+
};
66+
67+
export const VariousSourcesNoCivicArray: Story<CivicCellRendererProps> = Template.bind({});
68+
VariousSourcesNoCivicArray.args = {
69+
data: {
70+
externalSource: ['IPRKB', 'totes-not-civic'],
71+
externalStatementId: ['uuid-id', '1100', '1110', '1111'],
72+
},
73+
};
74+
75+
export const VariousSourcesWithCivicArray: Story<CivicCellRendererProps> = Template.bind({});
76+
VariousSourcesWithCivicArray.args = {
77+
data: {
78+
externalSource: ['IPRKB', 'GraphKB', 'CIViC'],
79+
externalStatementId: ['uuid-id', '1100', '1110', '1111'],
80+
},
81+
};
82+
83+
export const NoCivicSingle: Story<CivicCellRendererProps> = Template.bind({});
84+
NoCivicSingle.args = {
85+
data: {
86+
externalSource: 'IPRKB',
87+
externalStatementId: 'uuid-id',
88+
},
89+
};
90+
91+
export const NoCivicArray: Story<CivicCellRendererProps> = Template.bind({});
92+
NoCivicArray.args = {
93+
data: {
94+
externalSource: 'IPRKB',
95+
externalStatementId: ['uuid-id1', 'uuid-id2', 'uuid-id3'],
96+
},
97+
};

app/components/DataTable/components/CivicCellRenderer/index.tsx

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,101 @@ import React, { useState, useEffect } from 'react';
33
import NewTabLink from '@/components/NewTabLink';
44
// eslint-disable-next-line import/no-extraneous-dependencies
55
import { ICellRendererParams } from '@ag-grid-community/core';
6+
import {
7+
Button, IconButton, Menu, MenuItem,
8+
} from '@material-ui/core';
9+
import { OpenInNew } from '@material-ui/icons';
610

711
type CivicCellRendererProps = ICellRendererParams['data'];
812

913
const CivicCellRenderer = ({
1014
data,
1115
}: CivicCellRendererProps): JSX.Element => {
16+
const {
17+
externalStatementId,
18+
externalSource,
19+
} = data;
20+
1221
const [link, setLink] = useState('');
22+
const [links, setLinks] = useState<string[]>([]);
1323
const [text, setText] = useState('');
24+
const [anchorEl, setAnchorEl] = useState<Element>(null);
25+
26+
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => setAnchorEl(event.currentTarget);
27+
const handleMenuClose = () => setAnchorEl(null);
1428

1529
useEffect(() => {
16-
if (data?.externalSource?.toLowerCase() === 'civic'
17-
&& !Number.isNaN(parseInt(data.externalStatementId, 10))) {
18-
setLink(`https://civicdb.org/links/evidence/${parseInt(data.externalStatementId, 10)}`);
19-
setText(data.externalSource);
20-
} else {
21-
setText(data.externalSource);
30+
if (data) {
31+
if (Array.isArray(externalSource)) {
32+
const numOnly = /^\d+$/;
33+
if (externalSource.map((es) => es.toLowerCase()).includes('civic')) {
34+
// TODO: Assume all numeric for now that it is civic ids, more types of external sources to come
35+
setLinks(externalStatementId.filter((id) => numOnly.test(id)));
36+
setText(externalSource.filter((src) => src?.toLowerCase() !== 'civic').join(', '));
37+
} else {
38+
setText(externalSource.join(', '));
39+
}
40+
} else {
41+
const intId = parseInt(externalStatementId, 10);
42+
if (
43+
externalSource?.toLowerCase() === 'civic'
44+
&& !Number.isNaN(intId)
45+
) {
46+
if (Array.isArray(externalStatementId)) {
47+
setLinks(externalStatementId);
48+
} else {
49+
setLink(`https://civicdb.org/links/evidence/${intId}`);
50+
setText(externalSource);
51+
}
52+
} else {
53+
setText(externalSource);
54+
}
55+
}
2256
}
23-
}, [data]);
57+
}, [data, externalStatementId, externalSource]);
2458

25-
if (link) {
59+
const menuItems = links.map((linkId) => (
60+
<MenuItem
61+
key={linkId}
62+
onClick={handleMenuClose}
63+
>
64+
<NewTabLink link={`https://civicdb.org/links/evidence/${linkId}`} text={linkId} />
65+
</MenuItem>
66+
));
67+
68+
if (links.length > 1) {
2669
return (
27-
<NewTabLink link={link} text={text} />
70+
<>
71+
<span>{text ? `${text},` : '' }</span>
72+
<button
73+
type="button"
74+
style={{
75+
backgroundColor: 'unset',
76+
border: 'none',
77+
}}
78+
aria-label="Open in CIViC"
79+
title="Open in CIViC"
80+
className="new-tab-link"
81+
onClick={handleMenuOpen}
82+
>
83+
CIViC
84+
</button>
85+
<Menu
86+
anchorEl={anchorEl}
87+
open={Boolean(anchorEl)}
88+
onClose={handleMenuClose}
89+
>
90+
{menuItems}
91+
</Menu>
92+
</>
2893
);
2994
}
30-
return (
31-
<div>{text}</div>
32-
);
95+
96+
if (link) {
97+
return <NewTabLink link={link} text={text} />;
98+
}
99+
return <div>{text}</div>;
33100
};
34101

35102
export default CivicCellRenderer;
103+
export { CivicCellRendererProps };

app/views/ReportView/components/CopyNumber/columnDefs.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
const columnDefs = [{
1+
// eslint-disable-next-line import/no-extraneous-dependencies
2+
import { ColDef } from '@ag-grid-community/core';
3+
4+
const columnDefs: ColDef[] = [{
25
headerName: 'Gene',
36
cellRenderer: 'GeneCellRenderer',
47
cellRendererParams: { link: true },
@@ -34,18 +37,29 @@ const columnDefs = [{
3437
hide: false,
3538
}, {
3639
headerName: 'Expression (RPKM)',
37-
field: 'gene.expressionVariants.rpkm',
38-
hide: false,
40+
colId: 'rpkm',
41+
valueGetter: 'data.gene.expressionVariants.rpkm',
42+
hide: true,
3943
}, {
4044
headerName: 'Expression (Normal FC)',
4145
colId: 'primarySiteFoldChange',
42-
field: 'gene.expressionVariants.primarySiteFoldChange',
43-
hide: false,
46+
valueGetter: 'data.gene.expressionVariants.primarySiteFoldChange',
47+
hide: true,
4448
}, {
4549
headerName: 'Expression (Perc)',
4650
colId: 'diseasePercentile',
47-
field: 'gene.expressionVariants.diseasePercentile',
51+
valueGetter: 'data.gene.expressionVariants.diseasePercentile',
4852
hide: false,
53+
}, {
54+
headerName: 'Expression (TPM)',
55+
colId: 'tpm',
56+
valueGetter: 'data.gene.expressionVariants.tpm',
57+
hide: true,
58+
}, {
59+
headerName: 'Expression (kIQR)',
60+
colId: 'primarySitekIQR',
61+
valueGetter: 'data.gene.expressionVariants.primarySitekIQR',
62+
hide: true,
4963
}, {
5064
headerName: 'Oncogene',
5165
colId: 'oncogene',

app/views/ReportView/components/CopyNumber/index.tsx

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ const TITLE_MAP = {
2626
lowExp: 'Lowly Expressed Tumour Suppressors with Copy Losses',
2727
};
2828

29-
const getInfoDescription = (relevance: string) => `Copy variants where the variant matched 1 or
30-
more statements of ${relevance} relevance in the knowledge base matches section. Details on these
31-
matches can be seen in the knowledge base matches section of this report.`;
29+
const getInfoDescription = (relevance: string) => `Copy variants where the variant matched 1 or
30+
more statements of ${relevance} relevance in the knowledge base matches section. Details on these
31+
matches can be seen in the knowledge base matches section of this report.`;
3232

3333
const INFO_BUBBLES = {
3434
biological: getInfoDescription('biological'),
@@ -60,6 +60,13 @@ const CopyNumber = ({
6060
highExp: [],
6161
lowExp: [],
6262
});
63+
const [visibleCols, setVisibleCols] = useState<string[]>(
64+
columnDefs.reduce((accumulator: string[], current) => {
65+
if (current.hide === false || !current.hide) {
66+
accumulator.push(current.field ?? current.colId);
67+
} return accumulator;
68+
}, []),
69+
);
6370

6471
useEffect(() => {
6572
if (report) {
@@ -69,7 +76,36 @@ const CopyNumber = ({
6976
api.get(`/reports/${report.ident}/copy-variants`),
7077
api.get(`/reports/${report.ident}/image/retrieve/cnvLoh.circos,cnv.1,cnv.2,cnv.3,cnv.4,cnv.5,loh.1,loh.2,loh.3,loh.4,loh.5`),
7178
]);
72-
const [cnvsResp, imagesResp] = await apiCalls.request();
79+
const [cnvsResp, imagesResp] = await apiCalls.request() as [CopyNumberType[], ImageType[]];
80+
81+
if (cnvsResp?.length) {
82+
const nextVisible = [];
83+
for (const {
84+
gene: {
85+
expressionVariants: {
86+
tpm, rpkm, primarySiteFoldChange, primarySitekIQR,
87+
},
88+
},
89+
} of cnvsResp) {
90+
/* Show either RPKM or TPM columns based on which is populated */
91+
if (tpm !== null && !nextVisible.includes('tpm')) {
92+
nextVisible.push('tpm');
93+
}
94+
if (rpkm !== null && !nextVisible.includes('rpkm')) {
95+
nextVisible.push('rpkm');
96+
}
97+
if (primarySiteFoldChange !== null && !nextVisible.includes('primarySiteFoldChange')) {
98+
nextVisible.push('primarySiteFoldChange');
99+
}
100+
if (primarySitekIQR !== null && !nextVisible.includes('primarySitekIQR')) {
101+
nextVisible.push('primarySitekIQR');
102+
}
103+
if (nextVisible.length === 2) {
104+
break;
105+
}
106+
}
107+
setVisibleCols((prevVal) => [...prevVal, ...nextVisible]);
108+
}
73109

74110
const circosIndex = imagesResp.findIndex((img) => img.key === 'cnvLoh.circos');
75111
const [circosResp] = imagesResp.splice(circosIndex, 1);
@@ -151,6 +187,8 @@ const CopyNumber = ({
151187
}
152188
}, [cnvs]);
153189

190+
const handleVisibleColsChange = (change) => setVisibleCols(change);
191+
154192
return (
155193
<div className="copy-number">
156194
<Typography variant="h3">Copy Number Analyses</Typography>
@@ -177,6 +215,8 @@ const CopyNumber = ({
177215
rowData={value}
178216
titleText={TITLE_MAP[key]}
179217
demoDescription={INFO_BUBBLES[key]}
218+
visibleColumns={visibleCols}
219+
syncVisibleColumns={handleVisibleColsChange}
180220
/>
181221
</React.Fragment>
182222
))}

app/views/ReportView/components/SmallMutations/columnDefs.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
const columnDefs = [{
1+
// eslint-disable-next-line import/no-extraneous-dependencies
2+
import { ColDef } from '@ag-grid-community/core';
3+
4+
const columnDefs: ColDef[] = [{
25
headerName: 'Gene',
36
field: 'gene.name',
47
hide: false,
@@ -54,20 +57,31 @@ const columnDefs = [{
5457
hide: true,
5558
}, {
5659
headerName: 'Expression (RPKM)',
57-
field: 'gene.expressionVariants.rpkm',
58-
hide: false,
60+
colId: 'rpkm',
61+
valueGetter: 'data.gene.expressionVariants.rpkm',
62+
hide: true,
5963
}, {
6064
headerName: 'Expression (FC normal)',
61-
colId: 'foldChange',
62-
field: 'gene.expressionVariants.primarySiteFoldChange',
63-
hide: false,
65+
colId: 'primarySiteFoldChange',
66+
valueGetter: 'data.gene.expressionVariants.primarySiteFoldChange',
67+
hide: true,
68+
}, {
69+
headerName: 'Expression (TPM)',
70+
colId: 'tpm',
71+
valueGetter: 'data.gene.expressionVariants.tpm',
72+
hide: true,
6473
}, {
6574
headerName: 'Expression (Perc Disease)',
6675
colId: 'diseasePercentile',
67-
field: 'gene.expressionVariants.diseasePercentile',
68-
hide: false,
76+
valueGetter: 'data.gene.expressionVariants.diseasePercentile',
77+
}, {
78+
headerName: 'Expression (kIQR)',
79+
colId: 'primarySitekIQR',
80+
valueGetter: 'data.gene.expressionVariants.primarySitekIQR',
81+
hide: true,
6982
}, {
7083
headerName: 'Actions',
84+
colId: 'actions',
7185
cellRenderer: 'ActionCellRenderer',
7286
pinned: 'right',
7387
sortable: false,

0 commit comments

Comments
 (0)