Problem/Motivation
A common need in charting is the ability to visualize the relationship between the series values and a given value, such as a target, upper or lower limit, or fixed comparison. These can sometimes be plotted as series, but, especially when they are fixed, they are generally better as a plotline (this is the Highcharts term for this concept).
A quick search reveals that there is wide support for this concept among the different libraries supported by the Charts module:
| Library | Has Equivalent? | What It's Called |
|---|---|---|
| ApexCharts | Yes | annotations.xaxis / annotations.yaxis |
| Apache ECharts | Yes | series.markLine |
| C3.js | Yes | grid.x.lines / grid.y.lines |
| Chart.js | Yes (via Plugin) | annotations (type: 'line') |
| Billboard.js | Yes | grid.x.lines / grid.y.lines |
| Google Charts | No | Requires dummy data series or trendline |
| Kendo UI | Yes | valueAxis.notes.line (or plotBands) |
| Plotly | Yes | layout.shapes |
Proposed resolution
Add a conditional "Plot lines" section to the chart settings form (views, field, and block), shown only for the packaged libraries that support the concept (Highcharts, Chart.js, C3, Billboard). Contrib libraries are out of scope for this issue.
The section follows the same table format as the views data provider table. Each row is a candidate source with an enable checkbox, an orientation select (horizontal = value/y-axis, vertical = category/x-axis), a color, and a label:
- In views, the sources are the view fields; the line is drawn at the value the field provides.
- In field, block, and canvas block, the sources are the data series; an enabled series renders as a plot line instead of a regular series.
Charts does not aggregate the value. It is expected to arrive ready-made from the data source — Views aggregation, a module such as Views Descriptive Statistics, or a precomputed column (e.g. a threshold column in a CSV) — so the first value of the field/series is used.
The value is mapped to each library's native equivalent: Highcharts plotLines, C3/Billboard grid.x/y.lines, and Chart.js annotation lines. Because plot lines do not extend an axis's range, Highcharts and Chart.js also receive soft/suggested bounds so a line outside the data range stays visible.
Remaining tasks
- Review the MR.
- Manual testing across the four supported libraries in views, field, and block contexts.
- Decide whether to give C3/Billboard the same in-range guarantee (they have no soft-bounds equivalent; would require setting an explicit axis min/max).
- Decide whether plot line selection should key off series name rather than positional index, to survive column reordering on CSV import.
User interface changes
New collapsible "Plot lines" section in the chart settings form, rendered only for libraries that support plot lines. The section updates over AJAX when rows/columns are added to the data collector table, so it no longer requires a save-and-reopen.
API changes
- New
#plot_linesproperty on thechart_xaxis/chart_yaxisrender elements (array of items withvalue,label,color). - New
ChartBase::supportsPlotLines()method (defaultFALSE; overridden toTRUEby the four packaged libraries). Not added toChartInterface, to avoid breaking plugins that implement the interface directly; callers guard withmethod_exists(). - New
#ajax_companionsproperty on the data collector table element, letting sibling elements derived from the table refresh in the same AJAX response. - New
Util::firstNumericValue()helper.
Data model changes
New plot_lines.sources mapping added to the chart settings config schema (and the canvas block schema), keyed by field/series, each storing enabled, orientation, color, and label_text. Additive only; no update hook required.
Issue fork charts-3615474
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
andileco commentedComment #4
andileco commentedComment #5
andileco commentedComment #6
nikathoneCode looks good.
Comment #7
andileco commentedThe Canvas block within the Canvas context is broken (works in the regular Drupal context).
Comment #9
andileco commented