Make a custom chart interactive
A grasp of these concepts will help you understand this documentation better:
Custom charts can support the same interactions as Holistics's built-in charts:
How interactions work
Enabling an interaction takes two steps:
- Declare what your chart accepts, in Selections
- Wire it to a Holistics feature, via
holisticsConfig
Selections
Selections declare what a user can do, such as clicking a data point or dragging to select a range. You define them with Vega-Lite params. Vega-Lite calls a selection a signal.
A selection is either point-based or interval-based.
-
Point selection (click). A point selection fires when a user clicks a single value (left click), or selects several values (hold Shift and left click). To deselect, the user clicks a selected value again. Read more in the Vega-Lite selection docs.
-
Interval selection (drag). An interval selection fires when a user holds left click and drags across a range of values. To deselect, the user double-clicks anywhere on the chart.
holisticsConfig
This property wires those selections to Holistics features.
Two fields matter:
crossFilterSignals: selections that trigger Cross Filter on click.contextMenuSignals: selections that open the context menu (Date-drill, Drill-through, View underlying data) on right-click. Use a hover selection here so the menu targets the point under the cursor.
For the full property reference, see holisticsConfig and params.
Interaction examples
Each example below starts from a working chart and adds one interaction. The full, copy-ready code for each one lives in the Custom Chart Library.
Highlight selected columns on point selection
Outcome. When a user selects one or more columns, those columns stay fully visible while the rest fade out. On hover, a stroke outlines the column.
-
In your template, declare two point selections in
params: one that fires on click, and one that fires on hover.template: @vgl {..."params": [// effective only while the user hovers{ "name": "pointSelectionOnMouseOver", "select": {"type": "point", "on": "mouseover"} },// effective when the user clicks{ "name": "normalPointSelection", "select": "point" }]...} -
Add a
fillOpacityrule so selected columns keep full opacity while the rest fade.template: @vgl {..."mark": {"fill": "#484848" // base color of the columns},"encoding": {"fillOpacity": {// selected columns stay fully visible"condition": {"param": "normalPointSelection", "value": 1},// unselected columns fade to 30%"value": 0.3}}...} -
Try it. Click a single column, then select a range by holding Shift and clicking. The selected columns should stay highlighted.
-
Add a
strokeWidthrule so hovered columns get a light stroke and selected columns get a bolder one.template: @vgl {..."mark": {"fill": "#484848","stroke": "black" // stroke color},"encoding": {"strokeWidth": {"condition": [// bolder stroke (2) on selected columns{ "param": "normalPointSelection", "empty": false, "value": 2 },// light stroke (1) on hovered columns{ "param": "pointSelectionOnMouseOver", "empty": false, "value": 1 }],"value": 0 // no stroke by default}}...} -
Try selecting and hovering again. You should see a light stroke on hovered columns and a bolder stroke on selected ones.
Full code example: Highlight stroke effect on point selection.
Cross-filter on point selection
Outcome. When a user selects data on your chart, a cross filter for that selection is applied to every widget in the dashboard.
Prerequisite: a chart with a point selection declared. The highlight example above is a good starting point.
-
In your template, declare a point selection in
params.template: @vgl {..."params": [// effective when the user clicks{ "name": "normalPointSelection", "select": "point" }]...} -
Add a
holisticsConfigfield withcrossFilterSignals, and list the point selection from step 1.template: @vgl {..."holisticsConfig": {"crossFilterSignals": ["normalPointSelection"]}...} -
Try it. Click a single column, or select a range with Shift and click. A cross filter should be created and applied to the other reports in your dashboard.
Full code example: Cross Filter on point selection.
Cross-filter on interval selection
Outcome. When a user drags to select an interval on your chart, a cross filter for that range is applied to every widget in the dashboard.
Prerequisite: a working custom chart you can add a selection to.
-
In your template, declare an interval selection in
params.template: @vgl {..."params": [{"name": "intervalSelection","select": {"type": "interval", "encodings": ["x"]}}]...} -
Add a
holisticsConfigfield withcrossFilterSignals, and list the interval selection from step 1.template: @vgl {..."holisticsConfig": {"crossFilterSignals": ["intervalSelection"]}...} -
Try it. Hold left click and drag across a range of values. A cross filter should be created and applied to the other reports in your dashboard.
Full code example: Cross Filter on interval selection.
Cross-filter all sub-charts in a combo chart
Outcome. A selection on a combo (mixed) chart applies the cross filter to every sub-chart in the layer, not just the one the user interacted with.
Prerequisite: a combo chart built with a Vega-Lite layer. See Cross Filter on sub-charts in a combo chart for how to build one.
-
In your template, find the sub-chart in the
layerarray that the cross filter should also apply to.template: @vgl {..."layer": [// your bar chart definition{ ... },// your line chart definition (the one we'll filter){ ... }]...} -
Add a
transformto that sub-chart with afilterthat takes the user's selection as itsparam.template: @vgl {..."layer": [{ ... }, // your bar chart definition{"transform": [// filter this sub-chart by the user's interval selection{ "filter": {"param": "intervalSelection"} }]// ...the rest of the sub-chart definition}]...} -
Create a cross filter on your dashboard and confirm the sub-chart updates along with the rest.
Full code example: Cross Filter on sub-charts in a combo chart.
Open the context menu on point selection
Prerequisite: a chart you can add a selection to (see the cross-filter example).
The context menu opens on right-click, so it must read the point currently under the cursor. That means it needs a hover selection, not the click selection you use for Cross Filter. (A click selection has no target on right-click unless the user left-clicks the point first, which would also cross-filter.)
-
In your template, declare a hover point selection in
params. The"on": "mouseover"trigger makes it track whichever point the cursor is over.template: @vgl {..."params": [{ "name": "hoverPointSelection", "select": {"type": "point", "on": "mouseover", "clear": "mouseout"} }]...} -
Add a
holisticsConfigfield withcontextMenuSignals, and list the hover selection from step 1.template: @vgl {..."holisticsConfig": {"contextMenuSignals": ["hoverPointSelection"]}...}
Steps 1–2 wire up the whole context menu at once, so every option below needs them. What each option needs on top of that:
Drill-through
Outcome. Right-clicking a data point opens Drill-through in the context menu.
Prerequisite: at least one dimension field. Drill-through passes dimension values as filters to the target dashboard, so a chart with no dimensions has nothing to drill with.
Additional setup: none.
Date-drill
Outcome. Right-clicking a data point opens Date-drill in the context menu.
Prerequisite: a date or datetime dimension field. Date-drill changes the time granularity of that field, so the chart needs a date field to drill on.
Additional setup: none.
View underlying data
Outcome. The context menu's View underlying data option resolves the full data row behind the point you right-clicked.
Prerequisite: none beyond steps 1–2 above.
Additional setup: widen the selection's fields array to include at least 1 measure/metric and 1 dimension. Otherwise Holistics can't resolve the full underlying row:
template: @vgl {
...
"params": [
{
"name": "hoverPointSelection",
"select": {
"type": "point",
"fields": [@{fields.category.name}, @{fields.value.name}], // include the metric field
"on": "mouseover",
"clear": "mouseout"
}
}
]
...
}
If that selection is already scoped to one field for another purpose and you don't want to widen it, declare a second selection with the full field list instead, and list both signal names in contextMenuSignals.