Skip to content

Commit

Permalink
Store formatted data in _fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Jun 7, 2022
1 parent 2ae48d6 commit 6626a98
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 58 deletions.
44 changes: 25 additions & 19 deletions packages/core/BaseFeatureWidget/BaseFeatureDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useState } from 'react'
import dompurify from 'dompurify'
import { ErrorBoundary } from 'react-error-boundary'
import {
Accordion,
Expand Down Expand Up @@ -31,6 +30,7 @@ const MAX_FIELD_NAME_WIDTH = 170

// these are always omitted as too detailed
const globalOmit = [
'_fmt',
'length',
'position',
'subfeatures',
Expand Down Expand Up @@ -239,28 +239,37 @@ const ArrayValue = ({
)
}
}
const toLocale = (n: number) => n.toLocaleString('en-US')

function CoreDetails(props: BaseProps) {
const { feature } = props
const { refName, start, end, strand } = feature as SimpleFeatureSerialized & {
const obj = feature as SimpleFeatureSerialized & {
start: number
end: number
strand: number
refName: string
_fmt: {
start?: number
end?: number
refName?: string
name?: string
}
}

// eslint-disable-next-line no-underscore-dangle
const formattedFeat = { ...obj, ...obj._fmt }
const { start, strand, end, refName } = formattedFeat

const strandMap: Record<string, string> = {
'-1': '-',
'0': '',
'1': '+',
}
const strandStr = strandMap[strand as number] ? `(${strandMap[strand]})` : ''
const displayStart = (start + 1).toLocaleString('en-US')
const displayEnd = end.toLocaleString('en-US')
const displayRef = refName ? `${refName}:` : ''
const str = strandMap[strand as number] ? `(${strandMap[strand]})` : ''
const displayedDetails: Record<string, any> = {
...feature,
length: (end - start).toLocaleString('en-US'),
position: `${displayRef}${displayStart}..${displayEnd} ${strandStr}`,
...formattedFeat,
length: toLocale(end - start),
position: `${refName}:${toLocale(start + 1)}..${toLocale(end)} ${str}`,
}

const coreRenderedDetails = [
Expand Down Expand Up @@ -423,7 +432,7 @@ function UriAttribute({
)
}

export const Attributes: React.FunctionComponent<AttributeProps> = props => {
export function Attributes(props: AttributeProps) {
const {
attributes,
omit = [],
Expand All @@ -432,9 +441,11 @@ export const Attributes: React.FunctionComponent<AttributeProps> = props => {
prefix = [],
} = props
const omits = [...omit, ...globalOmit]
const { _fmt, ...rest } = attributes
const formattedAttributes = { ...rest, ..._fmt }

const maxLabelWidth = generateMaxWidth(
Object.entries(attributes).filter(
Object.entries(formattedAttributes).filter(
([k, v]) => v !== undefined && !omits.includes(k),
),
prefix,
Expand All @@ -445,7 +456,7 @@ export const Attributes: React.FunctionComponent<AttributeProps> = props => {

return (
<>
{Object.entries(attributes)
{Object.entries(formattedAttributes)
.filter(([k, v]) => v !== undefined && !omits.includes(k))
.map(([key, value]) => {
const description = accessNested([...prefix, key], descriptions)
Expand Down Expand Up @@ -522,13 +533,8 @@ function isEmpty(obj: Record<string, unknown>) {
return Object.keys(obj).length === 0
}

function textContent(value: string) {
const node = document.createElement('div')
node.innerHTML = dompurify.sanitize(value)
return node.textContent || ''
}
function generateTitle(name: unknown, id: unknown, type: unknown) {
return [ellipses(textContent(`${name}` || `${id}`)), `${type}`]
return [ellipses(`${name}` || `${id}`), `${type}`]
.filter(f => !!f)
.join(' - ')
}
Expand All @@ -548,7 +554,7 @@ export const FeatureDetails = (props: {
getConf(session, ['featureDetails', 'sequenceTypes']) || defaultSeqTypes

return (
<BaseCard title={generateTitle(name as string, id, type)}>
<BaseCard title={generateTitle(name, id, type)}>
<Typography>Core details</Typography>
<CoreDetails {...props} />
<Divider />
Expand Down
55 changes: 33 additions & 22 deletions packages/core/BaseFeatureWidget/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@ import { ElementId } from '../util/types/mst'

const configSchema = ConfigurationSchema('BaseFeatureWidget', {})

function iterateSubfeatures(
obj: Record<string, unknown>,
interface Feat {
subfeatures?: Record<string, unknown>[]
}

function formatSubfeatures(
obj: Feat,
depth: number,
parse: (obj: Record<string, unknown>) => void,
currentDepth = 0,
returnObj = {} as Record<string, unknown>,
) {
if (obj.subfeatures) {
// @ts-ignore
obj.subfeatures = obj.subfeatures.map(sub => {
iterateSubfeatures(sub, parse)
return parse(sub)
})
if (depth <= currentDepth) {
return returnObj
}
returnObj.subfeatures = obj.subfeatures?.map(sub => {
formatSubfeatures(sub, depth, parse, currentDepth + 1, returnObj)
return parse(sub)
})
return returnObj
}

export default function stateModelFactory(pluginManager: PluginManager) {
Expand All @@ -26,6 +34,7 @@ export default function stateModelFactory(pluginManager: PluginManager) {
id: ElementId,
type: types.literal('BaseFeatureWidget'),
featureData: types.frozen(),
formattedFields: types.frozen(),
unformattedFeatureData: types.frozen(),
view: types.safeReference(
pluginManager.pluggableMstType('view', 'stateModel'),
Expand All @@ -45,7 +54,7 @@ export default function stateModelFactory(pluginManager: PluginManager) {
clearFeatureData() {
self.featureData = undefined
},
setFormattedFeatureData(feat: Record<string, unknown>) {
setFormattedData(feat: Record<string, unknown>) {
self.featureData = feat
},
}))
Expand All @@ -57,24 +66,26 @@ export default function stateModelFactory(pluginManager: PluginManager) {
const { unformattedFeatureData, track } = self
if (unformattedFeatureData) {
const feature = clone(unformattedFeatureData)
let trackExtra = {}

if (self.track) {
const ret = getConf(track, ['formatFields'], { feature })
if (ret) {
trackExtra = ret
}
if (track) {
// eslint-disable-next-line no-underscore-dangle
feature._fmt = getConf(track, ['formatDetails', 'feature'], {
feature,
})

const depth = getConf(track, ['formatDetails', 'depth'])

iterateSubfeatures(feature, subfeature => {
const r =
getConf(track, ['formatSubfeatureFields'], {
feature: subfeature,
}) || {}
return { ...subfeature, ...r }
formatSubfeatures(feature, depth, subfeature => {
// eslint-disable-next-line no-underscore-dangle
subfeature._fmt = getConf(
track,
['formatDetails', 'subfeatures'],
{ feature: subfeature },
)
})
}

self.setFormattedFeatureData({ ...feature, ...trackExtra })
self.setFormattedData(feature)
}
}),
)
Expand Down
32 changes: 19 additions & 13 deletions packages/core/pluggableElementTypes/models/baseTrackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,25 @@ export function createBaseTrackConfig(pluginManager: PluginManager) {
}),
displays: types.array(pluginManager.pluggableConfigSchemaType('display')),

formatFields: {
type: 'frozen',
description: 'adds extra fields to the feature details',
defaultValue: {},
contextVariable: ['feature'],
},

formatSubfeatureFields: {
type: 'frozen',
description: 'adds extra fields to the subfeatures of a feature',
defaultValue: {},
contextVariable: ['feature'],
},
formatDetails: ConfigurationSchema('FormatDetails', {
feature: {
type: 'frozen',
description: 'adds extra fields to the feature details',
defaultValue: {},
contextVariable: ['feature'],
},
subfeatures: {
type: 'frozen',
description: 'adds extra fields to the subfeatures of a feature',
defaultValue: {},
contextVariable: ['feature'],
},
depth: {
type: 'number',
defaultValue: 2,
description: 'depth to iterate on subfeatures',
},
}),
},
{
preProcessSnapshot: s => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1721,7 +1721,9 @@ Object {
"type": "LinearArcDisplay",
},
],
"formatFields": "jexl:{link:\\"https://google.com/?q=\\"+feature.name}",
"formatDetails": Object {
"feature": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>',random:'Wow'}",
},
"name": "GFF3Tabix genes",
"trackId": "gff3tabix_genes",
"type": "FeatureTrack",
Expand Down
8 changes: 6 additions & 2 deletions test_data/config_demo.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@
"type": "FeatureTrack",
"trackId": "ncbi_gff_hg19",
"name": "NCBI RefSeq w/ subfeature details",
"formatSubfeatureFields": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>'}",
"formatDetails": {
"subfeatures": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>',random:'Wow'}"
},
"assemblyNames": ["hg19"],
"category": ["Annotation"],
"metadata": {
Expand All @@ -139,7 +141,9 @@
"type": "FeatureTrack",
"trackId": "ncbi_gff_hg19_2",
"name": "NCBI RefSeq w/ top-level feature details",
"formatFields": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>'}",
"formatDetails": {
"feature": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>',random:'Wow'}"
},
"assemblyNames": ["hg19"],
"category": ["Annotation"],
"metadata": {
Expand Down
4 changes: 3 additions & 1 deletion test_data/volvox/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,9 @@
"trackId": "gff3tabix_genes",
"assemblyNames": ["volvox"],
"name": "GFF3Tabix genes",
"formatFields": "jexl:{link:\"https://google.com/?q=\"+feature.name}",
"formatDetails": {
"feature": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>',random:'Wow'}"
},
"category": ["Miscellaneous"],
"adapter": {
"type": "Gff3TabixAdapter",
Expand Down

0 comments on commit 6626a98

Please sign in to comment.