Skip to content

Commit

Permalink
[@mantine/charts] Add data formatter for legend and tooltip
Browse files Browse the repository at this point in the history
  • Loading branch information
OlfillasOdikno committed Jan 22, 2024
1 parent 06bd805 commit 114be9d
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 36 deletions.
6 changes: 4 additions & 2 deletions packages/@mantine/charts/src/AreaChart/AreaChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,14 @@ export const AreaChart = factory<AreaChartFactory>((_props, ref) => {
{withLegend && (
<Legend
verticalAlign="top"
content={(payload) => (
content={({ payload }) => (
<ChartLegend
payload={payload.payload}
payload={payload}
onHighlight={setHighlightedArea}
legendPosition={legendProps?.verticalAlign || 'top'}
classNames={resolvedClassNames}
styles={resolvedStyles}
formatter={legendProps?.formatter}
/>
)}
height={44}
Expand Down Expand Up @@ -379,6 +380,7 @@ export const AreaChart = factory<AreaChartFactory>((_props, ref) => {
unit={unit}
classNames={resolvedClassNames}
styles={resolvedStyles}
formatter={tooltipProps?.formatter}
/>
)}
{...tooltipProps}
Expand Down
6 changes: 4 additions & 2 deletions packages/@mantine/charts/src/BarChart/BarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,14 @@ export const BarChart = factory<BarChartFactory>((_props, ref) => {
{withLegend && (
<Legend
verticalAlign="top"
content={(payload) => (
content={({ payload }) => (
<ChartLegend
payload={payload.payload}
payload={payload}
onHighlight={setHighlightedArea}
legendPosition={legendProps?.verticalAlign || 'top'}
classNames={resolvedClassNames}
styles={resolvedStyles}
formatter={legendProps?.formatter}
/>
)}
height={44}
Expand Down Expand Up @@ -288,6 +289,7 @@ export const BarChart = factory<BarChartFactory>((_props, ref) => {
unit={unit}
classNames={resolvedClassNames}
styles={resolvedStyles}
formatter={tooltipProps?.formatter}
/>
)}
{...tooltipProps}
Expand Down
27 changes: 17 additions & 10 deletions packages/@mantine/charts/src/ChartLegend/ChartLegend.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { Formatter, Payload } from 'recharts/types/component/DefaultLegendContent';
import {
Box,
BoxProps,
Expand All @@ -12,7 +13,7 @@ import {
} from '@mantine/core';
import classes from './ChartLegend.module.css';

export function getFilteredChartLegendPayload(payload: Record<string, any>[]) {
export function getFilteredChartLegendPayload(payload: Payload[]) {
return payload.filter((item) => item.color !== 'none');
}

Expand All @@ -22,9 +23,10 @@ export interface ChartLegendProps
extends BoxProps,
StylesApiProps<ChartLegendFactory>,
ElementProps<'div'> {
payload: Record<string, any>[] | undefined;
payload: Payload[] | undefined;
onHighlight: (area: string | null) => void;
legendPosition: 'top' | 'bottom' | 'middle';
formatter?: Formatter;
}

export type ChartLegendFactory = Factory<{
Expand All @@ -47,6 +49,7 @@ export const ChartLegend = factory<ChartLegendFactory>((_props, ref) => {
payload,
onHighlight,
legendPosition,
formatter,
...others
} = props;

Expand All @@ -71,16 +74,20 @@ export const ChartLegend = factory<ChartLegendFactory>((_props, ref) => {
<div
key={index}
{...getStyles('legendItem')}
onMouseEnter={() => onHighlight(item.dataKey)}
onMouseEnter={() => onHighlight(item.value)}
onMouseLeave={() => onHighlight(null)}
>
<ColorSwatch
color={item.color}
size={12}
{...getStyles('legendItemColor')}
withShadow={false}
/>
<p {...getStyles('legendItemName')}>{item.dataKey}</p>
{item.color ? (
<ColorSwatch
color={item.color}
size={12}
{...getStyles('legendItemColor')}
withShadow={false}
/>
) : undefined}
<p {...getStyles('legendItemName')}>
{formatter ? formatter(item.value, item, index) : item.value}
</p>
</div>
));

Expand Down
73 changes: 53 additions & 20 deletions packages/@mantine/charts/src/ChartTooltip/ChartTooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import { Formatter, Payload } from 'recharts/types/component/DefaultTooltipContent';
import {
Box,
BoxProps,
Expand All @@ -12,7 +13,9 @@ import {
} from '@mantine/core';
import classes from './ChartTooltip.module.css';

export function getFilteredChartTooltipPayload(payload: Record<string, any>[]) {
type MantinePayload = Payload<string, any> & { fill?: string };

export function getFilteredChartTooltipPayload(payload: MantinePayload[]) {
return payload.filter((item) => item.fill !== 'none');
}

Expand All @@ -34,10 +37,13 @@ export interface ChartTooltipProps
label?: React.ReactNode;

/** Chart data provided by recharts */
payload: Record<string, any>[] | undefined;
payload: MantinePayload[] | undefined;

/** Data units, provided by parent component */
unit?: string;

/** Function used to format the display content */
formatter?: Formatter<string, any>;
}

export type ChartTooltipFactory = Factory<{
Expand All @@ -50,8 +56,19 @@ const defaultProps: Partial<ChartTooltipProps> = {};

export const ChartTooltip = factory<ChartTooltipFactory>((_props, ref) => {
const props = useProps('ChartTooltip', defaultProps, _props);
const { classNames, className, style, styles, unstyled, vars, payload, label, unit, ...others } =
props;
const {
classNames,
className,
style,
styles,
unstyled,
vars,
payload,
label,
unit,
formatter,
...others
} = props;

const getStyles = useStyles<ChartTooltipFactory>({
name: 'ChartTooltip',
Expand All @@ -70,23 +87,39 @@ export const ChartTooltip = factory<ChartTooltipFactory>((_props, ref) => {

const filteredPayload = getFilteredChartTooltipPayload(payload);

const items = filteredPayload.map((item) => (
<div key={item.name} {...getStyles('tooltipItem')}>
<div {...getStyles('tooltipItemBody')}>
<ColorSwatch
color={item.color}
size={12}
{...getStyles('tooltipItemColor')}
withShadow={false}
/>
<div {...getStyles('tooltipItemName')}>{item.name}</div>
</div>
<div {...getStyles('tooltipItemData')}>
{item.payload[item.dataKey]}
{unit}
const items = filteredPayload.map((item, index) => {
let finalValue: React.ReactNode | string = item.value;
let finalName: React.ReactNode | any = item.name;

if (formatter !== undefined && item.value !== undefined) {
const formatted = formatter(item.value, item.name, item, index, payload);
if (Array.isArray(formatted)) {
[finalValue, finalName] = formatted;
} else {
finalValue = formatted;
}
}

return (
<div key={index} {...getStyles('tooltipItem')}>
<div {...getStyles('tooltipItemBody')}>
{item.color ? (
<ColorSwatch
color={item.color}
size={12}
{...getStyles('tooltipItemColor')}
withShadow={false}
/>
) : undefined}
<div {...getStyles('tooltipItemName')}>{finalName}</div>
</div>
<div {...getStyles('tooltipItemData')}>
{finalValue}
{unit}
</div>
</div>
</div>
));
);
});

return (
<Box {...getStyles('tooltip')} ref={ref} {...others}>
Expand Down
6 changes: 4 additions & 2 deletions packages/@mantine/charts/src/LineChart/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,14 @@ export const LineChart = factory<LineChartFactory>((_props, ref) => {
{withLegend && (
<Legend
verticalAlign="top"
content={(payload) => (
content={({ payload }) => (
<ChartLegend
payload={payload.payload}
payload={payload}
onHighlight={setHighlightedArea}
legendPosition={legendProps?.verticalAlign || 'top'}
classNames={resolvedClassNames}
styles={resolvedStyles}
formatter={legendProps?.formatter}
/>
)}
height={44}
Expand Down Expand Up @@ -311,6 +312,7 @@ export const LineChart = factory<LineChartFactory>((_props, ref) => {
unit={unit}
classNames={resolvedClassNames}
styles={resolvedStyles}
formatter={tooltipProps?.formatter}
/>
)}
{...tooltipProps}
Expand Down

0 comments on commit 114be9d

Please sign in to comment.