Average Profit Bar Chart with One Product Line Highlighted in React

Using ApexCharts with React

This Average Profit Bar Chart with One Product Line Highlighted example wires ApexCharts into React through the react-apexcharts component.

Install it with npm install react-apexcharts apexcharts, then mount the chart with <Chart type="..." options={options} series={series} />.

React charts guide
import React from 'react'
import ReactApexChart from 'react-apexcharts'
import ApexCharts from 'apexcharts'
import './styles.css'

import 'apexcharts/features/highlight-filter'

// Shared by the vanilla-js, React and Vue builds.

function money(v) {
  return (v < 0 ? '-$' : '$') + Math.abs(Math.round(v))
}
//
// Average profit per order by store, across all products (the wholes), and the
// same average over one product line (the parts). An average over a subset
// can be larger than the overall one, or fall on the other side of zero, so
// the parts here do both.
var STORES = ['Seattle', 'Denver', 'Austin', 'Chicago', 'Boston', 'Miami']
var ALL = [42, -18, 31, 24, 12, -9]
var LINES = {
  premium: [
    { x: 'Seattle', y: 42, highlight: 61 },
    { x: 'Denver', y: -18, highlight: -4 },
    { x: 'Austin', y: 31, highlight: 48 },
    { x: 'Chicago', y: 24, highlight: 19 },
    { x: 'Boston', y: 12, highlight: 27 },
    { x: 'Miami', y: -9, highlight: 6 },
  ],
  budget: [
    { x: 'Seattle', y: 42, highlight: 18 },
    { x: 'Denver', y: -18, highlight: -26 },
    { x: 'Austin', y: 31, highlight: 12 },
    { x: 'Chicago', y: 24, highlight: 28 },
    { x: 'Boston', y: 12, highlight: -3 },
    { x: 'Miami', y: -9, highlight: -21 },
  ],
}

function seriesFor(line) {
  return [
    {
      name: 'Avg profit per order',
      data: line
        ? LINES[line]
        : STORES.map(function (s, i) {
            return { x: s, y: ALL[i] }
          }),
    },
  ]
}

function pickLine(chart, line) {
  document.querySelectorAll('[data-line]').forEach(function (b) {
    b.classList.toggle('active', b.getAttribute('data-line') === line)
  })
  return chart.updateSeries(seriesFor(line))
}

function wireLines(chart) {
  document.querySelectorAll('[data-line]').forEach(function (b) {
    b.addEventListener('click', function () {
      pickLine(chart, b.getAttribute('data-line'))
    })
  })
}

const ApexChart = () => {
  const [state, setState] = React.useState({
    series: seriesFor('premium'),
    options: {
      chart: {
        id: 'profit',
        type: 'bar',
        height: 360,
        toolbar: { show: false },
      },
      colors: ['#0d9488'],
      plotOptions: {
        bar: { horizontal: true, barHeight: '62%', borderRadius: 4 },
      },
      dataLabels: {
        enabled: true,
        formatter: money,
      },
      xaxis: {
        labels: { formatter: money },
      },
      grid: {
        xaxis: { lines: { show: true } },
        yaxis: { lines: { show: false } },
      },
      tooltip: {
        y: { formatter: money },
      },
    },
  })

  React.useEffect(() => {
    // The react-apexcharts wrapper owns the render, so reach the live instance
    // by its chart.id. (seriesFor / pickLine live in the shared head script.)
    const timer = window.setInterval(() => {
      const chart = ApexCharts.getChartByID('profit')
      if (!chart) return
      window.clearInterval(timer)
      wireLines(chart)
    }, 50)
    return () => window.clearInterval(timer)
  }, [])

  return (
    <div>
      <div className="wrap">
        <h1>Averages can outgrow the whole</h1>
        <p>
          Each bar is a store's average profit per order across every product.
          Pick a product line to draw that line's own average solid in front.
          Where it beats the overall average, the axis stretches to fit it and a
          dashed edge marks where the overall average ends; where it loses
          money, it is drawn on its own side of zero.
        </p>
        <div className="actions">
          <span>Product line</span>
          <button data-line="premium" className="active">
            Premium
          </button>
          <button data-line="budget">Budget</button>
          <button data-line="">All (clear)</button>
        </div>
        <div className="chart-wrap">
          <div id="chart">
            <ReactApexChart
              options={state.options}
              series={state.series}
              type="bar"
              height={360}
            />
          </div>
        </div>
      </div>
    </div>
  )
}

export default ApexChart