Skip to content

Commit

Permalink
[charts] Let the useXxxSeries support array of ids and document them (
Browse files Browse the repository at this point in the history
  • Loading branch information
JCQuintas authored Feb 11, 2025
1 parent e71e831 commit 9b0bc3f
Show file tree
Hide file tree
Showing 34 changed files with 676 additions and 123 deletions.
2 changes: 1 addition & 1 deletion packages/x-charts-pro/src/Heatmap/HeatmapPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { useXScale, useYScale, useZColorScale } from '@mui/x-charts/hooks';
import { useHeatmapSeries } from '../hooks/useSeries';
import { useHeatmapSeries } from '../hooks/useHeatmapSeries';
import { HeatmapItem, HeatmapItemProps } from './HeatmapItem';

export interface HeatmapPlotProps extends Pick<HeatmapItemProps, 'slots' | 'slotProps'> {}
Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts-pro/src/Heatmap/HeatmapTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from '@mui/x-charts/ChartsTooltip';
import { useXAxis, useYAxis } from '@mui/x-charts/hooks';
import { getLabel, ChartsLabelMark } from '@mui/x-charts/internals';
import { useHeatmapSeries } from '../hooks/useSeries';
import { useHeatmapSeries } from '../hooks/useHeatmapSeries';

export interface HeatmapTooltipProps
extends Omit<ChartsTooltipContainerProps, 'trigger' | 'children'> {}
Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts-pro/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { useHeatmapSeries } from './useSeries';
export { useHeatmapSeries } from './useHeatmapSeries';
export * from './zoom';
64 changes: 64 additions & 0 deletions packages/x-charts-pro/src/hooks/useHeatmapSeries.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { renderHook } from '@mui/internal-test-utils';
import { expect } from 'chai';
import * as React from 'react';
import { useHeatmapSeries } from './useHeatmapSeries';
import { Heatmap } from '../Heatmap';
import { HeatmapSeriesType } from '../models';

describe('useHeatmapSeries', () => {
const mockSeries: HeatmapSeriesType[] = [
{
type: 'heatmap',
id: '1',
data: [
[0, 0, 10],
[0, 1, 20],
[0, 2, 40],
],
},
{
type: 'heatmap',
id: '2',
data: [
[3, 2, 20],
[3, 3, 70],
[3, 4, 90],
],
},
];

const defaultProps = {
series: mockSeries,
height: 400,
width: 400,
xAxis: [{ data: [1, 2, 3, 4] }],
yAxis: [{ data: ['A', 'B', 'C', 'D', 'E'] }],
};

const options: any = {
wrapper: ({ children }: { children: React.ReactElement }) => {
return <Heatmap {...defaultProps}>{children}</Heatmap>;
},
};

it('should return all heatmap series when no seriesIds are provided', () => {
const { result } = renderHook(() => useHeatmapSeries(), options);
expect(result.current?.seriesOrder).to.deep.equal(['1', '2']);
expect(Object.keys(result.current?.series ?? {})).to.deep.equal(['1', '2']);
});

it('should return the specific heatmap series when a single seriesId is provided', () => {
const { result } = renderHook(() => useHeatmapSeries('1'), options);
expect(result.current?.id).to.deep.equal(mockSeries[0].id);
});

it('should return the specific heatmap series when multiple seriesIds are provided', () => {
const { result } = renderHook(() => useHeatmapSeries(['2', '1']), options);
expect(result.current?.map((v) => v?.id)).to.deep.equal([mockSeries[1].id, mockSeries[0].id]);
});

it('should return undefined series when invalid seriesIds are provided', () => {
const { result } = renderHook(() => useHeatmapSeries(['1', '3']), options);
expect(result.current?.map((v) => v?.id)).to.deep.equal([mockSeries[0].id, undefined]);
});
});
37 changes: 37 additions & 0 deletions packages/x-charts-pro/src/hooks/useHeatmapSeries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';
import {
createSeriesSelectorsOfType,
ProcessedSeries,
SeriesId,
ChartSeriesDefaultized,
} from '@mui/x-charts/internals';

const selectorSeries = createSeriesSelectorsOfType('heatmap');

/**
* Get access to the internal state of heatmap series.
* The returned object contains:
* - series: a mapping from ids to series attributes.
* - seriesOrder: the array of series ids.
* @returns {{ series: Record<SeriesId, DefaultizedHeatmapSeriesType>; seriesOrder: SeriesId[]; } | undefined} heatmapSeries
*/
export function useHeatmapSeries(): ProcessedSeries['heatmap'];
/**
* Get access to the internal state of heatmap series.
*
* @param {SeriesId} seriesId The id of the series to get.
* @returns {ChartSeriesDefaultized<'heatmap'> | undefined} heatmapSeries
*/
export function useHeatmapSeries(seriesId: SeriesId): ChartSeriesDefaultized<'heatmap'> | undefined;
/**
* Get access to the internal state of heatmap series.
*
* @param {SeriesId[]} seriesIds The ids of the series to get. Order is preserved.
* @returns {ChartSeriesDefaultized<'heatmap'>[] | undefined} heatmapSeries
*/
export function useHeatmapSeries(
seriesIds: SeriesId[],
): (ChartSeriesDefaultized<'heatmap'> | undefined)[];
export function useHeatmapSeries(seriesIds?: SeriesId | SeriesId[]) {
return selectorSeries(seriesIds);
}
16 changes: 0 additions & 16 deletions packages/x-charts-pro/src/hooks/useSeries.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/x-charts/src/BarChart/BarPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { BarClipPath } from './BarClipPath';
import { BarLabelItemProps, BarLabelSlotProps, BarLabelSlots } from './BarLabel/BarLabelItem';
import { BarLabelPlot } from './BarLabel/BarLabelPlot';
import { checkScaleErrors } from './checkScaleErrors';
import { useBarSeries } from '../hooks/useSeries';
import { useBarSeries } from '../hooks/useBarSeries';
import { useSkipAnimation } from '../context/AnimationProvider';
import { SeriesProcessorResult } from '../internals/plugins/models/seriesConfig/seriesProcessor.types';

Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts/src/LineChart/AreaPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { getCurveFactory } from '../internals/getCurve';
import { isBandScale } from '../internals/isBandScale';
import { DEFAULT_X_AXIS_KEY } from '../constants';
import { LineItemIdentifier } from '../models/seriesType/line';
import { useLineSeries } from '../hooks/useSeries';
import { useLineSeries } from '../hooks/useLineSeries';
import { useSkipAnimation } from '../context/AnimationProvider';
import { useChartGradientIdBuilder } from '../hooks/useChartGradientId';
import { useXAxes, useYAxes } from '../hooks/useAxis';
Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts/src/LineChart/LineHighlightPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { useSelector } from '../internals/store/useSelector';
import { LineHighlightElement, LineHighlightElementProps } from './LineHighlightElement';
import { getValueToPositionMapper } from '../hooks/useScale';
import { DEFAULT_X_AXIS_KEY } from '../constants';
import { useLineSeries } from '../hooks/useLineSeries';
import getColor from './seriesConfig/getColor';
import { useLineSeries } from '../hooks/useSeries';
import { useChartContext } from '../context/ChartProvider';
import { selectorChartsInteractionXAxis } from '../internals/plugins/featurePlugins/useChartInteraction';
import { useXAxes, useYAxes } from '../hooks/useAxis';
Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts/src/LineChart/LinePlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { getCurveFactory } from '../internals/getCurve';
import { isBandScale } from '../internals/isBandScale';
import { DEFAULT_X_AXIS_KEY } from '../constants';
import { LineItemIdentifier } from '../models/seriesType/line';
import { useLineSeries } from '../hooks/useSeries';
import { useLineSeries } from '../hooks/useLineSeries';
import { useSkipAnimation } from '../context/AnimationProvider';
import { useChartGradientIdBuilder } from '../hooks/useChartGradientId';
import { useXAxes, useYAxes } from '../hooks';
Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts/src/LineChart/MarkPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { DEFAULT_X_AXIS_KEY } from '../constants';
import { useSkipAnimation } from '../context/AnimationProvider';
import { useChartId } from '../hooks/useChartId';
import { getValueToPositionMapper } from '../hooks/useScale';
import { useLineSeries } from '../hooks/useSeries';
import { useLineSeries } from '../hooks/useLineSeries';
import { cleanId } from '../internals/cleanId';
import { LineItemIdentifier } from '../models/seriesType/line';
import { CircleMarkElement } from './CircleMarkElement';
Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts/src/PieChart/PiePlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PieArcPlot, PieArcPlotProps, PieArcPlotSlotProps, PieArcPlotSlots } fro
import { PieArcLabelPlotSlots, PieArcLabelPlotSlotProps, PieArcLabelPlot } from './PieArcLabelPlot';
import { getPercentageValue } from '../internals/getPercentageValue';
import { getPieCoordinates } from './getPieCoordinates';
import { usePieSeries } from '../hooks/useSeries';
import { usePieSeries } from '../hooks/usePieSeries';
import { useSkipAnimation } from '../context/AnimationProvider';
import { useDrawingArea } from '../hooks';

Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts/src/ScatterChart/ScatterPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { Scatter, ScatterProps } from './Scatter';
import { useScatterSeries } from '../hooks/useScatterSeries';
import getColor from './seriesConfig/getColor';
import { useScatterSeries } from '../hooks/useSeries';
import { useXAxes, useYAxes } from '../hooks';
import { useZAxes } from '../hooks/useZAxis';

