Skip to content

Commit e129500

Browse files
committed
Merge branch 'release/0.13.3'
2 parents f5afb00 + aaf4316 commit e129500

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1639
-8451
lines changed

assets/docs/interactivity.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,46 @@ Each LocusZoom rendering is controlled by a declarative set of layout options. I
6464
In practice, this is the key idea behind the `display_options` widget, a built-in feature that handles such mutations in a controlled fashion. If you are doing this using your own code, the following "gotchas" apply:
6565

6666
* When the layout is defined before first render, it uses abstract syntax (eg `{{namespace[assoc]field}}`. To modify an existing plot layout after it has been rendered, you will need to use concrete syntax in which the namespace has been filled in: `assoc2_mystudy:field`.
67-
* LocusZoom layouts are nested and hierarchical (plot --> panels[] --> data_layers[]). Writing a layout thus involves awkward syntax such as `layout.panels[0].data_layers[1]`. This is very fragile if you frequently add or rename panels: when writing code that modifies a layout, ask what you can do to make it maintainable.
68-
* Make it semantically clear which item is being modified, in case the array changes later: `const assoc_panel_layout = panel_layouts[0]`
69-
* Instead of hard-coding position in an array, consider dynamically locating the desired section by a property that may change less often: `const assoc_layer = assoc_panel_layout.data_layers.find(item => item.id === 'association')`
67+
* LocusZoom layouts are nested and hierarchical (plot --> panels[] --> data_layers[]). See the helper functions below for advice on how to write mutations that are more readable and maintainable.
7068
* Be conservative in how many fields you allow to be changed. Layouts allow almost any aspect of the plot to be customized, but it can be difficult to test every possible combination. It's generally easier to code and maintain controlled options (like a list of preset views).
7169

7270
After re-defining the layout, be sure to call `plot.applyState()` (also known as `plot.refresh()`) to trigger a re-render, so that the changes to the layout take effect.
7371

72+
### Helper functions for modifying nested layouts
73+
The "building block" style of layouts makes it easy to reuse pieces, but customizing part of a layout after rendering can be very clunky (example: `layout.panels[0].data_layers[1]`). In particular, if the order of elements in the layout ever changed (like adding a new panel or toolbar button), then code that accessed items by array position would break in ways that are very hard to debug. This is a maintainability headache.
74+
75+
As an alternative, a helper function `LocusZoom.Layouts.mutate_attrs` can be used to modify all parts of a layout that match a selector, using a readable syntax based on the [JsonPath](https://goessner.net/articles/JsonPath/) query language. See the developer documentation for further details.
76+
77+
Examples:
78+
```javascript
79+
// Add a field to a data layer, taking into account what fields are already there. The third argument is a function that receives the old value and returns the new one
80+
> LocusZoom.Layouts.mutate_attrs(plot_layout, '$..data_layers[?(@.tag === "association")].fields', (old_value) => old_value.concat(['assoc:field1', 'assoc:field2']));
81+
82+
// When the user clicks a button on the page, change what field is used for the y-axis for all association scatter plots. In this syntax, all matches receive the same value (the last argument is a value, instead of a callable function).
83+
> LocusZoom.Layouts.mutate_attrs(existing_plot.layout, '$..data_layers[?(@.tag === "association")].y_axis.field', 'assoc:pip_cluster');
84+
> existing_plot.applyState();
85+
86+
// The mutation function is not limited to changing scalar values or lists. If the selector targets a compound object, the function can be used to modify several properties all at once. Make sure to return the resulting config object when done.
87+
> LocusZoom.Layouts.mutate_attrs(existing_plot.layout, '$..data_layers[?(@.tag === "phewas")].color[?(@.scale_function === "categorical_bin")]', function(options) { options.field = 'newfield'; options.parameters.null_value = 'red' ; return options; });
88+
89+
// For debugging purposes, there is a read-only function that can be used to verify that a selector works as expected. It will return a list, one item per result.
90+
> LocusZoom.Layouts.query_attrs(plot_layout, '$..id');
91+
```
92+
93+
*Notes:*
94+
95+
We do not implement the entire JsonPath specification. The syntax used by LocusZoom:
96+
97+
- DOES support single child (`.`), deep nested (`..`), and wild-card (`*`) accessors
98+
- DOES support filtering arrays-of-config-objects to only items that match a simple single-attribute-exact-match predicate (`$.panels_array[?(@.akeyhasvalue === "targetvalue")]`)
99+
- DOES support queries that nest/combine operators (`$..data_layers[?(@.tag === 'association')].fields`)
100+
- DOES NOT support complex JS expressions in predicates (which would be a security issue), or indexing array items. (writing layouts based on item[0] is a maintainability anti-pattern, and we are actively trying to discourage doing that)
101+
- The end result of all selectors used should be to return a specific key inside an object. Lists can be filtered, but not indexed.
102+
103+
Most pre-made data layer and panel layouts now contain a `tag` field, which can be used to write semantically meaningful selectors, like, "modify all scatter plots that show GWAS association data".
104+
105+
> This helper function is aimed at making quick changes to one or two fields (before render), or more complex customizations (after render). If you are trying to make complex customizations to a layout when it is first defined, it is often better to build up in pieces so that you have more control of the result. For example, customizing a single data layer as part of a layout: `LocusZoom.Layouts.get('data_layer', 'association', { id: 'customoverridevalue' })`.
106+
74107
## Events communicate with the outside world
75108
Each time that a LocusZoom plot is modified, it fires an *event* that notifies any listeners of the change.
76109

dist/ext/lz-aggregation-tests.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/ext/lz-credible-sets.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/ext/lz-dynamic-urls.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)