Skip to content

Commit

Permalink
[charts] Support slots/slotProps for the tooltip (#10515)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfauquette authored Oct 5, 2023
1 parent 16df383 commit 6bb263e
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 48 deletions.
14 changes: 12 additions & 2 deletions docs/pages/x/api/charts/charts-tooltip.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
{
"props": {
"axisContent": { "type": { "name": "elementType" } },
"axisContent": {
"type": { "name": "elementType" },
"deprecated": true,
"deprecationInfo": "Use slots.axisContent instead"
},
"classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } },
"itemContent": { "type": { "name": "elementType" } },
"itemContent": {
"type": { "name": "elementType" },
"deprecated": true,
"deprecationInfo": "Use slots.itemContent instead"
},
"slotProps": { "type": { "name": "object" }, "default": "{}" },
"slots": { "type": { "name": "object" }, "default": "{}" },
"trigger": {
"type": {
"name": "enum",
Expand Down
10 changes: 10 additions & 0 deletions docs/translations/api-docs/charts/charts-tooltip.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@
"deprecated": "",
"typeDescriptions": {}
},
"slotProps": {
"description": "The props used for each component slot.",
"deprecated": "",
"typeDescriptions": {}
},
"slots": {
"description": "Overridable component slots.",
"deprecated": "",
"typeDescriptions": {}
},
"trigger": {
"description": "Select the kind of tooltip to display - 'item': Shows data about the item below the mouse. - 'axis': Shows values associated with the hovered x value - 'none': Does not display tooltip",
"deprecated": "",
Expand Down
15 changes: 12 additions & 3 deletions packages/x-charts/src/BarChart/BarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import { ChartsAxis, ChartsAxisProps } from '../ChartsAxis';
import { BarSeriesType } from '../models/seriesType/bar';
import { MakeOptional } from '../models/helpers';
import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants';
import { ChartsTooltip, ChartsTooltipProps } from '../ChartsTooltip';
import {
ChartsTooltip,
ChartsTooltipProps,
ChartsTooltipSlotComponentProps,
ChartsTooltipSlotsComponent,
} from '../ChartsTooltip';
import {
ChartsLegend,
ChartsLegendProps,
Expand All @@ -24,11 +29,13 @@ import { ChartsAxisSlotsComponent, ChartsAxisSlotComponentProps } from '../model
export interface BarChartSlotsComponent
extends ChartsAxisSlotsComponent,
BarPlotSlotsComponent,
ChartsLegendSlotsComponent {}
ChartsLegendSlotsComponent,
ChartsTooltipSlotsComponent {}
export interface BarChartSlotComponentProps
extends ChartsAxisSlotComponentProps,
BarPlotSlotComponentProps,
ChartsLegendSlotComponentProps {}
ChartsLegendSlotComponentProps,
ChartsTooltipSlotComponentProps {}

export interface BarChartProps
extends Omit<ResponsiveChartContainerProps, 'series'>,
Expand Down Expand Up @@ -309,6 +316,8 @@ BarChart.propTypes = {
axisContent: PropTypes.elementType,
classes: PropTypes.object,
itemContent: PropTypes.elementType,
slotProps: PropTypes.object,
slots: PropTypes.object,
trigger: PropTypes.oneOf(['axis', 'item', 'none']),
}),
/**
Expand Down
30 changes: 18 additions & 12 deletions packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { SxProps, Theme } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import { useSlotProps } from '@mui/base/utils';
import { AxisInteractionData } from '../context/InteractionProvider';
import { SeriesContext } from '../context/SeriesContextProvider';
import { CartesianContext } from '../context/CartesianContextProvider';
Expand Down Expand Up @@ -91,10 +92,11 @@ export function DefaultChartsAxisContent(props: ChartsAxisContentProps) {
export function ChartsAxisTooltipContent(props: {
axisData: AxisInteractionData;
content?: React.ElementType<ChartsAxisContentProps>;
contentProps?: Partial<ChartsAxisContentProps>;
sx?: SxProps<Theme>;
classes: ChartsAxisContentProps['classes'];
}) {
const { content, axisData, sx, classes } = props;
const { content, contentProps, axisData, sx, classes } = props;

const isXaxis = (axisData.x && axisData.x.index) !== undefined;

Expand Down Expand Up @@ -129,15 +131,19 @@ export function ChartsAxisTooltipContent(props: {
}, [USED_AXIS_ID, isXaxis, xAxis, yAxis]);

const Content = content ?? DefaultChartsAxisContent;
return (
<Content
axisData={axisData}
series={relevantSeries}
axis={relevantAxis}
dataIndex={dataIndex}
axisValue={axisValue}
sx={sx}
classes={classes}
/>
);
const chartTooltipContentProps = useSlotProps({
elementType: Content,
externalSlotProps: contentProps,
additionalProps: {
axisData,
series: relevantSeries,
axis: relevantAxis,
dataIndex,
axisValue,
sx,
classes,
},
ownerState: {},
});
return <Content {...chartTooltipContentProps} />;
}
22 changes: 17 additions & 5 deletions packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { SxProps, Theme } from '@mui/material/styles';
import { useSlotProps } from '@mui/base/utils';
import { ItemInteractionData } from '../context/InteractionProvider';
import { SeriesContext } from '../context/SeriesContextProvider';
import { ChartSeriesDefaultized, ChartSeriesType } from '../models/seriesType/config';
Expand All @@ -12,7 +13,7 @@ import {
} from './ChartsTooltipTable';
import { ChartsTooltipClasses } from './tooltipClasses';

export type ChartsItemContentProps<T extends ChartSeriesType> = {
export type ChartsItemContentProps<T extends ChartSeriesType = ChartSeriesType> = {
/**
* The data used to identify the triggered item.
*/
Expand All @@ -28,7 +29,7 @@ export type ChartsItemContentProps<T extends ChartSeriesType> = {
sx?: SxProps<Theme>;
};

export function DefaultChartsItemContent<T extends ChartSeriesType>(
export function DefaultChartsItemContent<T extends ChartSeriesType = ChartSeriesType>(
props: ChartsItemContentProps<T>,
) {
const { series, itemData, sx, classes } = props;
Expand Down Expand Up @@ -72,16 +73,27 @@ export function DefaultChartsItemContent<T extends ChartSeriesType>(
export function ChartsItemTooltipContent<T extends ChartSeriesType>(props: {
itemData: ItemInteractionData<T>;
content?: React.ElementType<ChartsItemContentProps<T>>;
contentProps?: Partial<ChartsItemContentProps<T>>;
sx?: SxProps<Theme>;
classes: ChartsItemContentProps<T>['classes'];
}) {
const { content, itemData, sx, classes } = props;
const { content, itemData, sx, classes, contentProps } = props;

const series = React.useContext(SeriesContext)[itemData.type]!.series[
itemData.seriesId
] as ChartSeriesDefaultized<T>;

const Content = content ?? DefaultChartsItemContent<T>;

return <Content itemData={itemData} series={series} sx={sx} classes={classes} />;
const chartTooltipContentProps = useSlotProps({
elementType: Content,
externalSlotProps: contentProps,
additionalProps: {
itemData,
series,
sx,
classes,
},
ownerState: {},
});
return <Content {...chartTooltipContentProps} />;
}
81 changes: 69 additions & 12 deletions packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { unstable_composeClasses as composeClasses } from '@mui/utils';
import { Popper } from '@mui/base/Popper';
import composeClasses from '@mui/utils/composeClasses';
import { styled } from '@mui/material/styles';
import { Popper, PopperProps } from '@mui/base/Popper';
import { NoSsr } from '@mui/base/NoSsr';
import { useSlotProps } from '@mui/base/utils';
import {
AxisInteractionData,
InteractionContext,
Expand All @@ -14,6 +16,18 @@ import { ChartsItemContentProps, ChartsItemTooltipContent } from './ChartsItemTo
import { ChartsAxisContentProps, ChartsAxisTooltipContent } from './ChartsAxisTooltipContent';
import { ChartsTooltipClasses, getTooltipUtilityClass } from './tooltipClasses';

export interface ChartsTooltipSlotsComponent {
popper?: React.ElementType<PopperProps>;
axisContent?: React.ElementType<ChartsAxisContentProps>;
itemContent?: React.ElementType<ChartsItemContentProps>;
}

export interface ChartsTooltipSlotComponentProps {
popper?: Partial<PopperProps>;
axisContent?: Partial<ChartsAxisContentProps>;
itemContent?: Partial<ChartsItemContentProps>;
}

export type ChartsTooltipProps = {
/**
* Select the kind of tooltip to display
Expand All @@ -25,16 +39,28 @@ export type ChartsTooltipProps = {
trigger?: TriggerOptions;
/**
* Component to override the tooltip content when triger is set to 'item'.
* @deprecated Use slots.itemContent instead
*/
itemContent?: React.ElementType<ChartsItemContentProps<any>>;
/**
* Component to override the tooltip content when triger is set to 'axis'.
* @deprecated Use slots.axisContent instead
*/
axisContent?: React.ElementType<ChartsAxisContentProps>;
/**
* Override or extend the styles applied to the component.
*/
classes?: Partial<ChartsTooltipClasses>;
/**
* Overridable component slots.
* @default {}
*/
slots?: ChartsTooltipSlotsComponent;
/**
* The props used for each component slot.
* @default {}
*/
slotProps?: ChartsTooltipSlotComponentProps;
};

const useUtilityClasses = (ownerState: { classes: ChartsTooltipProps['classes'] }) => {
Expand All @@ -50,8 +76,17 @@ const useUtilityClasses = (ownerState: { classes: ChartsTooltipProps['classes']
return composeClasses(slots, getTooltipUtilityClass, classes);
};

const ChartsTooltipRoot = styled(Popper, {
name: 'MuiChartsTooltip',
slot: 'Root',
overridesResolver: (_, styles) => styles.root,
})(({ theme }) => ({
pointerEvents: 'none',
zIndex: theme.zIndex.modal,
}));

function ChartsTooltip(props: ChartsTooltipProps) {
const { trigger = 'axis', itemContent, axisContent } = props;
const { trigger = 'axis', itemContent, axisContent, slots, slotProps } = props;

const mousePosition = useMouseTracker();

Expand All @@ -64,34 +99,44 @@ function ChartsTooltip(props: ChartsTooltipProps) {

const classes = useUtilityClasses({ classes: props.classes });

const PopperComponent = slots?.popper ?? ChartsTooltipRoot;
const popperProps = useSlotProps({
elementType: PopperComponent,
externalSlotProps: slotProps?.popper,
additionalProps: {
open: popperOpen,
placement: 'right-start' as const,
anchorEl: generateVirtualElement(mousePosition),
},
ownerState: {},
});

if (trigger === 'none') {
return null;
}

return (
<NoSsr>
{popperOpen && (
<Popper
open={popperOpen}
placement="right-start"
anchorEl={generateVirtualElement(mousePosition)}
style={{ pointerEvents: 'none' }}
>
<PopperComponent {...popperProps}>
{trigger === 'item' ? (
<ChartsItemTooltipContent
itemData={displayedData as ItemInteractionData<ChartSeriesType>}
content={itemContent}
content={slots?.itemContent ?? itemContent}
contentProps={slotProps?.itemContent}
sx={{ mx: 2 }}
classes={classes}
/>
) : (
<ChartsAxisTooltipContent
axisData={displayedData as AxisInteractionData}
content={axisContent}
content={slots?.axisContent ?? axisContent}
contentProps={slotProps?.axisContent}
sx={{ mx: 2 }}
classes={classes}
/>
)}
</Popper>
</PopperComponent>
)}
</NoSsr>
);
Expand All @@ -104,6 +149,7 @@ ChartsTooltip.propTypes = {
// ----------------------------------------------------------------------
/**
* Component to override the tooltip content when triger is set to 'axis'.
* @deprecated Use slots.axisContent instead
*/
axisContent: PropTypes.elementType,
/**
Expand All @@ -112,8 +158,19 @@ ChartsTooltip.propTypes = {
classes: PropTypes.object,
/**
* Component to override the tooltip content when triger is set to 'item'.
* @deprecated Use slots.itemContent instead
*/
itemContent: PropTypes.elementType,
/**
* The props used for each component slot.
* @default {}
*/
slotProps: PropTypes.object,
/**
* Overridable component slots.
* @default {}
*/
slots: PropTypes.object,
/**
* Select the kind of tooltip to display
* - 'item': Shows data about the item below the mouse.
Expand Down
17 changes: 13 additions & 4 deletions packages/x-charts/src/LineChart/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import { ChartsAxis, ChartsAxisProps } from '../ChartsAxis/ChartsAxis';
import { LineSeriesType } from '../models/seriesType/line';
import { MakeOptional } from '../models/helpers';
import { DEFAULT_X_AXIS_KEY } from '../constants';
import { ChartsTooltip, ChartsTooltipProps } from '../ChartsTooltip';
import {
ChartsTooltip,
ChartsTooltipProps,
ChartsTooltipSlotComponentProps,
ChartsTooltipSlotsComponent,
} from '../ChartsTooltip';
import {
ChartsLegend,
ChartsLegendProps,
Expand All @@ -34,14 +39,16 @@ export interface LineChartSlotsComponent
LinePlotSlotsComponent,
MarkPlotSlotsComponent,
LineHighlightPlotSlotsComponent,
ChartsLegendSlotsComponent {}
ChartsLegendSlotsComponent,
ChartsTooltipSlotsComponent {}
export interface LineChartSlotComponentProps
extends ChartsAxisSlotComponentProps,
AreaPlotSlotComponentProps,
LinePlotSlotComponentProps,
MarkPlotSlotComponentProps,
LineHighlightPlotSlotComponentProps,
ChartsLegendSlotComponentProps {}
ChartsLegendSlotComponentProps,
ChartsTooltipSlotComponentProps {}

export interface LineChartProps
extends Omit<ResponsiveChartContainerProps, 'series'>,
Expand Down Expand Up @@ -139,7 +146,7 @@ const LineChart = React.forwardRef(function LineChart(props: LineChartProps, ref
<MarkPlot slots={slots} slotProps={slotProps} />
<LineHighlightPlot slots={slots} slotProps={slotProps} />
<ChartsLegend {...legend} slots={slots} slotProps={slotProps} />
<ChartsTooltip {...tooltip} />
<ChartsTooltip {...tooltip} slots={slots} slotProps={slotProps} />
<ChartsClipPath id={clipPathId} />
{children}
</ResponsiveChartContainer>
Expand Down Expand Up @@ -331,6 +338,8 @@ LineChart.propTypes = {
axisContent: PropTypes.elementType,
classes: PropTypes.object,
itemContent: PropTypes.elementType,
slotProps: PropTypes.object,
slots: PropTypes.object,
trigger: PropTypes.oneOf(['axis', 'item', 'none']),
}),
/**
Expand Down
Loading

0 comments on commit 6bb263e

Please sign in to comment.