Expand Down
12 changes: 5 additions & 7 deletions packages/x-charts/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ export * from './useAxis';
export * from './useZAxis';
export * from './useColorScale';
export * from './useSvgRef';
export * from './useSeries';
export * from './useScatterSeries';
export * from './usePieSeries';
export * from './useBarSeries';
export * from './useLineSeries';
export * from './useItemHighlighted';
export * from './useItemHighlightedGetter';
export {
useSeries,
usePieSeries,
useLineSeries,
useBarSeries,
useScatterSeries,
} from './useSeries';
export * from './useLegend';
export { useChartGradientId, useChartGradientIdObjectBound } from './useChartGradientId';
39 changes: 35 additions & 4 deletions packages/x-charts/src/hooks/useAxis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,66 @@ import {
} from '../internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.selectors';
import { useSelector } from '../internals/store/useSelector';
import { useStore } from '../internals/store/useStore';
import { AxisId } from '../models/axis';

/**
* Get all the x-axes.
*
* - `xAxis` is an object with the shape `{ [axisId]: axis }`.
* - `xAxisIds` is an array of axis IDs.
*
* If access to a specific X axis is needed, use the `useXAxis` hook instead.
*
* @returns `{ xAxis, xAxisIds }` - The x-axes and their IDs.
*/
export function useXAxes() {
const store = useStore<[UseChartCartesianAxisSignature]>();
const { axis: xAxis, axisIds: xAxisIds } = useSelector(store, selectorChartXAxis);

return { xAxis, xAxisIds };
}

