Skip to content

Instantly share code, notes, and snippets.

@portedison
Last active August 29, 2024 04:31
Show Gist options
  • Save portedison/d24becf3ab7179d6afa84bf2d203a1a8 to your computer and use it in GitHub Desktop.
Save portedison/d24becf3ab7179d6afa84bf2d203a1a8 to your computer and use it in GitHub Desktop.
// This is an example of a simple react component on one of my sites,
// this component uses chart.js, typescript (generated from wagtail/django schema)
import {
CategoryScale,
Chart as ChartJS,
LineElement,
LinearScale,
PointElement,
Tooltip,
} from 'chart.js';
import clsx from 'clsx';
import { useState } from 'react';
import { Line } from 'react-chartjs-2';
import { components } from 'schema/wagtail';
import { widont } from 'utils/typography';
import s from './BlockPerformanceGraph.module.scss';
const MONTHS = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
ChartJS.register(CategoryScale, LinearScale, LineElement, Tooltip, PointElement, LineElement);
const formatMoney = (value: number) => {
return new Intl.NumberFormat('en', {
style: 'currency',
currency: 'NZD',
currencyDisplay: 'narrowSymbol',
maximumFractionDigits: 0,
useGrouping: true,
// notation: compact ? 'compact' : undefined,
}).format(value);
};
interface BlockPerformanceGraphProps {
heading: string;
text?: string;
funds: components['schemas']['ManagedFundIndexFund'][];
disclaimers?: string[];
link?: string;
link_text?: string;
}
export const BlockPerformanceGraph = (props: BlockPerformanceGraphProps) => {
const [selectedFund, setSelectedFund] = useState(props.funds[0]);
const purchase = 10000;
const data = selectedFund.fund_value_data
.map((d) => Number(d.withdrawalPrice) * purchase)
.reverse();
const labels = selectedFund.fund_value_data.map((d) => d.value_date).reverse();
return (
<div className={clsx(s.root, 'rollIn')}>
<div className={s.container}>
<header className={s.header}>
<h2
className={clsx(s.h1, s.heading)}
dangerouslySetInnerHTML={{ __html: props.heading }}
/>
{props.text && (
<p className={s.text} dangerouslySetInnerHTML={{ __html: widont(props.text) }} />
)}
</header>
<nav className={s.nav}>
<ul>
{props.funds?.map((fund, i) => (
<li key={i}>
<button
onClick={(e) => setSelectedFund(fund)}
className={clsx(selectedFund == fund && s.selected)}
dangerouslySetInnerHTML={{ __html: fund.name }}
style={{ '--color-fund': fund.color } as React.CSSProperties}
></button>
</li>
))}
</ul>
</nav>
<div className={s.chartWrapper}>
<div className={s.chartWrapperWidth}>
<Line
className={s.chart}
options={{
responsive: true,
maintainAspectRatio: false,
layout: {
padding: 20,
},
animation: {
duration: 300,
},
plugins: {
tooltip: {
position: 'nearest',
// xAlign: 'center',
// yAlign: 'bottom',
displayColors: false,
titleAlign: 'center',
bodyAlign: 'center',
backgroundColor: selectedFund.color || '#282828',
callbacks: {
title: (context) => {
const date = new Date(context[0].label!);
return `${MONTHS[date.getMonth()]} ${date.getFullYear()}`;
},
label: function (context) {
const label = [
`Your ${formatMoney(purchase)} investment`,
`would be worth ${formatMoney(context.parsed.y)}`,
];
return label;
},
},
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
autoSkip: false,
maxRotation: 0,
callback: function (value, index) {
const display = this.getLabelForValue(Number(value)).split('-')[1] === '01';
return display ? this.getLabelForValue(Number(value)).split('-')[0] : '';
},
},
grid: {
display: false,
},
},
y: {
ticks: {
callback: (value, index, ticks) => {
return formatMoney(Number(value));
},
},
grid: {
display: false,
},
},
},
}}
data={{
labels: labels,
datasets: [
{
data: data,
backgroundColor: [selectedFund.color || '#282828'],
borderWidth: 1,
borderColor: [selectedFund.color || '#282828'],
hoverBackgroundColor: [selectedFund.color || '#282828'],
tension: 0.3,
pointRadius: 4,
pointHoverRadius: 8,
pointHitRadius: 15,
},
],
}}
/>
</div>
</div>
{props.disclaimers && (
<div className={s.disclaimers}>
{props.disclaimers.map((disclaimer, i) => (
<div
className={s.disclaimer}
key={i}
dangerouslySetInnerHTML={{ __html: widont(disclaimer) }}
/>
))}
</div>
)}
</div>
</div>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment