Customizing feature details
TL;DR: the track slot formatDetails takes jexl callbacks that return an
object of fields to merge onto a feature: a new key adds a row, an existing key
overrides it, and undefined/null hides it. formatAbout does the same for
the About track dialog. For complex logic, register a jexl function in a small
plugin.
{
"type": "FeatureTrack",
"trackId": "genes",
"assemblyNames": ["hg19"],
"name": "Genes",
"formatDetails": {
"feature": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>',newfield:'Custom contents here: '+feature.name,type:undefined }"
},
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
}
}
jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "genes",
"assemblyNames": ["hg19"],
"name": "Genes",
"formatDetails": {
"feature": "jexl:{name:'\''<a href=https://google.com/?q='\''+feature.name+'\''>'\''+feature.name+'\''</a>'\'',newfield:'\''Custom contents here: '\''+feature.name,type:undefined }"
},
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
}
}'
In JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add track from pasted JSON, and paste:
{
"type": "FeatureTrack",
"trackId": "genes",
"assemblyNames": ["hg19"],
"name": "Genes",
"formatDetails": {
"feature": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>',newfield:'Custom contents here: '+feature.name,type:undefined }"
},
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
}
}
The <a> markup is needed only because the link text differs from the URL; a
value that is nothing but a URL is linked for you. The slots
(feature, subfeatures, depth, maxDepth) are on the
formatDetails config docs.
How the returned object is applied
The callback's object is shallow-merged onto the feature, and the result drives the panel:
- A new key adds a row.
- An existing key replaces that row's value, core fields included:
{type: undefined}removes the Type row,{name: ...}rewrites the Name row.lengthcounts as one even though the panel computes it fromstart/end. undefinedornullhides the row.nullsurvives a serialization round-trip (saving a session turnsundefinedintonullanyway).- A non-object return drops the tier.
"jexl:feature.name"where"jexl:{name:feature.name}"was meant produces no rows.
Values are HTML, and bare URLs become links
Every value passes through an HTML sanitizer, so <b>, <a> and <table>
render as markup, and a value that is not recognizable HTML is escaped (a VCF
<TRA> allele still reads as <TRA>). A value that is just a URL becomes a
link on its own; the
cookbook's details recipe links a gene
to NCBI that way.
Static fields, depth and maxDepth
Only a string starting with jexl: is evaluated, so a field that is the same on
every feature is written as a plain object. A GFF3 gene nests three levels deep
(gene, mRNA, then exon and CDS), and two slots bound how far the panel goes:
depthbounds thesubfeaturescallback. At1it reformats only the transcript rows.maxDepthbounds the panel. At1it shows the transcript cards but not the exon and CDS cards inside them, formatted or not.
{
"type": "FeatureTrack",
"trackId": "genes_transcripts_only",
"name": "Genes",
"assemblyNames": ["hg19"],
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
},
"formatDetails": {
"feature": {
"Source": "GENCODE v44",
"Contact": "helpdesk@example.org",
"phase": null
},
"subfeatures": "jexl:{Transcript:feature.name, phase:undefined}",
"depth": 1,
"maxDepth": 1
}
}
jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "genes_transcripts_only",
"name": "Genes",
"assemblyNames": ["hg19"],
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
},
"formatDetails": {
"feature": {
"Source": "GENCODE v44",
"Contact": "helpdesk@example.org",
"phase": null
},
"subfeatures": "jexl:{Transcript:feature.name, phase:undefined}",
"depth": 1,
"maxDepth": 1
}
}'
In JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add track from pasted JSON, and paste:
{
"type": "FeatureTrack",
"trackId": "genes_transcripts_only",
"name": "Genes",
"assemblyNames": ["hg19"],
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
},
"formatDetails": {
"feature": {
"Source": "GENCODE v44",
"Contact": "helpdesk@example.org",
"phase": null
},
"subfeatures": "jexl:{Transcript:feature.name, phase:undefined}",
"depth": 1,
"maxDepth": 1
}
}
Session-wide formatDetails
The same four slots exist under
configuration.formatDetails, applied to every
track:
{
"configuration": {
"formatDetails": {
"feature": "jexl:{Assembly:'hg19', score:undefined}",
"subfeatures": "jexl:{Assembly:'hg19'}",
"maxDepth": 2
}
}
}
Where a track sets the same slot, feature and subfeatures objects are
merged (the track's keys over the session's) and depth/maxDepth
override (the track's value wins).
Complex logic goes in a jexl function
Register a function from a small plugin, as the
no-build plugin tutorial
shows, and call it from the slot:
"feature": "jexl:{name:formatName(feature)}". The function can return a whole
object (many attributes relabeled at once) or one value (a dbxref turned into
a link).
The feature a formatDetails callback receives is a plain object read from the
serialized session, so use property access (feature.start);
feature.get('start') fails here. See
property access vs get().
The About track dialog
formatAbout reshapes the "About track" dialog the same way, on the track or
session-wide as configuration.formatAbout. The
callback's variable is config, the track's own configuration:
{
"type": "FeatureTrack",
"trackId": "genes",
"name": "Genes",
"assemblyNames": ["hg19"],
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
},
"formatAbout": {
"hideUris": true,
"config": "jexl:{Source:'GENCODE v44',adapter:undefined}"
}
}
jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "genes",
"name": "Genes",
"assemblyNames": ["hg19"],
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
},
"formatAbout": {
"hideUris": true,
"config": "jexl:{Source:'\''GENCODE v44'\'',adapter:undefined}"
}
}'
In JBrowse Desktop, or in any running JBrowse Web session, open a view on this track’s assembly, then File → Open track..., choose Add track from pasted JSON, and paste:
{
"type": "FeatureTrack",
"trackId": "genes",
"name": "Genes",
"assemblyNames": ["hg19"],
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
},
"formatAbout": {
"hideUris": true,
"config": "jexl:{Source:'GENCODE v44',adapter:undefined}"
}
}
hideUrisdrops every file location from the dialog only;config.jsonstill carries them. A session-widetruecannot be turned back on by a track.configmerges over the config shown, exactly asformatDetails.featuredoes.
Adding a panel or replacing the widget
To add a section of your own or replace the widget wholesale, a plugin uses two extension points:
Core-extraFeaturePanelappends a React component below the built-in sectionsCore-replaceWidgetwraps or replaces the whole feature-details widget