
Rechars という React のチャートライブラリを利用して、 Archive ページにカテゴリーごとに記事を集計してグラフ化する機能を作った。
グラフの Bar にカーソルを載せると Tooltip が表示されて、具体的な件数がわかる。

カテゴリごとに表示・非表示を切り替えることも可能。グラフ下のカテゴリー名( Legend )をクリックして切り替えられる。

ただし残念なことに Bar を非表示にしたときに Legend の表示を変化させるのが難しくてできていない。
仕事で使ってる Looker とか Redash であれば Legend をクリックして表示・非表示を切り替えることができ、それに連動して Legend の色をトーンダウンさせたりする機能が付属しているが、利用した Recharts にはその機能がなかった。 Bar の表示・非表示切り替えも標準サポートされていなかったので、 GitHub の Issue の情報を頼りに無理矢理実装した。
コードはこんな感じ。結構汚い Hack で、 Bar の表示・非表示を、表示用のキー文字列に空白を追加するかしないかで切り替えている。
// クリックされたアイテムが `this.state.disabled` 配列の中にすでに存在していれば除外し、
// 存在してなければ追加する
selectBar(event) {
let dataKey = event.dataKey.trim()
if (this.state.disabled.includes(dataKey)) {
this.setState({ disabled: this.state.disabled.filter(item => item !== dataKey) })
} else {
this.setState({ disabled: this.state.disabled.concat([dataKey]) })
}
}
render() {
return (
<ResponsiveContainer height={500}>
<BarChart
data={this.state.data}
margin={{
top: 20, right: 20, left: 0, bottom: 20,
}}
style={{ fontSize: '14px' }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="year" />
<YAxis />
<Tooltip labelStyle={{ color: '#000', fontWeight: 'bold' }} itemStyle={{ margin: '0 2px 0 4px', padding: '0' }} />
{// Legend クリック時のコールバックに `this.selectBar` を指定する }
<Legend onClick={this.selectBar} />
{/*
`this.state.categories` 配列と `this.state.disabled` 配列の内容を比較し、
`this.state.disabled` に追加済のカテゴリーは dataKey に空白を追加することで非表示に
*/}
{this.state.categories.map((category, index) => {
let dataKey = this.state.disabled.includes(category) ? category + " " : category
let color = this.colors[index % this.colors.length]
return(<Bar key={index} dataKey={dataKey} stackId="a" fill={color} />)
})}
</BarChart>
</ResponsiveContainer>
);
}