Extension points
The basic API is that producers can say:
const ret = pluginManager.evaluateExtensionPoint('ExtensionPointName', {
value: 1,
})
And consumers can say:
pluginManager.addToExtensionPoint('ExtensionPointName', arg => {
return arg.value + 1
})
pluginManager.addToExtensionPoint('ExtensionPointName', arg => {
return arg.value + 1
})
In this case, arg
that is passed in evaluateExtensionPoint calls all the
callbacks that have been registered by addToExtensionPoint
. If multiple
extension points are registered, the return value of the first extension point
is passed as the new argument to the second, and so on (they are chained
together).
So in the example above, ret would be {value:3}
after evaluating the extension
point.
In the core codebase, we have the concept of extension points that users can call or add to.
The API is
// extra props are optional, can pass an extra context object your extension
// point receives
pluginManager.evaluateExtensionPoint(extensionPointName, args, props)
Args are 'accumulated' (e.g. the return value of your extension point is passed along to the args argument of the next one), and props are just passed along
There is also an async method:
// extra props are optional, can pass an extra context object your extension
// point receives
pluginManager.evaluateAsyncExtensionPoint(extensionPointName, args, props)
Users can additionally add to extension points, so that when they are evaluated, it runs a chain of callbacks that are registered to that extension point:
pluginManager.addToExtensionPoint(extensionPointName, callback => newArgs)
The newArgs returned by your callback are passed on as the args to the next in the chain.
Here are the extension points in the core codebase:
Core-extendPluggableElement
args
-pluggableElement:PluggableElement
- thisprops
- none
type: synchronous
used to add extra functionality to e.g state tree models, for example, extra right-click context menus. your callback will receive every pluggable element registered to the system
Core-guessAdapterForLocation
type: synchronous
args
- adapter config
used to infer an adapter type given a location type from the "Add track" workflow. you will receive a callback asking if you can provide an adapter config given a location object
Core-guessTrackTypeForLocation
type: synchronous
args
-FileLocation
object
used to infer a track type given a location type from the "Add track workflow"
Core-extendSession
type: synchronous
used to extend the session model itself with new features
args
-AbstractSessionModel
- instance of the session model to customize the about dialog
Core-replaceAbout
type: synchronous
adds option to provide a different component for the "About this track" dialog
args
- aReactComponent
, by default the AboutTrack dialogprops
- an argument of the format below
interface props {
session: AbstractSessionModel
config: AnyConfigurationModel
}
Example: returns a new about track dialog for a particular track
pluginManager.addToExtensionPoint(
'Core-replaceAbout',
(DefaultAboutComponent, { session, config }) => {
return config.trackId === 'volvox.inv.vcf'
? NewAboutComponent
: DefaultAboutComponent
},
)
Core-extraAboutPanel
type: synchronous
adds option to provide a different component for the "About this track" dialog
interface props {
session: AbstractSessionModel
config: AnyConfigurationModel
}
Return value: An object with the name of the panel and the React component to use for the panel
Example: adds an extra about dialog panel for a particular track ID
pluginManager.addToExtensionPoint(
'Core-extraAboutPanel',
(DefaultAboutExtra, { session, config }) => {
return config.trackId === 'volvox_sv_test'
? { name: 'More info', Component: ExtraAboutPanel }
: DefaultAboutExtra
},
)
Core-customizeAbout
type: synchronous
args
- a config snapshotRecord<string, unknown>
for the track, withformatAbout
already applied to it
Return value: New config snapshot object
Core-replaceWidget
type: synchronous
adds option to provide a different component for a given widget, drawer or modal
args
- aReactComponent
props
- an object of the type below
interface props {
session: AbstractSessionModel
model: WidgetModel
}
See also: Core-extraFeaturePanel
Return value: The new React component you want to use
Note: Core-replaceWidget is called any time any widget opens, so if you are trying to only customize e.g. the feature details widget, you can filter on widget.trackId because only feature detail widgets has a 'trackId' field. You can filter on widget.type also but this is stringly typed, and may vary depending on track type.
Example of Core-replaceWidget - add widget above the default widget
pluginManager.addToExtensionPoint(
'Core-replaceWidget',
(DefaultWidget, { model }) => {
// replace widget for this particular track ID
return model.trackId !== 'volvox.inv.vcf'
? DefaultWidget
: function NewWidget(props) {
// this new widget adds a custom panel above the old DefaultWidget,
// but you can replace it with any contents that you want
return (
<div>
<div>Custom content here above the default details widget</div>
<DefaultWidget {...props} />
</div>
)
}
},
)
Note 1: it is not always possible to retrieve the configuration associated with a track that produced the feature details. Therefore, we check model.trackId that produced the popup instead.
Note 2: If you want e.g. a "User copy" of your track to get same treatment, might use a regex to loose match the trackId (the copy of a track will have a timestamp and -sessionTrack added to it).
Core-extraFeaturePanel
type: synchronous
args
- aReactComponent
, the default AboutTrack dialogprops
- an object of the type below
interface props {
model: BaseFeatureWidget // a widget model, has model.trackId defined if you want to check track
feature: Record<string, unknown> // snapshot of feature object
session: AbstractSessionModel
}
Note: the model has properties model.trackId
, model.trackType
, and
model.track
, though model.track
may be undefined if the user closed the
track, while trackId and trackType will be defined even if user closed the track
Return value: An object with the name of the panel and the React component to use for the panel
Example:
pluginManager.addToExtensionPoint(
'Core-extraFeaturePanel',
(DefaultFeatureExtra, { model }) => {
return model.trackId === 'volvox_filtered_vcf'
? { name: 'Extra info', Component: ExtraFeaturePanel }
: DefaultFeatureExtra
},
)
Core-preProcessTrackConfig
type: synchronous
args
-SnapshotIn<AnyConfigurationModel>
- Copy of the current track config
Return value: A new track config
Example:
pluginManager.addToExtensionPoint('Core-preProcessTrackConfig', snap => {
return {
...snap.metadata,
extraMetadata: 'extra metadata',
}
})
TrackSelector-multiTrackMenuItems
type: synchronous
args
-MenuItem[]
- an array of items that you can accumulate onprops
- an object of the form below
interface props {
session: AbstractSessionModel
}
used to add new menu items to the "shopping cart" in the header of the hierarchical track menu when tracks are added to the selection
LaunchView-LinearGenomeView
type: async
launches a linear genome view given parameters. it is not common to extend this extension point, but you can use it as an example to create a LaunchView type for your own view
args
- an object with the following format
interface args {
session: AbstractSessionModel // the session model
assembly: string // assembly name
loc: string // locstring
tracks: string[] // array of track IDs
}
LaunchView-CircularView
type: async
similar to LaunchView-LinearGenomeView, this is not common to extend, but you can use it as an example to create a LaunchView type for your own view
args
- an object with the following format
interface args {
session: AbstractSessionModel // the session model
assembly: string // assembly name
tracks: string[] // array of track IDs
}
LaunchView-SvInspectorView
type: async
launches a sv inspector with given parameters. it is not common to extend this extension point, but you can use it as an example to create a LaunchView type for your own view
args
- an object with the following format
interface args {
session: AbstractSessionModel // the session model
assembly: string // assembly name
uri: string // uri for file to load into the SV inspector
fileType?: string // type of file referred to by the uri ("VCF"|"CSV"|"BEDPE",etc) if uri extension does not properly hint at the file type
}
LaunchView-SpreadsheetView
type: async
launches a sv inspector with given parameters. it is not common to extend this extension point, but you can use it as an example to create a LaunchView type for your own view
args
- an object with the following format
interface args {
session: AbstractSessionModel // the session model
assembly: string // assembly name
uri: string // uri for file to load into the SV inspector
fileType?: string // type of file referred to by the uri ("VCF"|"CSV"|"BEDPE",etc) if uri extension does not properly hint at the file type
}
LaunchView-DotplotView
type: async
launches a dotplot with given parameters. it is not common to extend this extension point, but you can use it as an example to create a LaunchView type for your own view
interface args {
session: AbstractSessionModel // the session model
views: {
loc: string
assembly: string
tracks?: string[]
}[] // array of length 2, for vert and horiz
tracks: string[] // synteny track IDs to load on open
}
LaunchView-LinearSyntenyView
type: async
launches a linear synteny view with given parameters. it is not common to extend this extension point, but you can use it as an example to create a LaunchView type for your own view
interface args {
session: AbstractSessionModel // the session model
views: {
loc: string // locstring
assembly: string // assembly name
tracks?: string[] // trackIDs to open on top and bottom
}[] // array of length 2, for top and bottom rows of synteny view
tracks: string[] // synteny track IDs to load on open
}
LinearGenomeView-TracksContainer
type: synchronous
args
-React.ReactNode[]
- an array of rendered react components (ReactNode) which you can append toprops
- an object of the type below
interface props {
model: LinearGenomeViewModel // instance of the linear genome view model
}
Allows rendering a custom component as a child of the LinearGenomeView's "TracksContainer". Used to render highlights for example with a div of height 100% over the TracksContainer
Extension point footnote
Users that want to add further extension points can do so, by simply calling
const returnVal = pluginManager.evaluateExtensionPoint(
'YourCustomNameHere',
processThisValue,
extraContext,
)
Then, any code that had used:
pluginManager.addToExtensionPoint(
'YourCustomNameHere',
(processThisValue, extraContext) => {
/* the first arg is the "processThisValue" from the extension point, it may
get mutated if multiple extension points are chained together
the second argument to the extension point is the extra context from
evaluating the extension point. it does not get mutated even if there is a
chain of values, it is passed as is to each one*/
return processThisValue
},
)
The naming system, "Core-" just refers to the fact that these extension points are from our core codebase. Plugin developers may choose their own prefix to avoid collisions.