Closed Frencil closed 8 years ago
@MrFlick - I've completed another pass over this branch that abstracts the model storage in state from being an array of conditions to a model object with a covariates array. Please take a look and let me know if it looks like I'm on the right track.
It's definitely the right track. But having function like LocusZoom.Instance.prototype.addModelCovariate
for every possible Model property doesn't make sense. Just expose the model object and the model object might have addCovariate
or addConditiningSNP
or setRegions
or setPhenotype
or whatever. And I still think we should move the rendering of the model outside of the panel instance to a different class that we can bring in or swap out for different applications. Or do we tell people to write new panel types do be able to change the rendering? Is that how we make that extensible?
Refactored branch to use more compartmentalized "Dashboard" model; all logic specific to covariates model building menu constrained to a single dashboard component definition. Pattern established for adding arbitrarily complex UI elements in a modular, isolated way. Squashing and merging.
Overview
This branch introduces UI and API hooks for more graceful control over model building with an initial emphasis on a list of covariates. Here are the underlying assumptions of this approach:
Approach
Primary "Model" Element
To begin, we need a UI element that serves as both an indicator to aggregate changes in our model as well as provide access to a menu with detail and controls. That is achieved in this approach by a new addition to the panels control element (the element that has buttons to move panels around vertically, remove panels, or access the panel description).
In the
controls
parameter for the panel layout we can now define whether a panel should show the conditional indicator:The
model
parameter withincontrols
defaults tofalse
for all panels, and so must be explicitly enabled. This also allows for explicit selection of which panels should show information about the model (e.g. the genes panel should never show it).The model element that appears in the controls area behaves as both an indicator of the model represented in the state (presently by how many covariates it has) and as a UI element to access and edit, for now at least, the covariates in the state. Clicking the model element bring up an HTML overlay similar to the description overlay (formalized in this branch as "menus"). This overlay is automatically populated with a table of elements that are a part of the model covariates array with the ability to remove them individually or remove all of them.
State and Model Covariates API Methods
This branch only goes so far as defining a standard method for storing an arbitrarily complex model object in the
state
that, at a minimum, includes an array for covariates. It is still up to the implementer extending custom data sources to use what's stored in the state appropriately for how they plan to execute a requests that implement, for example, conditional analysis using model covariates.The standard approach defined in this branch is to use the
model
object andcovariates
array:Since ideally a variety of different things can be covariates the working assumption is that entries in the covariates array will be whatever is appropriate.
There are new instance/plot level methods for working with
state.model.covariates
:addModelCovariate
- Accepts a single element (which can be anything) and adds it tostate.model.covariates
only if the element is not already present there (assumption here being that the same covariate should never be added twice ever)removeModelCovariateByIdx
- Accepts a numerical index and removes that covariate fromstate.model.covariates
(e.g. 0 would remove the first element)removeAllModelCovariates
- Emptystate.model.covariates
Each of the above methods includes a built-in trigger to
applyState()
. If there is a data source that is trained to alter its request based on the contents ofstate.model
this should result in an immediate request for new data.Additional Changes
DataLayer.applyDataMethods
Since elements in the conditions array can be anything it can be tricky to display them in a general way. The approach to that outlined in this branch is to define a new abstract method on the data layer prototype called
applyDataMethods
.By default
applyDataMethods
applies three methods to each element in a data set:toHTML()
- a generic method to try to return the value of the element's ID (as defined by which field is the ID field in the layout) as a stringgetDataLayer()
- returns a reference to the data layer that houses the data elementdeselect()
- deselects the element that the tool tip is attached to (if it's selected)This method also invokes
applyCustomDataMethods()
, a stub intended to be reimplemented as needed by data layers of particular types that may need other methods present on all elements in the data set (or to redefine the defaults - e.g. define a more specifictoHTML()
method).LocusZoom.getToolTipData
To make it easy to drop a button in a tool tip that allows for adding that tool tip's parent element to the conditional analysis it was doable but required some ugly code.
LocusZoom.getToolTipData
was created as a shortcut for going from the element context of any node in any tool tip to the actual data that tool tip represents.First off, the
createToolTip
method uses d3's data joins to actually join the data for the tool tip directly to the outermost HTML node representing the tool tip. Then this function can be called by any element within the tool tip using thethis
context (e.g. in the onclick handler for a button:console.log(LocusZoom.getToolTipData(this))
should log the data to the console).From there this can be wrapped in a call to
conditionOn
for the plot to make a button in a tooltip add the data tostate.condition
and fire the request for new data as seen in the standard layout:Presumably the ability to get the data behind a tool tip from an arbitrary element inside that tool tip would be useful for other applications in the future. The method works by recursively jumping to parent nodes until it finds one classed as a LocusZoom tool tip, so should work for arbitrarily complex tool tip contents.
Panel Control Buttons class
This branch implements a standardized class for panel control buttons, as we now have definitions for five buttons (panel move up, panel move down, remove panel, show panel description, and show conditional analysis menu). Many buttons have patterns that could be abstracted, in particular the creation of a "menu" object that can be populated arbitrarily. The panel description button uses the menu object and just fills it with the description HTML while the model button has a more structured method for dynamically building the menu's contents based on what's in
state.model
.This pattern was hit on when considering issue #75 - a menu for showing current data layers in the panel with UI to reorder them, toggle them on/off, or remove them. Such a menu could look very similar to the conditions menu and populate dynamically from the layout.
TODO