Chart.js icon indicating copy to clipboard operation
Chart.js copied to clipboard

Skip only last border in stacked bar

Open liondog opened this issue 3 years ago • 2 comments

Expected behavior

Separating stacked bar elements with a border / separator improves readability and is a common UI pattern: image

For example see https://www.ibm.com/design/language/data-visualization/charts or https://developers.google.com/chart/interactive/docs/gallery/barchart#stacked-bar-charts

Current behavior

Chart.js provides

borderWidth: { top: 1 }

that shows the separators as expected. But the issue is that the separator on the last stacked bar element is not hidden. This leads to the unwanted side-effect that the stacked bar is not pixel perfect anymore to the respective value axis:

image

The issue is that skipping only the last border is not possible using

elements: { bar: { borderSkipped: 'end' // 'top' shows the same unexpected behavior } }

Reproducible sample

https://codepen.io/mimizan/pen/ZErKoWp

Optional extra steps/info to reproduce

No response

Possible solution

Simply not providing a top border on the last element programmatically is not a solution as the user might want to show/hide individual bar elements via the legend. Determining the last visible stacked bar element via ScriptableOptions looks impossible or very complex.

https://github.com/chartjs/Chart.js/pull/8941 changed the global bar border radius option to only apply to the last bar element.

I'd propose that:

  • Using any borderSkipped property in the bar options applies only to the last element of a stack,
  • and for convenience 'end' skips the last top border or for horizontal bar charts the last right border / 'start' skips the first bottom border or for horizontal bar charts the first left border.

Context

No response

chart.js version

v3.7.1

Browser name and version

Chrome Canary, Firefox Developer Edition

Link to your project

No response

liondog avatar May 19 '22 10:05 liondog

You can use a scriptable function for this:

options: {
  elements: {
    bar: {
      borderSkipped: (ctx) => {
        const {
          chart,
          datasetIndex
        } = ctx;

        const topVisebleBar = chart.data.datasets.reduce((acc, curr, i) => {
          if (!chart.getDatasetMeta(i).hidden) {
            acc = i;
          }

          return acc;
        }, 0);

        return datasetIndex === topVisebleBar ? 'end' : 'start'
      }
    }
  }
}

https://codepen.io/leelenaleee/pen/RwQVJZW

LeeLenaleee avatar May 19 '22 11:05 LeeLenaleee

In the original pen there are options defined twice, that is not something you can do.

Is this what you are trying to do: https://codepen.io/kurkle/pen/MWQvMeG ?

(Probably the scriptable option that @LeeLenaleee suggested is what you need)

kurkle avatar May 23 '22 15:05 kurkle