GitHub user rlei-odes created a discussion: Making per-series chart styling 
reachable


I'm new to contributing here. Using Superset with a real dataset for the first
time, I ran into a wall trying to style individual series, and I think the wall
is smaller than it looks. Two small PRs to offer, and one question I'd like
opinions on before writing anything.

## What I'm trying to build
<img width="823" height="409" alt="goal_example_chart" 
src="https://github.com/user-attachments/assets/046c3306-d66c-4f25-b6c1-86f5c6fd3e40";
 />


Grey for previous years, dark for actual, hatched for forecast, outline for
plan. A common convention in business reporting, recently formalised as ISO
24896. The point is that the *fill* carries the meaning, so the chart reads
without the legend and colour stays free for something else.

The screenshot is from the plugin I made, see links below. My goal would be to 
get closer to achieving this by adding the necessary features.

## Where it stands

ECharts does all of this natively, and Superset ships it. The two properties I
need are `itemStyle.borderColor` + `borderWidth` for the outline, and
`itemStyle.decal` for the hatching.

Neither has a control, so the only way to set them is *Customize → ECharts
Options (JS object literals)*, the free-text box where you can hand-write 
ECharts
options that get merged over the ones Superset computed. What you write there is
checked against an allowlist schema first, and anything not on it is dropped.

- **Outline** is on that allowlist already.
- **Hatching** isn't - `decal` is the only member of `itemStyle` missing from
  `itemStyleSchema`, which looks accidental.

But neither is actually reachable, for a reason that has nothing to do with the
allowlist: both are *per-series* properties, so setting one means writing a
`series` key. `mergeCustomEChartOptions` replaces arrays instead of merging
them:

```js
// mergeCustomEChartOptions.ts
// Replace arrays and primitive values directly
(result as PlainObject)[key] = customValue;
```

So supplying `series` discards the series Superset just computed, data
included. The replacement can't carry its own data either, since `data` isn't in
`seriesSchema` (deliberately, and rightly).

So that box works well for top-level objects like `xAxis` and `yAxis`, which do
merge, and cannot express anything per series at all.

## Two PRs I'd like to offer

Both small, additive, and useful on their own:

1. **Allow `decal` in the options schema.** One property on `itemStyleSchema`,
   mirroring ECharts' `DecalObject`. `borderColor` and `lineStyle.type` are
   already allowed and a decal is the same kind of value, so this mostly closes
   an inconsistency. On its own it changes nothing that renders, see the
   question below.
2. **Chart chrome as controls** - gridlines, axis ticks, axis labels, axis line
   colour and width. Currently hardcoded as `splitLine: { show: !isSmallChart }`
   in the transform. Useful well beyond my use case; the sparse look in the
   chart above isn't reachable today without hand-written JSON.

## The question

Neither of those makes per-series styling work. That needs the array problem
solved, and I can see two ways:

**a) Merge `series` element-wise instead of replacing it.** Then
`{ series: [{ itemStyle: { decal: {...} } }] }` would decorate the first series
without destroying its data, and both outline and hatching become usable
immediately through a control that already exists. This looks small, and ECharts
itself merges by index or id in `setOption`. The obvious risk is that it changes
behaviour for anyone currently relying on replacement, and series indices are
positional, so it may be fragile when the series count changes.

**b) A real control for per-series styling**, rather than routing it through
JSON. More work, better UX, and it raises a design question I don't want to
answer alone: how should a series be addressed? Matching on the rendered series
name is the obvious answer and I'd argue against it. Names are composed from
the metric and groupby values, so a rule silently stops matching the moment
someone adds a dimension.

Is either direction something Superset wants? I'm happy to do the work, but I'd
rather find out now than arrive with a large PR. And if the answer is "this
belongs in a plugin", that's useful to know too.

## Working code

All of the above is running in a fork as a self-contained plugin:

- [Diff against 
master](https://github.com/apache/superset/compare/master...rlei-odes:superset:business-charts)
- [What's added versus merely 
surfaced](https://github.com/rlei-odes/superset/blob/business-charts/superset-frontend/plugins/plugin-chart-business-charts/FEATURES.md)



GitHub link: https://github.com/apache/superset/discussions/43426

----
This is an automatically sent email for [email protected].
To unsubscribe, please send an email to: 
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to