Last active
August 29, 2024 04:31
-
-
Save portedison/d24becf3ab7179d6afa84bf2d203a1a8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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