/**
* Get all the y-axes.
*
* - `yAxis` is an object with the shape `{ [axisId]: axis }`.
* - `yAxisIds` is an array of axis IDs.
*
* If access to a specific Y axis is needed, use the `useYAxis` hook instead.
*
* @returns `{ yAxis, yAxisIds }` - The y-axes and their IDs.
*/
export function useYAxes() {
const store = useStore<[UseChartCartesianAxisSignature]>();
const { axis: yAxis, axisIds: yAxisIds } = useSelector(store, selectorChartYAxis);

return { yAxis, yAxisIds };
}

export function useXAxis(identifier?: number | string) {
/**
* Get the X axis.
* @param {AxisId | undefined} axisId - If provided returns the x axis with axisId, else returns the values for the default x axis.
* @returns The X axis.
*/
export function useXAxis(axisId?: AxisId) {
const store = useStore<[UseChartCartesianAxisSignature]>();
const { axis: xAxis, axisIds: xAxisIds } = useSelector(store, selectorChartXAxis);

const id = typeof identifier === 'string' ? identifier : xAxisIds[identifier ?? 0];
const id = axisId ?? xAxisIds[0];

return xAxis[id];
}

export function useYAxis(identifier?: number | string) {
/**
* Get the Y axis.
* @param {AxisId | undefined} axisId - If provided returns the y axis with axisId, else returns the values for the default y axis.
* @returns The Y axis.
*/
export function useYAxis(axisId?: AxisId) {
const store = useStore<[UseChartCartesianAxisSignature]>();
const { axis: yAxis, axisIds: yAxisIds } = useSelector(store, selectorChartYAxis);

const id = typeof identifier === 'string' ? identifier : yAxisIds[identifier ?? 0];
const id = axisId ?? yAxisIds[0];

return yAxis[id];
}
54 changes: 54 additions & 0 deletions packages/x-charts/src/hooks/useBarSeries.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { renderHook } from '@mui/internal-test-utils';
import { expect } from 'chai';
import * as React from 'react';
import { useBarSeries } from './useBarSeries';
import { BarSeriesType } from '../models';
import { BarChart } from '../BarChart';

describe('useBarSeries', () => {
const mockSeries: BarSeriesType[] = [
{
type: 'bar',
id: '1',
data: [1, 2, 3],
},
{
type: 'bar',
id: '2',
data: [4, 5, 6],
},
];

const defaultProps = {
series: mockSeries,
height: 400,
width: 400,
};

const options: any = {
wrapper: ({ children }: { children: React.ReactElement }) => {
return <BarChart {...defaultProps}>{children}</BarChart>;
},
};

it('should return all bar series when no seriesIds are provided', () => {
const { result } = renderHook(() => useBarSeries(), options);
expect(result.current?.seriesOrder).to.deep.equal(['1', '2']);
expect(Object.keys(result.current?.series ?? {})).to.deep.equal(['1', '2']);
});

it('should return the specific bar series when a single seriesId is provided', () => {
const { result } = renderHook(() => useBarSeries('1'), options);
expect(result.current?.id).to.deep.equal(mockSeries[0].id);
});

it('should return the specific bar series when multiple seriesIds are provided', () => {
const { result } = renderHook(() => useBarSeries(['2', '1']), options);
expect(result.current?.map((v) => v?.id)).to.deep.equal([mockSeries[1].id, mockSeries[0].id]);
});

it('should return undefined series when invalid seriesIds are provided', () => {
const { result } = renderHook(() => useBarSeries(['1', '3']), options);
expect(result.current?.map((v) => v?.id)).to.deep.equal([mockSeries[0].id, undefined]);
});
});
33 changes: 33 additions & 0 deletions packages/x-charts/src/hooks/useBarSeries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use client';
import { ProcessedSeries } from '../internals/plugins/corePlugins/useChartSeries/useChartSeries.types';
import { SeriesId } from '../models/seriesType/common';
import { ChartSeriesDefaultized } from '../models/seriesType/config';
import { createSeriesSelectorsOfType } from '../internals/createSeriesSelectorOfType';

const selectorSeries = createSeriesSelectorsOfType('bar');

/**
* Get access to the internal state of bar series.
* The returned object contains:
* - series: a mapping from ids to series attributes.
* - seriesOrder: the array of series ids.
* @returns {{ series: Record<SeriesId, DefaultizedBarSeriesType>; seriesOrder: SeriesId[]; } | undefined} barSeries
*/
export function useBarSeries(): ProcessedSeries['bar'];
/**
* Get access to the internal state of bar series.
*
* @param {SeriesId} seriesId The id of the series to get.
* @returns {ChartSeriesDefaultized<'bar'> | undefined} barSeries
*/
export function useBarSeries(seriesId: SeriesId): ChartSeriesDefaultized<'bar'> | undefined;
/**
* Get access to the internal state of bar series.
*
* @param {SeriesId[]} seriesIds The ids of the series to get. Order is preserved.
* @returns {ChartSeriesDefaultized<'bar'>[] | undefined} barSeries
*/
export function useBarSeries(seriesIds: SeriesId[]): (ChartSeriesDefaultized<'bar'> | undefined)[];
export function useBarSeries(seriesIds?: SeriesId | SeriesId[]) {
return selectorSeries(seriesIds);
}
6 changes: 5 additions & 1 deletion packages/x-charts/src/hooks/useChartId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { useStore } from '../internals/store/useStore';
import { useSelector } from '../internals/store/useSelector';
import { selectorChartId } from '../internals/plugins/corePlugins/useChartId/useChartId.selectors';

export function useChartId() {
/**
* Get the unique identifier of the chart.
* @returns chartId if it exists.
*/
export function useChartId(): string | undefined {
const store = useStore();
return useSelector(store, selectorChartId);
}
Loading

0 comments on commit 9b0bc3f

Please sign in to comment.