diff --git a/packages/core/BaseFeatureWidget/BaseFeatureDetail/index.tsx b/packages/core/BaseFeatureWidget/BaseFeatureDetail/index.tsx index f9a78ffe0c..2bc7ee8a65 100644 --- a/packages/core/BaseFeatureWidget/BaseFeatureDetail/index.tsx +++ b/packages/core/BaseFeatureWidget/BaseFeatureDetail/index.tsx @@ -590,6 +590,7 @@ export function FeatureDetails(props: { feature: SimpleFeatureSerialized depth?: number omit?: string[] + descriptions?: Record formatter?: (val: unknown, key: string) => React.ReactNode }) { const { omit = [], model, feature, depth = 0 } = props diff --git a/plugins/variants/src/VariantFeatureWidget/AnnotGrid.tsx b/plugins/variants/src/VariantFeatureWidget/AnnotGrid.tsx index a1f7a2d052..d2bd94bd1f 100644 --- a/plugins/variants/src/VariantFeatureWidget/AnnotGrid.tsx +++ b/plugins/variants/src/VariantFeatureWidget/AnnotGrid.tsx @@ -6,6 +6,8 @@ import { GridValidRowModel, } from '@mui/x-data-grid' import { Checkbox, FormControlLabel, Typography } from '@mui/material' +import ResizeBar, { useResizeBar } from '@jbrowse/core/ui/ResizeBar' +import { measureGridWidth } from '@jbrowse/core/util' export default function VariantAnnotPanel({ rows, @@ -14,17 +16,14 @@ export default function VariantAnnotPanel({ rows: GridValidRowModel[] columns: GridColDef[] }) { - const rowHeight = 25 - const headerHeight = 100 - const footerHeight = 50 + const { ref, scrollLeft } = useResizeBar() const [checked, setChecked] = useState(false) - const height = - Math.min(rows.length, 100) * rowHeight + - headerHeight + - (checked ? 50 : 0) + - footerHeight + const [widths, setWidths] = useState( + columns.map(e => measureGridWidth(rows.map(r => r[e.field]))), + ) + return rows.length ? ( -
+
Show options} /> -
+
+ ({ ...c, width: widths[i] }))} slots={{ toolbar: checked ? GridToolbar : null }} />
diff --git a/plugins/variants/src/VariantFeatureWidget/VariantAnnotationTable.tsx b/plugins/variants/src/VariantFeatureWidget/VariantAnnotationTable.tsx new file mode 100644 index 0000000000..2e88acd83f --- /dev/null +++ b/plugins/variants/src/VariantFeatureWidget/VariantAnnotationTable.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import { BaseCard } from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail' +import AnnotGrid from './AnnotGrid' + +export default function VariantAnnotationTable({ + data, + fields, + title, +}: { + data: string[] + fields: string[] + title: string +}) { + return data.length ? ( + + ({ + id, + ...Object.fromEntries(elt.split('|').map((e, i) => [fields[i], e])), + })) || [] + } + columns={fields.map(c => ({ field: c }))} + /> + + ) : null +} diff --git a/plugins/variants/src/VariantFeatureWidget/VariantFeatureWidget.tsx b/plugins/variants/src/VariantFeatureWidget/VariantFeatureWidget.tsx index dd226f8f98..ee360081b4 100644 --- a/plugins/variants/src/VariantFeatureWidget/VariantFeatureWidget.tsx +++ b/plugins/variants/src/VariantFeatureWidget/VariantFeatureWidget.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import React from 'react' import { observer } from 'mobx-react' import { Divider, Paper } from '@mui/material' @@ -7,25 +6,31 @@ import { parseBreakend } from '@gmod/vcf' // locals import VariantSampleGrid from './VariantSampleGrid' -import VariantCsqPanel from './VariantCsqPanel' -import VariantAnnPanel from './VariantAnnPanel' import BreakendPanel from './BreakendPanel' +import VariantAnnotationTable from './VariantAnnotationTable' +import { SimpleFeatureSerialized } from '@jbrowse/core/util' -function VariantFeatureDetails(props: any) { +const basicDescriptions = { + CHROM: 'chromosome: An identifier from the reference genome', + POS: 'position: The reference position, with the 1st base having position 1', + ID: 'identifier: Semi-colon separated list of unique identifiers where available', + REF: 'reference base(s): Each base must be one of A,C,G,T,N (case insensitive).', + ALT: 'alternate base(s): Comma-separated list of alternate non-reference alleles', + QUAL: 'quality: Phred-scaled quality score for the assertion made in ALT', + FILTER: + 'filter status: PASS if this position has passed all filters, otherwise a semicolon-separated list of codes for filters that fail', +} + +function VariantFeatureDetails(props: { + model: { + featureData: SimpleFeatureSerialized + descriptions: Record + } +}) { const { model } = props const { featureData, descriptions } = model const feat = JSON.parse(JSON.stringify(featureData)) const { samples, ...rest } = feat - const basicDescriptions = { - CHROM: 'chromosome: An identifier from the reference genome', - POS: 'position: The reference position, with the 1st base having position 1', - ID: 'identifier: Semi-colon separated list of unique identifiers where available', - REF: 'reference base(s): Each base must be one of A,C,G,T,N (case insensitive).', - ALT: 'alternate base(s): Comma-separated list of alternate non-reference alleles', - QUAL: 'quality: Phred-scaled quality score for the assertion made in ALT', - FILTER: - 'filter status: PASS if this position has passed all filters, otherwise a semicolon-separated list of codes for filters that fail', - } return ( @@ -35,9 +40,9 @@ function VariantFeatureDetails(props: any) { {...props} /> - + - + {feat.type === 'breakend' ? ( + ) +} + +function CsqPanel({ + descriptions, + feature, +}: { + descriptions: { INFO?: { CSQ?: { Description?: string } } } + feature: { INFO?: { CSQ?: string[] } } +}) { + const csqDescription = descriptions?.INFO?.CSQ?.Description + const csqFields = + csqDescription?.match(/.*Format: (.*)/)?.[1].split('|') || [] + const csq = (feature.INFO?.CSQ || []) as string[] + return ( + + ) +} + export default observer(VariantFeatureDetails) diff --git a/plugins/variants/src/VariantFeatureWidget/VariantSampleGrid.tsx b/plugins/variants/src/VariantFeatureWidget/VariantSampleGrid.tsx index e49dc63ccc..49f9a67b3c 100644 --- a/plugins/variants/src/VariantFeatureWidget/VariantSampleGrid.tsx +++ b/plugins/variants/src/VariantFeatureWidget/VariantSampleGrid.tsx @@ -54,8 +54,7 @@ function SampleFilters({ export default function VariantSamples(props: { feature: SimpleFeatureSerialized - // eslint-disable-next-line @typescript-eslint/no-explicit-any - descriptions: any + descriptions: { FORMAT?: { [key: string]: { Description?: string } } } }) { const { feature, descriptions = {} } = props const { ref, scrollLeft } = useResizeBar()