Filter by a Product in React
Using ApexCharts with React
This Filter by a Product 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} />.
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.
//
// Revenue ($k) per product, region and quarter. The left chart sums it per
// product; the right one per region and quarter. Clicking a product filters
// the right chart BY it without dropping anything: every column keeps its
// full revenue, faded, and the product's share is drawn solid in front.
var PRODUCTS = ['Laptops', 'Phones', 'Tablets', 'Audio']
var REGIONS = ['North', 'South']
var QUARTERS = ['Q1', 'Q2', 'Q3', 'Q4']
var REVENUE = {
Laptops: { North: [42, 38, 45, 61], South: [30, 28, 33, 44] },
Phones: { North: [55, 49, 52, 70], South: [48, 51, 47, 66] },
Tablets: { North: [18, 15, 17, 26], South: [22, 19, 21, 25] },
Audio: { North: [12, 14, 13, 21], South: [9, 11, 10, 16] },
}
function sum(a) {
return a.reduce(function (s, v) {
return s + v
}, 0)
}
function productTotal(p) {
return sum(
REGIONS.map(function (r) {
return sum(REVENUE[p][r])
}),
)
}
function regionQuarters(r, onlyProduct) {
return QUARTERS.map(function (q, k) {
return sum(
PRODUCTS.filter(function (p) {
return !onlyProduct || p === onlyProduct
}).map(function (p) {
return REVENUE[p][r][k]
}),
)
})
}
// The picked product, or null.
var picked = null
function productSeries() {
var totals = PRODUCTS.map(productTotal)
var s = { name: 'Revenue', data: totals }
// On the source chart the pick is the whole of its own column, so it stays
// solid while the others fade.
if (picked) {
s.highlightData = PRODUCTS.map(function (p, j) {
return p === picked ? totals[j] : null
})
}
return [s]
}
function quarterSeries() {
return REGIONS.map(function (r) {
var s = { name: r, data: regionQuarters(r) }
if (picked) s.highlightData = regionQuarters(r, picked)
return s
})
}
function pickProduct(index) {
var product = PRODUCTS[index]
picked = picked === product ? null : product
var readout = document.getElementById('readout')
if (readout) {
readout.innerHTML = picked
? 'Filtered by <b>' + picked + '</b>. Click it again to clear.'
: 'Click a product to filter the quarters by it.'
}
// Let the click handler unwind before both charts redraw.
Promise.resolve().then(function () {
ApexCharts.getChartByID('products').updateSeries(productSeries())
ApexCharts.getChartByID('quarters').updateSeries(quarterSeries())
})
}
const ApexChart = () => {
const [state, setState] = React.useState({
series: productSeries(),
options: {
chart: {
id: 'products',
type: 'bar',
height: 320,
toolbar: { show: false },
events: {
dataPointSelection: function (e, ctx, opts) {
pickProduct(opts.dataPointIndex)
},
},
},
colors: ['#0f766e'],
plotOptions: {
bar: { horizontal: true, barHeight: '64%', borderRadius: 3 },
},
states: { active: { filter: { type: 'none' } } },
dataLabels: { enabled: false },
xaxis: {
categories: PRODUCTS,
tickAmount: 3,
labels: {
formatter: function (v) {
return '$' + v + 'k'
},
},
},
// On the source chart the pick is its own column, so a tooltip row for the
// part would only repeat the total.
highlightFilter: {
tooltip: { show: false },
dataLabels: { value: 'whole' },
},
legend: { show: false },
title: { text: 'Revenue by product', style: { fontSize: '13px' } },
tooltip: {
y: {
formatter: function (v) {
return '$' + v + 'k'
},
},
},
},
series1: quarterSeries(),
options1: {
chart: {
id: 'quarters',
type: 'bar',
height: 320,
toolbar: { show: false },
},
colors: ['#2563eb', '#f59e0b'],
plotOptions: {
bar: { columnWidth: '70%', borderRadius: 3 },
},
dataLabels: { enabled: false },
stroke: { show: true, width: 1, colors: ['transparent'] },
xaxis: { categories: QUARTERS },
yaxis: {
labels: {
formatter: function (v) {
return '$' + v + 'k'
},
},
},
legend: { position: 'bottom' },
title: { text: 'Revenue by quarter', style: { fontSize: '13px' } },
tooltip: {
shared: true,
intersect: false,
y: {
formatter: function (v) {
return '$' + v + 'k'
},
},
},
},
})
return (
<div>
<div className="wrap">
<h1>Filter by a product, keep the whole picture</h1>
<p>
Click a product on the left. The quarters on the right keep every
region's full revenue, faded, with that product's share solid in
front, so you see both how much it sold and how much of the business
it is.
</p>
<div className="readout" id="readout">
Click a product to filter the quarters by it.
</div>
<div className="grid">
<div className="card">
<div id="chart">
<ReactApexChart
options={state.options}
series={state.series}
type="bar"
height={320}
/>
</div>
</div>
<div className="card">
<div id="chart2">
<ReactApexChart
options={state.options1}
series={state.series1}
type="bar"
height={320}
/>
</div>
</div>
</div>
</div>
</div>
)
}
export default ApexChart