Skip to content

Commit

Permalink
Add formatFields and formatFieldsNested
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Jun 3, 2022
1 parent 5d43d88 commit cdf0346
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 52 deletions.
48 changes: 20 additions & 28 deletions packages/core/BaseFeatureWidget/BaseFeatureDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ const MAX_FIELD_NAME_WIDTH = 170

// these are always omitted as too detailed
const globalOmit = [
'origId',
'origName',
'origType',
'length',
'position',
'subfeatures',
Expand Down Expand Up @@ -131,8 +134,8 @@ export function BaseCard({
export const FieldName = ({
description,
name,
prefix = [],
width,
prefix = [],
}: {
description?: React.ReactNode
name: string
Expand Down Expand Up @@ -343,10 +346,9 @@ const DataGridDetails = ({
const columns = colNames.map(val => ({
field: val,
width: Math.max(
...rows.map(row => {
const result = String(row[val])
return Math.min(Math.max(measureText(result, 14) + 50, 80), 1000)
}),
...rows.map(row =>
Math.min(Math.max(measureText(String(row[val]), 14) + 50, 80), 1000),
),
),
}))

Expand Down Expand Up @@ -526,22 +528,30 @@ function isEmpty(obj: Record<string, unknown>) {
return Object.keys(obj).length === 0
}

function generateTitle(name: string, id: string, type: string) {
return [ellipses(name || id), type].filter(f => !!f).join(' - ')
}

export const FeatureDetails = (props: {
model: IAnyStateTreeNode
feature: SimpleFeatureSerialized & { name?: string; id?: string }
feature: SimpleFeatureSerialized & {
origName?: string
origId?: string
origType?: string
}
depth?: number
omit?: string[]
formatter?: (val: unknown, key: string) => React.ReactElement
}) => {
const { omit = [], model, feature, depth = 0 } = props
const { name = '', id = '', type = '', subfeatures } = feature
const { origName = '', origId = '', origType = '', subfeatures } = feature
const session = getSession(model)
const defaultSeqTypes = ['mRNA', 'transcript']
const sequenceTypes =
getConf(session, ['featureDetails', 'sequenceTypes']) || defaultSeqTypes

return (
<BaseCard title={[ellipses(name || id), type].filter(f => !!f).join(' - ')}>
<BaseCard title={generateTitle(origName, origId, origType)}>
<Typography>Core details</Typography>
<CoreDetails {...props} />
<Divider />
Expand Down Expand Up @@ -585,8 +595,7 @@ export const FeatureDetails = (props: {

const BaseFeatureDetails = observer((props: BaseInputProps) => {
const { model } = props
const { featureData, track } = model
const session = getSession(model)
const { featureData } = model

if (!featureData) {
return null
Expand All @@ -596,24 +605,7 @@ const BaseFeatureDetails = observer((props: BaseInputProps) => {
return null
}

const sessionExtra = session
? getConf(session, ['featureDetails', 'extraFields'], {
feature,
})
: {}
const trackExtra = track
? getConf(track, ['extraFields'], {
feature,
})
: {}


return (
<FeatureDetails
model={model}
feature={{ ...feature, ...sessionExtra, ...trackExtra }}
/>
)
return <FeatureDetails model={model} feature={feature} />
})

export default BaseFeatureDetails
65 changes: 62 additions & 3 deletions packages/core/BaseFeatureWidget/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
import { types } from 'mobx-state-tree'
import PluginManager from '../PluginManager'
import { ConfigurationSchema } from '../configuration'
import { getConf, ConfigurationSchema } from '../configuration'
import { ElementId } from '../util/types/mst'

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

function isObject(obj: unknown): obj is Record<string, unknown> {
return typeof obj === 'object' && obj !== null
}

function iterate(
obj: Record<string, unknown>,
parse: (key: string, value: unknown, obj: Record<string, unknown>) => void,
) {
for (var k in obj) {
const r = obj[k]
if (isObject(r)) {
iterate(r, parse)
} else if (obj.hasOwnProperty(k)) {
parse(k, r, obj)
}
}
}

function iterateSubfeatures(
obj: Record<string, unknown>,
parse: (obj: Record<string, unknown>) => void,
) {
if (obj.subfeatures) {
//@ts-ignore
obj.subfeatures = obj.subfeatures.map(sub => {
iterateSubfeatures(sub, parse)
return parse(sub)
})
}
}

export default function stateModelFactory(pluginManager: PluginManager) {
return types
.model('BaseFeatureWidget', {
Expand All @@ -22,8 +53,36 @@ export default function stateModelFactory(pluginManager: PluginManager) {
),
})
.actions(self => ({
setFeatureData(data: Record<string, unknown>) {
self.featureData = data
setFeatureData(feature: Record<string, unknown>) {
let trackExtra = {}

iterate(feature, (key, val, obj) => {
if (key === 'id') {
obj.origId = val
}
if (key === 'name') {
obj.origName = val
}
if (key === 'type') {
obj.origType = val
}
})

if (self.track) {
const ret = getConf(self.track, ['formatFields'], { feature })
if (ret) {
trackExtra = ret
}

iterateSubfeatures(feature, obj => {
const r = getConf(self.track, ['formatFieldsNested'], {
feature: obj,
})
return r ? { ...obj, ...r } : obj
})
}

self.featureData = { ...feature, ...trackExtra }
},
clearFeatureData() {
self.featureData = undefined
Expand Down
29 changes: 13 additions & 16 deletions packages/core/pluggableElementTypes/models/baseTrackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,19 @@ export function createBaseTrackConfig(pluginManager: PluginManager) {
),
}),
displays: types.array(pluginManager.pluggableConfigSchemaType('display')),
// see corresponding entry in circular-view ChordTrack
// no config slot editor exists for this at the time being
// configRelationships: {
// type: 'configRelationships',
// model: types.array(
// types.model('Relationship', {
// type: types.string,
// target: types.maybe(types.reference(base)),
// }),
// ),
// defaultValue: [],
// },
extraFields: {
type: 'string',
description: 'modifies on the base feature details',
defaultValue: `jexl:{}`,

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

formatFieldsNested: {
type: 'frozen',
description:
'adds extra fields to the base feature details, applied across nested subfeatures',
defaultValue: {},
contextVariable: ['feature'],
},
},
Expand Down
4 changes: 1 addition & 3 deletions packages/core/util/jexl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,7 @@ export default function (/* config?: any*/): JexlNonBuildable {
)
j.addFunction('toLowerCase', (str: string) => str.toLowerCase())
j.addFunction('toUpperCase', (str: string) => str.toUpperCase())
j.addFunction('trim', (str: string) => {
str.trim()
})
j.addFunction('trim', (str: string) => str.trim())
j.addFunction('trimEnd', (str: string) => str.trimEnd())
j.addFunction('trimStart', (str: string) => str.trimStart())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,12 +335,14 @@ export const BaseLinearDisplay = types
'BaseFeatureWidget',
'baseFeature',
{
featureData: feature.toJSON(),
view: getContainingView(self),
display: self,
track: getContainingTrack(self),
},
)

// @ts-ignore
featureWidget.setFeatureData(feature.toJSON())
session.showWidget(featureWidget)
}
if (isSelectionContainer(session)) {
Expand Down
29 changes: 28 additions & 1 deletion test_data/config_demo.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,31 @@
{
"type": "FeatureTrack",
"trackId": "ncbi_gff_hg19",
"name": "NCBI RefSeq (GFF3Tabix)",
"name": "NCBI RefSeq w/ nested feature details",
"formatFieldsNested": "jexl:{name:\"https://google.com/?q=\"+feature.id}",
"assemblyNames": ["hg19"],
"category": ["Annotation"],
"metadata": {
"source": "https://www.ncbi.nlm.nih.gov/genome/guide/human/",
"dateaccessed": "12/03/2020"
},
"adapter": {
"type": "Gff3TabixAdapter",
"gffGzLocation": {
"uri": "https://s3.amazonaws.com/jbrowse.org/genomes/hg19/ncbi_refseq/GRCh37_latest_genomic.sort.gff.gz"
},
"index": {
"location": {
"uri": "https://s3.amazonaws.com/jbrowse.org/genomes/hg19/ncbi_refseq/GRCh37_latest_genomic.sort.gff.gz.tbi"
}
}
}
},
{
"type": "FeatureTrack",
"trackId": "ncbi_gff_hg19_2",
"name": "NCBI RefSeq w/ top-level feature details",
"formatFields": "jexl:{name:\"https://google.com/?q=\"+feature.name,random:\"Wow\"}",
"assemblyNames": ["hg19"],
"category": ["Annotation"],
"metadata": {
Expand Down Expand Up @@ -116,6 +140,9 @@
"type": "FeatureTrack",
"trackId": "nclist_genes_hg19",
"name": "Gencode v19",
"formatFields": "jexl:{name:\"https://civicdb.org/events/genes/\"+feature.gene_id+\"/summary/variants/\"+feature.id+\"/summary\"}",
"formatFieldsNested": "jexl:{name:\"https://civicdb.org/events/genes/\"+feature.gene_id+\"/summary/variants/\"+feature.id+\"/summary\"}",

"assemblyNames": ["hg19"],
"category": ["Annotation"],
"adapter": {
Expand Down

0 comments on commit cdf0346

Please sign in to comment.