import React from 'react'
import ReactApexChart from 'react-apexcharts'
import ApexCharts from 'apexcharts'
import './styles.css'
// Shared by the vanilla-js, React and Vue builds.
//
// Each device gets its RAW measurements: one number per page load. No
// quartiles, no median, nothing precomputed. `points` is the sample, and the
// library derives the five-number summary from it.
var DEVICES = [
{ name: 'Desktop', center: 1.4, spread: 0.3, slowTail: 1.4 },
{ name: 'Laptop', center: 1.9, spread: 0.38, slowTail: 1.8 },
{ name: 'Tablet', center: 2.8, spread: 0.55, slowTail: 2.4 },
{ name: 'Phone (5G)', center: 3.2, spread: 0.62, slowTail: 3.0 },
{ name: 'Phone (3G)', center: 6.1, spread: 1.3, slowTail: 4.5 },
]
function makeSample(cfg, index) {
var seed = 8191 * (index + 3)
function rand() {
seed = (seed * 16807) % 2147483647
return (seed - 1) / 2147483646
}
var out = []
for (var i = 0; i < 140; i++) {
// Box-Muller, then a few genuinely slow loads: the tail is the point of
// the chart, and it is what the two whisker rules disagree about.
var u1 = Math.max(rand(), 1e-9)
var z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * rand())
var v = cfg.center + z * cfg.spread
if (i % 34 === 0) v = cfg.center + cfg.slowTail * (0.6 + rand())
out.push(Math.round(Math.max(0.2, v) * 100) / 100)
}
return out
}
var RAW = DEVICES.map(function (cfg, i) {
return { x: cfg.name, points: makeSample(cfg, i) }
})
function wireWhiskerControls(chart) {
var buttons = [].slice.call(document.querySelectorAll('[data-whiskers]'))
var note = document.querySelector('#note')
function apply(mode) {
buttons.forEach(function (b) {
b.className = b.getAttribute('data-whiskers') === mode ? 'on' : ''
})
chart.updateOptions({
plotOptions: { boxPlot: { whiskers: mode } },
})
if (note) {
note.textContent =
mode === 'tukey'
? 'Whiskers stop at 1.5 x IQR. The dots past the whisker are the outliers.'
: 'Whiskers reach the slowest and fastest load. Nothing is hidden.'
}
}
buttons.forEach(function (b) {
b.addEventListener('click', function () {
apply(b.getAttribute('data-whiskers'))
})
})
apply('minmax')
}
const ApexChart = () => {
const [state, setState] = React.useState({
series: [
{
name: 'Load time',
type: 'boxPlot',
data: RAW,
},
],
options: {
chart: {
id: 'loadTimes',
type: 'boxPlot',
// Fixed width so the layout cannot shift with the page.
width: 700,
height: 440,
toolbar: {
show: false,
},
},
plotOptions: {
boxPlot: {
colors: {
upper: '#5A87FF',
lower: '#B7BEFF',
},
// Every observation, drawn over its own box. With the 1.5 x IQR rule the
// dots beyond the whisker are exactly the outliers that rule excludes.
points: {
show: true,
size: 2.5,
jitter: 0.45,
opacity: 0.55,
},
},
},
legend: {
show: false,
},
yaxis: {
title: {
text: 'Page load (seconds)',
},
min: 0,
},
xaxis: {
title: {
text: 'Device',
},
},
tooltip: {
shared: false,
intersect: true,
},
},
})
React.useEffect(() => {
// The react-apexcharts wrapper owns the render, so reach the live instance
// by its chart.id before wiring the controls.
const timer = window.setInterval(() => {
const chart = ApexCharts.getChartByID('loadTimes')
if (!chart) return
window.clearInterval(timer)
wireWhiskerControls(chart)
}, 50)
return () => window.clearInterval(timer)
}, [])
return (
<div>
<div className="wrap">
<h1>Hand it the measurements, not the quartiles</h1>
<p>
Every box below is built from 140 raw page-load timings. The series
carries those measurements and nothing else: the median, the quartiles
and the whiskers are computed for you. Switch the whisker rule to see
the choice it involves, because a distribution with a slow tail looks
like a different story depending on where the whisker stops.
</p>
<div className="actions">
<button data-whiskers="minmax" className="on">
Whiskers: min to max
</button>
<button data-whiskers="tukey">Whiskers: 1.5 x IQR</button>
</div>
<div className="chart-wrap">
<div id="chart">
<ReactApexChart
options={state.options}
series={state.series}
type="boxPlot"
height={440}
width={700}
/>
</div>
</div>
<div className="note" id="note"></div>
</div>
</div>
)
}
export default ApexChart