Customizing feature details
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": ["volvox"],
"name": "Genes",
"formatDetails": {
"feature": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>',extrafield:'Field added with custom callback:' + feature.name,phase:undefined,type:undefined}"
},
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
}
}
jbrowse add-track-json '{
"type": "FeatureTrack",
"trackId": "genes",
"assemblyNames": ["volvox"],
"name": "Genes",
"formatDetails": {
"feature": "jexl:{name:'\''<a href=https://google.com/?q='\''+feature.name+'\''>'\''+feature.name+'\''</a>'\'',extrafield:'\''Field added with custom callback:'\'' + feature.name,phase:undefined,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": ["volvox"],
"name": "Genes",
"formatDetails": {
"feature": "jexl:{name:'<a href=https://google.com/?q='+feature.name+'>'+feature.name+'</a>',extrafield:'Field added with custom callback:' + feature.name,phase:undefined,type:undefined}"
},
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
}
}
volvox.sort.gff3.gz is relative to a config.json. Replace it with its URL or its path on this computer.
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.- A non-object return is an error.
"jexl:feature.name"where"jexl:{name:feature.name}"was meant replaces the panel with a message naming the track.
The merged feature is what every part of the panel reads, the sequence panel and
a plugin's extra panels included, and a saved session keeps the raw feature and
re-runs the callbacks on load, so editing a track's formatDetails reshapes an
open panel in place.
What a callback can see
featureis the clicked feature, or insubfeaturesthe subfeature being formatted.trackis the track's config, so one session-wide callback can link out per assembly:"jexl:{ncbi:'https://www.ncbi.nlm.nih.gov/gene/?term='+feature.name+'+'+track.assemblyNames[0]}".- In
subfeatures,parentis the feature this one sits in anddepthhow far down, 1 for a gene's transcript and 2 for the transcript's exon, so a transcript row can carry its gene's name:"jexl:{gene:parent.name}".
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": ["volvox"],
"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": ["volvox"],
"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": ["volvox"],
"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
}
}
volvox.sort.gff3.gz is relative to a config.json. Replace it with its URL or its path on this computer.
Session-wide formatDetails
The feature, subfeatures, depth and maxDepth slots also 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": ["volvox"],
"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": ["volvox"],
"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": ["volvox"],
"adapter": {
"type": "Gff3TabixAdapter",
"uri": "volvox.sort.gff3.gz"
},
"formatAbout": {
"hideUris": true,
"config": "jexl:{Source:'GENCODE v44',adapter:undefined}"
}
}
volvox.sort.gff3.gz is relative to a config.json. Replace it with its URL or its path on this computer.
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