From d5ae6f395b2fc15c5f7df6596c88cf7300cb5d97 Mon Sep 17 00:00:00 2001 From: Jose C Quintas Jr Date: Wed, 27 Nov 2024 14:41:18 +0100 Subject: [PATCH] [charts] Allow the creation of custom HTML components using charts data (#15511) Signed-off-by: Jose C Quintas Jr Signed-off-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> --- docs/data/charts/components/HtmlLegend.js | 66 +++++++++++++++++ docs/data/charts/components/HtmlLegend.tsx | 66 +++++++++++++++++ .../charts/components/HtmlLegend.tsx.preview | 14 ++++ docs/data/charts/components/components.md | 19 +++++ docs/data/charts/composition/composition.md | 74 +++++++++++++++++-- docs/data/chartsApiPages.ts | 4 + .../pages/x/api/charts/chart-data-provider.js | 23 ++++++ .../x/api/charts/chart-data-provider.json | 70 ++++++++++++++++++ docs/pages/x/api/charts/charts-surface.json | 2 +- .../chart-container/chart-container.json | 2 +- .../chart-data-provider.json | 42 +++++++++++ .../charts/charts-surface/charts-surface.json | 2 +- .../ChartContainerPro/ChartContainerPro.tsx | 13 ++-- .../useChartContainerProProps.ts | 7 +- .../src/ChartContainer/ChartContainer.tsx | 35 +++++++-- .../src/ChartContainer/ResizableContainer.tsx | 58 --------------- .../ChartContainer/useChartContainerProps.ts | 5 +- .../src/ChartsSurface/ChartsSurface.test.tsx | 2 +- .../src/ChartsSurface/ChartsSurface.tsx | 34 +++++++-- .../x-charts/src/Gauge/GaugeContainer.tsx | 55 +++----------- .../ChartDataProvider/ChartDataProvider.tsx | 27 +++++++ .../src/context/SizeProvider/Size.types.ts | 4 +- .../src/context/SizeProvider/SizeContext.ts | 2 +- .../useChartContainerDimensions.ts | 15 +++- packages/x-charts/src/internals/index.ts | 2 - scripts/buildApiDocs/chartsSettings/index.ts | 3 + 26 files changed, 503 insertions(+), 143 deletions(-) create mode 100644 docs/data/charts/components/HtmlLegend.js create mode 100644 docs/data/charts/components/HtmlLegend.tsx create mode 100644 docs/data/charts/components/HtmlLegend.tsx.preview create mode 100644 docs/pages/x/api/charts/chart-data-provider.js create mode 100644 docs/pages/x/api/charts/chart-data-provider.json create mode 100644 docs/translations/api-docs/charts/chart-data-provider/chart-data-provider.json delete mode 100644 packages/x-charts/src/ChartContainer/ResizableContainer.tsx diff --git a/docs/data/charts/components/HtmlLegend.js b/docs/data/charts/components/HtmlLegend.js new file mode 100644 index 0000000000000..a58701b379a8f --- /dev/null +++ b/docs/data/charts/components/HtmlLegend.js @@ -0,0 +1,66 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { unstable_useBarSeries } from '@mui/x-charts/hooks'; +import { ChartDataProvider } from '@mui/x-charts/context'; +import { ChartsSurface } from '@mui/x-charts/ChartsSurface'; +import { BarPlot } from '@mui/x-charts/BarChart'; +import { ChartsXAxis } from '@mui/x-charts/ChartsXAxis'; +import { ChartsYAxis } from '@mui/x-charts/ChartsYAxis'; + +function MyCustomLegend() { + const s = unstable_useBarSeries(); + return ( + + + {Object.values(s?.series ?? []).map((v) => { + return ( + + + + + ); + })} + +
+
+
{`${v.label}`}
+ ); +} + +const veryLongText = + "Second Series. You should always try to avoid long sentences. But oftentimes, it's not possible. So, we need to handle them gracefully. This is a very long sentence that should be fully readable."; + +export default function HtmlLegend() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/data/charts/components/HtmlLegend.tsx b/docs/data/charts/components/HtmlLegend.tsx new file mode 100644 index 0000000000000..a58701b379a8f --- /dev/null +++ b/docs/data/charts/components/HtmlLegend.tsx @@ -0,0 +1,66 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { unstable_useBarSeries } from '@mui/x-charts/hooks'; +import { ChartDataProvider } from '@mui/x-charts/context'; +import { ChartsSurface } from '@mui/x-charts/ChartsSurface'; +import { BarPlot } from '@mui/x-charts/BarChart'; +import { ChartsXAxis } from '@mui/x-charts/ChartsXAxis'; +import { ChartsYAxis } from '@mui/x-charts/ChartsYAxis'; + +function MyCustomLegend() { + const s = unstable_useBarSeries(); + return ( + + + {Object.values(s?.series ?? []).map((v) => { + return ( + + + + + ); + })} + +
+
+
{`${v.label}`}
+ ); +} + +const veryLongText = + "Second Series. You should always try to avoid long sentences. But oftentimes, it's not possible. So, we need to handle them gracefully. This is a very long sentence that should be fully readable."; + +export default function HtmlLegend() { + return ( + + + + + + + + + + + ); +} diff --git a/docs/data/charts/components/HtmlLegend.tsx.preview b/docs/data/charts/components/HtmlLegend.tsx.preview new file mode 100644 index 0000000000000..3f05cd43a0a0e --- /dev/null +++ b/docs/data/charts/components/HtmlLegend.tsx.preview @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/data/charts/components/components.md b/docs/data/charts/components/components.md index be2f67d72870d..791c338ced4fe 100644 --- a/docs/data/charts/components/components.md +++ b/docs/data/charts/components/components.md @@ -76,3 +76,22 @@ By using `invert`, the value associated with the current mouse coordinate `y` ca ``` {{"demo": "ScaleDemo.js"}} + +## HTML components + +With the introduction of the `ChartDataProvider` in v8, the chart data can be accessed from any component. +This allows you to create HTML components that interact with the charts data. + +In the next example, notice that `MyCustomLegend` component displays the series names and colors. +This creates an html `table` element, which handles long series names better than the default legend. + +{{"demo": "HtmlLegend.js"}} + +:::warning +Note that the HTML components are not part of the SVG hierarchy. +Hence, they should be: + +- Outside the `` component to avoid mixing HTAM and SVG. +- Inside the `` component to get access to the data. + +::: diff --git a/docs/data/charts/composition/composition.md b/docs/data/charts/composition/composition.md index 657c8724c1355..4bf8606dd5dba 100644 --- a/docs/data/charts/composition/composition.md +++ b/docs/data/charts/composition/composition.md @@ -2,7 +2,7 @@ title: React Chart composition productId: x-charts githubLabel: 'component: charts' -components: ChartContainer, ChartContainerPro, ChartsGrid +components: ChartContainer, ChartContainerPro, ChartsGrid, ChartDataProvider, ChartsSurface packageName: '@mui/x-charts' --- @@ -13,11 +13,75 @@ packageName: '@mui/x-charts' ## Overview The `@mui/x-charts` follows an architecture based on context providers. -The overall idea is to pass your series and axes definitions to a single component: the ``. -This component transforms the data and makes it available to its children. +The overall idea is to pass your series and axes definitions to special components. +This component transforms the data and makes it available to its children, which can be composed. -Based on the data provided by the container, you can render some graphical elements with provided subcomponents, such as `` or ``. -Or you can [create your own components](/x/react-charts/components/). +There are two main classes of components, which are used to create a chart. + +### Structural components + +These are used to define the chart's structure and data. + +#### The Data Provider and Surface components + +As the name suggests, the `ChartDataProvider` provides the data to the children components. +While the `ChartsSurface` renders the SVG elements. + +```jsx + + + {children} + + +``` + +:::info +The demos here are using the `ChartContainer` component. +To see demos using the separate `ChartDataProvider` and `ChartsSurface` components, check the [HTML components documentation](/x/react-charts/components/#html-components). +::: + +#### The `ChartContainer` helper + +This component is a composition of the two previous components. +It can be used instead of them when there is no need to customize anything outside the chart's graphical elements. + +```jsx + + {children} + +``` + +### Graphical components + +These are any component that render the graphical elements of the chart. +They are the children of the **Structural components** shown above. +There are many of them, so they won't all be listed here. +You can even [create your own components](/x/react-charts/components/). + +Some examples of graphical components are: + +- `LinePlot` +- `BarPlot` +- `ChartsXAxis` +- `ChartsLegend` +- `ChartsTooltip` ## Container options diff --git a/docs/data/chartsApiPages.ts b/docs/data/chartsApiPages.ts index ea2fd2d5bd4f6..c4ec485b59c66 100644 --- a/docs/data/chartsApiPages.ts +++ b/docs/data/chartsApiPages.ts @@ -47,6 +47,10 @@ const chartsApiPages: MuiPage[] = [ title: 'ChartContainerPro', plan: 'pro', }, + { + pathname: '/x/api/charts/chart-data-provider', + title: 'ChartDataProvider', + }, { pathname: '/x/api/charts/charts-axis', title: 'ChartsAxis', diff --git a/docs/pages/x/api/charts/chart-data-provider.js b/docs/pages/x/api/charts/chart-data-provider.js new file mode 100644 index 0000000000000..73a45a0bc9325 --- /dev/null +++ b/docs/pages/x/api/charts/chart-data-provider.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './chart-data-provider.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docsx/translations/api-docs/charts/chart-data-provider', + false, + /\.\/chart-data-provider.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/x/api/charts/chart-data-provider.json b/docs/pages/x/api/charts/chart-data-provider.json new file mode 100644 index 0000000000000..001c5b9e16d98 --- /dev/null +++ b/docs/pages/x/api/charts/chart-data-provider.json @@ -0,0 +1,70 @@ +{ + "props": { + "series": { + "type": { "name": "arrayOf", "description": "Array<object>" }, + "required": true + }, + "colors": { + "type": { "name": "union", "description": "Array<string>
| func" }, + "default": "blueberryTwilightPalette" + }, + "dataset": { "type": { "name": "arrayOf", "description": "Array<object>" } }, + "height": { "type": { "name": "number" } }, + "highlightedItem": { + "type": { + "name": "shape", + "description": "{ dataIndex?: number, seriesId?: number
| string }" + } + }, + "margin": { + "type": { + "name": "shape", + "description": "{ bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "object Depends on the charts type." + }, + "onHighlightChange": { + "type": { "name": "func" }, + "signature": { + "type": "function(highlightedItem: HighlightItemData | null) => void", + "describedArgs": ["highlightedItem"] + } + }, + "plugins": { "type": { "name": "arrayOf", "description": "Array<object>" } }, + "skipAnimation": { "type": { "name": "bool" } }, + "width": { "type": { "name": "number" } }, + "xAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ classes?: object, colorMap?: { colors: Array<string>, type: 'ordinal', unknownColor?: string, values?: Array<Date
| number
| string> }
| { color: Array<string>
| func, max?: Date
| number, min?: Date
| number, type: 'continuous' }
| { colors: Array<string>, thresholds: Array<Date
| number>, type: 'piecewise' }, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, domainLimit?: 'nice'
| 'strict'
| func, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, sx?: Array<func
| object
| bool>
| func
| object, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" + } + }, + "yAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ classes?: object, colorMap?: { colors: Array<string>, type: 'ordinal', unknownColor?: string, values?: Array<Date
| number
| string> }
| { color: Array<string>
| func, max?: Date
| number, min?: Date
| number, type: 'continuous' }
| { colors: Array<string>, thresholds: Array<Date
| number>, type: 'piecewise' }, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, domainLimit?: 'nice'
| 'strict'
| func, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'left'
| 'right', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, sx?: Array<func
| object
| bool>
| func
| object, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" + } + }, + "zAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ colorMap?: { colors: Array<string>, type: 'ordinal', unknownColor?: string, values?: Array<Date
| number
| string> }
| { color: Array<string>
| func, max?: Date
| number, min?: Date
| number, type: 'continuous' }
| { colors: Array<string>, thresholds: Array<Date
| number>, type: 'piecewise' }, data?: array, dataKey?: string, id?: string, max?: number, min?: number }>" + } + } + }, + "name": "ChartDataProvider", + "imports": [ + "import { ChartDataProvider } from '@mui/x-charts/context';", + "import { ChartDataProvider } from '@mui/x-charts';", + "import { ChartDataProvider } from '@mui/x-charts-pro';" + ], + "classes": [], + "spread": false, + "themeDefaultProps": false, + "muiName": "MuiChartDataProvider", + "forwardsRefTo": "SVGSVGElement", + "filename": "/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/pages/x/api/charts/charts-surface.json b/docs/pages/x/api/charts/charts-surface.json index ae4ebc5a067c1..e5abdd4769eed 100644 --- a/docs/pages/x/api/charts/charts-surface.json +++ b/docs/pages/x/api/charts/charts-surface.json @@ -12,6 +12,6 @@ "muiName": "MuiChartsSurface", "filename": "/packages/x-charts/src/ChartsSurface/ChartsSurface.tsx", "inheritance": null, - "demos": "", + "demos": "", "cssComponent": false } diff --git a/docs/translations/api-docs/charts/chart-container/chart-container.json b/docs/translations/api-docs/charts/chart-container/chart-container.json index a15bc34c6f245..45f4bcacdecad 100644 --- a/docs/translations/api-docs/charts/chart-container/chart-container.json +++ b/docs/translations/api-docs/charts/chart-container/chart-container.json @@ -1,5 +1,5 @@ { - "componentDescription": "", + "componentDescription": "It sets up the data providers as well as the `` for the chart.\n\nThis is a combination of both the `ChartDataProvider` and `ChartsSurface` components.", "propDescriptions": { "colors": { "description": "Color palette used to colorize multiple series." }, "dataset": { diff --git a/docs/translations/api-docs/charts/chart-data-provider/chart-data-provider.json b/docs/translations/api-docs/charts/chart-data-provider/chart-data-provider.json new file mode 100644 index 0000000000000..57a8f05201d9a --- /dev/null +++ b/docs/translations/api-docs/charts/chart-data-provider/chart-data-provider.json @@ -0,0 +1,42 @@ +{ + "componentDescription": "Orchestrates the data providers for the chart components and hooks.\n\nUse this component if you have custom HTML components that need to access the chart data.", + "propDescriptions": { + "colors": { "description": "Color palette used to colorize multiple series." }, + "dataset": { + "description": "An array of objects that can be used to populate series and axes data using their dataKey property." + }, + "height": { + "description": "The height of the chart in px. If not defined, it takes the height of the parent element." + }, + "highlightedItem": { + "description": "The item currently highlighted. Turns highlighting into a controlled prop." + }, + "margin": { + "description": "The margin between the SVG and the drawing area. It's used for leaving some space for extra information such as the x- and y-axis or legend. Accepts an object with the optional properties: top, bottom, left, and right." + }, + "onHighlightChange": { + "description": "The callback fired when the highlighted item changes.", + "typeDescriptions": { "highlightedItem": "The newly highlighted item." } + }, + "plugins": { + "description": "An array of plugins defining how to preprocess data. If not provided, the container supports line, bar, scatter and pie charts." + }, + "series": { + "description": "The array of series to display. Each type of series has its own specificity. Please refer to the appropriate docs page to learn more about it." + }, + "skipAnimation": { + "description": "If true, animations are skipped. If unset or false, the animations respects the user's prefers-reduced-motion setting." + }, + "width": { + "description": "The width of the chart in px. If not defined, it takes the width of the parent element." + }, + "xAxis": { + "description": "The configuration of the x-axes. If not provided, a default axis config is used. An array of AxisConfig objects." + }, + "yAxis": { + "description": "The configuration of the y-axes. If not provided, a default axis config is used. An array of AxisConfig objects." + }, + "zAxis": { "description": "The configuration of the z-axes." } + }, + "classDescriptions": {} +} diff --git a/docs/translations/api-docs/charts/charts-surface/charts-surface.json b/docs/translations/api-docs/charts/charts-surface/charts-surface.json index 40b01b75badb8..337a2c55eefd4 100644 --- a/docs/translations/api-docs/charts/charts-surface/charts-surface.json +++ b/docs/translations/api-docs/charts/charts-surface/charts-surface.json @@ -1,5 +1,5 @@ { - "componentDescription": "", + "componentDescription": "It provides the drawing area for the chart elements.\nIt is the root `` of all the chart elements.\n\nIt also provides the `title` and `desc` elements for the chart.", "propDescriptions": { "disableAxisListener": { "description": "If true, the charts will not listen to the mouse move event. It might break interactive features, but will improve performance." diff --git a/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx b/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx index 4c0d9f6dcaa09..f6f9ff734c349 100644 --- a/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx +++ b/packages/x-charts-pro/src/ChartContainerPro/ChartContainerPro.tsx @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import type {} from '../typeOverloads'; import { Watermark } from '@mui/x-license/Watermark'; import { ChartContainerProps } from '@mui/x-charts/ChartContainer'; -import { ResizableContainer } from '@mui/x-charts/internals'; import { ChartsSurface } from '@mui/x-charts/ChartsSurface'; import { getReleaseInfo } from '../internals/utils/releaseInfo'; import { ChartDataProviderPro } from '../context/ChartDataProviderPro'; @@ -19,15 +18,15 @@ const ChartContainerPro = React.forwardRef(function ChartContainerPro( props: ChartContainerProProps, ref: React.Ref, ) { - const { chartDataProviderProProps, children, resizableContainerProps, chartsSurfaceProps } = - useChartContainerProProps(props, ref); + const { chartDataProviderProProps, children, chartsSurfaceProps } = useChartContainerProProps( + props, + ref, + ); return ( - - {children} - - + {children} + ); }); diff --git a/packages/x-charts-pro/src/ChartContainerPro/useChartContainerProProps.ts b/packages/x-charts-pro/src/ChartContainerPro/useChartContainerProProps.ts index aac56a291a328..b8b898c95ef39 100644 --- a/packages/x-charts-pro/src/ChartContainerPro/useChartContainerProProps.ts +++ b/packages/x-charts-pro/src/ChartContainerPro/useChartContainerProProps.ts @@ -22,15 +22,16 @@ export const useChartContainerProProps = ( onZoomChange, }; - const { chartDataProviderProps, chartsSurfaceProps, resizableContainerProps, children } = - useChartContainerProps(baseProps, ref); + const { chartDataProviderProps, chartsSurfaceProps, children } = useChartContainerProps( + baseProps, + ref, + ); return { chartDataProviderProProps: { ...chartDataProviderProps, ...chartDataProviderProProps, }, - resizableContainerProps, chartsSurfaceProps, children, }; diff --git a/packages/x-charts/src/ChartContainer/ChartContainer.tsx b/packages/x-charts/src/ChartContainer/ChartContainer.tsx index 64c82944b3d9e..a938fb051e305 100644 --- a/packages/x-charts/src/ChartContainer/ChartContainer.tsx +++ b/packages/x-charts/src/ChartContainer/ChartContainer.tsx @@ -2,24 +2,47 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { ChartDataProvider, ChartDataProviderProps } from '../context/ChartDataProvider'; -import { ResizableContainer } from './ResizableContainer'; import { useChartContainerProps } from './useChartContainerProps'; import { ChartsSurface, ChartsSurfaceProps } from '../ChartsSurface'; export interface ChartContainerProps extends ChartDataProviderProps, ChartsSurfaceProps {} +/** + * It sets up the data providers as well as the `` for the chart. + * + * This is a combination of both the `ChartDataProvider` and `ChartsSurface` components. + * + * Demos: + * + * - [Composition](http://localhost:3001/x/react-charts/composition/) + * + * API: + * + * - [ChartContainer API](https://mui.com/x/api/charts/chart-container/) + * + * @example + * ```jsx + * + * + * + * + * ``` + */ const ChartContainer = React.forwardRef(function ChartContainer( props: ChartContainerProps, ref: React.Ref, ) { - const { chartDataProviderProps, children, resizableContainerProps, chartsSurfaceProps } = - useChartContainerProps(props, ref); + const { chartDataProviderProps, children, chartsSurfaceProps } = useChartContainerProps( + props, + ref, + ); return ( - - {children} - + {children} ); }); diff --git a/packages/x-charts/src/ChartContainer/ResizableContainer.tsx b/packages/x-charts/src/ChartContainer/ResizableContainer.tsx deleted file mode 100644 index e5c0f57d42a09..0000000000000 --- a/packages/x-charts/src/ChartContainer/ResizableContainer.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { styled } from '@mui/material/styles'; -import { useSize } from '../context/SizeProvider'; -import type { SizeContextState } from '../context/SizeProvider'; - -/** - * Wrapping div that take the shape of its parent. - * - * @ignore - do not document. - */ -export const ResizableContainerRoot = styled('div', { - name: 'MuiResponsiveChart', - slot: 'Container', -})<{ ownerState: Partial> }>(({ ownerState }) => ({ - width: ownerState.width ?? '100%', - height: ownerState.height ?? '100%', - display: 'flex', - position: 'relative', - flexGrow: 1, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - overflow: 'hidden', - '&>svg': { - width: '100%', - height: '100%', - }, -})); - -/** - * Wrapping div that take the shape of its parent. - * - * @ignore - do not document. - */ -function ResizableContainer(props: { children: React.ReactNode }) { - const { inHeight, inWidth, containerRef } = useSize(); - - return ( - - {props.children} - - ); -} - -ResizableContainer.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the TypeScript types and run "pnpm proptypes" | - // ---------------------------------------------------------------------- - children: PropTypes.node, -} as any; - -export { ResizableContainer }; diff --git a/packages/x-charts/src/ChartContainer/useChartContainerProps.ts b/packages/x-charts/src/ChartContainer/useChartContainerProps.ts index 8fa2a647f20df..395a2ff1bc95f 100644 --- a/packages/x-charts/src/ChartContainer/useChartContainerProps.ts +++ b/packages/x-charts/src/ChartContainer/useChartContainerProps.ts @@ -7,7 +7,6 @@ import type { ChartContainerProps } from './ChartContainer'; export type UseChartContainerPropsReturnValue = { chartDataProviderProps: ChartDataProviderProps; chartsSurfaceProps: ChartsSurfaceProps & { ref: React.Ref }; - resizableContainerProps: any; children: React.ReactNode; }; @@ -37,14 +36,13 @@ export const useChartContainerProps = ( ...other } = props; - const resizableContainerProps = other; - const chartsSurfaceProps: ChartsSurfaceProps & { ref: React.Ref } = { title, desc, sx, disableAxisListener, ref, + ...other, }; const chartDataProviderProps: ChartDataProviderProps = { @@ -65,7 +63,6 @@ export const useChartContainerProps = ( return { chartDataProviderProps, - resizableContainerProps, chartsSurfaceProps, children, }; diff --git a/packages/x-charts/src/ChartsSurface/ChartsSurface.test.tsx b/packages/x-charts/src/ChartsSurface/ChartsSurface.test.tsx index 1ea244bf21331..6e917eedb054c 100644 --- a/packages/x-charts/src/ChartsSurface/ChartsSurface.test.tsx +++ b/packages/x-charts/src/ChartsSurface/ChartsSurface.test.tsx @@ -3,7 +3,7 @@ import { createRenderer } from '@mui/internal-test-utils'; import { ChartsSurface } from '@mui/x-charts/ChartsSurface'; import { expect } from 'chai'; import { SizeProvider } from '../context/SizeProvider'; -import { ChartProvider } from '../internals'; +import { ChartProvider } from '../context/ChartProvider'; describe('', () => { // JSDOM doesn't implement SVGElement diff --git a/packages/x-charts/src/ChartsSurface/ChartsSurface.tsx b/packages/x-charts/src/ChartsSurface/ChartsSurface.tsx index 605e8b2f00f0d..2403575a9b649 100644 --- a/packages/x-charts/src/ChartsSurface/ChartsSurface.tsx +++ b/packages/x-charts/src/ChartsSurface/ChartsSurface.tsx @@ -5,9 +5,9 @@ import * as React from 'react'; import useForkRef from '@mui/utils/useForkRef'; import { useAxisEvents } from '../hooks/useAxisEvents'; import { ChartsAxesGradients } from '../internals/components/ChartsAxesGradients'; -import { useDrawingArea } from '../hooks/useDrawingArea'; -import { useSvgRef } from '../hooks/useSvgRef'; +import { useDrawingArea, useSvgRef } from '../hooks'; import { useSize } from '../context/SizeProvider'; +import type { SizeContextState } from '../context/SizeProvider'; export interface ChartsSurfaceProps { className?: string; @@ -26,20 +26,43 @@ export interface ChartsSurfaceProps { const ChartsSurfaceStyles = styled('svg', { name: 'MuiChartsSurface', slot: 'Root', -})(() => ({ +})<{ ownerState: Partial> }>(({ ownerState }) => ({ + width: ownerState.width ?? '100%', + height: ownerState.height ?? '100%', + display: 'flex', + position: 'relative', + flexGrow: 1, + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + overflow: 'hidden', // This prevents default touch actions when using the svg on mobile devices. // For example, prevent page scroll & zoom. touchAction: 'none', })); +/** + * It provides the drawing area for the chart elements. + * It is the root `` of all the chart elements. + * + * It also provides the `title` and `desc` elements for the chart. + * + * Demos: + * + * - [Composition](http://localhost:3001/x/react-charts/composition/) + * + * API: + * + * - [ChartsSurface API](https://mui.com/x/api/charts/charts-surface/) + */ const ChartsSurface = React.forwardRef(function ChartsSurface( inProps: ChartsSurfaceProps, ref: React.Ref, ) { const { width, height, left, right, top, bottom } = useDrawingArea(); - const { hasIntrinsicSize } = useSize(); + const { hasIntrinsicSize, svgRef: containerRef, inHeight, inWidth } = useSize(); const svgRef = useSvgRef(); - const handleRef = useForkRef(svgRef, ref); + const handleRef = useForkRef(containerRef, svgRef, ref); const themeProps = useThemeProps({ props: inProps, name: 'MuiChartsSurface' }); const { children, disableAxisListener = false, className, title, desc, ...other } = themeProps; @@ -58,6 +81,7 @@ const ChartsSurface = React.forwardRef(functi return ( , Pick, - Omit { + Omit, + React.SVGProps { /** * The width of the chart in px. If not defined, it takes the width of the parent element. */ @@ -23,42 +24,12 @@ export interface GaugeContainerProps children?: React.ReactNode; } -const ResizableContainerRoot = styled('div', { - name: 'MuiGauge', - slot: 'Container', -})<{ ownerState: Pick }>(({ ownerState, theme }) => ({ - width: ownerState.width ?? '100%', - height: ownerState.height ?? '100%', - display: 'flex', - position: 'relative', - flexGrow: 1, - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - overflow: 'hidden', - '&>svg': { - width: '100%', - height: '100%', - }, +const GStyled = styled('g')(({ theme }) => ({ '& text': { fill: (theme.vars || theme).palette.text.primary, }, })); -function ResizableContainer(props: any) { - const { inHeight, inWidth, hasIntrinsicSize, containerRef } = useSize(); - - return ( - - {hasIntrinsicSize && props.children} - - ); -} - const GaugeContainer = React.forwardRef(function GaugeContainer( props: GaugeContainerProps, ref: React.Ref, @@ -99,23 +70,19 @@ const GaugeContainer = React.forwardRef(function GaugeContainer( cx={cx} cy={cy} > - - - + +
diff --git a/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx b/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx index e9c03e79cb78f..a75f2d7060b51 100644 --- a/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx +++ b/packages/x-charts/src/context/ChartDataProvider/ChartDataProvider.tsx @@ -40,6 +40,33 @@ export type ChartDataProviderProps = Omit< children?: React.ReactNode; }; +/** + * Orchestrates the data providers for the chart components and hooks. + * + * Use this component if you have custom HTML components that need to access the chart data. + * + * Demos: + * + * - [Composition](http://localhost:3001/x/react-charts/composition/) + * + * API: + * + * - [ChartDataProvider API](https://mui.com/x/api/charts/chart-data-provider/) + * + * @example + * ```jsx + * + * + * + * + * + * {'Custom Legend Component'} + * + * ``` + */ function ChartDataProvider(props: ChartDataProviderProps) { const { children, diff --git a/packages/x-charts/src/context/SizeProvider/Size.types.ts b/packages/x-charts/src/context/SizeProvider/Size.types.ts index 811e5dcd498dc..46a477ce55516 100644 --- a/packages/x-charts/src/context/SizeProvider/Size.types.ts +++ b/packages/x-charts/src/context/SizeProvider/Size.types.ts @@ -14,9 +14,9 @@ export interface SizeProviderProps { export interface SizeContextState extends Required> { /** - * The ref of the container element that the chart is rendered in. + * The ref of the svg element that the chart is rendered in. */ - containerRef: React.RefObject; + svgRef: React.RefObject; /** * If the chart has a defined size. */ diff --git a/packages/x-charts/src/context/SizeProvider/SizeContext.ts b/packages/x-charts/src/context/SizeProvider/SizeContext.ts index f9c4b22b7a9cd..b60bd58d9a8b8 100644 --- a/packages/x-charts/src/context/SizeProvider/SizeContext.ts +++ b/packages/x-charts/src/context/SizeProvider/SizeContext.ts @@ -7,7 +7,7 @@ export const SizeContext = React.createContext>( isInitialized: false, data: { hasIntrinsicSize: false, - containerRef: null as any, + svgRef: { current: null as any }, height: 0, width: 0, }, diff --git a/packages/x-charts/src/context/SizeProvider/useChartContainerDimensions.ts b/packages/x-charts/src/context/SizeProvider/useChartContainerDimensions.ts index 032af4225d689..4e4d566ccb4d2 100644 --- a/packages/x-charts/src/context/SizeProvider/useChartContainerDimensions.ts +++ b/packages/x-charts/src/context/SizeProvider/useChartContainerDimensions.ts @@ -9,7 +9,7 @@ const MAX_COMPUTE_RUN = 10; export const useChartContainerDimensions = (inWidth?: number, inHeight?: number) => { const hasInSize = inWidth !== undefined && inHeight !== undefined; const stateRef = React.useRef({ displayError: false, initialCompute: true, computeRun: 0 }); - const rootRef = React.useRef(null); + const rootRef = React.useRef(null); const [width, setWidth] = React.useState(0); const [height, setHeight] = React.useState(0); @@ -19,6 +19,17 @@ export const useChartContainerDimensions = (inWidth?: number, inHeight?: number) const mainEl = rootRef?.current; if (!mainEl) { + if (process.env.NODE_ENV !== 'production') { + // This is mostly for internal use. + throw new Error( + [ + `MUI X: ChartContainer does not have a valid reference to the element.`, + 'This may be caused by a ref forwarding issue.', + 'Make sure that the ref from SizedProvider is forwarded correctly.', + ].join('\n'), + ); + } + return {}; } @@ -112,7 +123,7 @@ export const useChartContainerDimensions = (inWidth?: number, inHeight?: number) const finalHeight = inHeight ?? height; return { - containerRef: rootRef, + svgRef: rootRef, width: finalWidth, height: finalHeight, hasIntrinsicSize: finalWidth > 0 && finalHeight > 0, diff --git a/packages/x-charts/src/internals/index.ts b/packages/x-charts/src/internals/index.ts index a41e2313e99fd..b3079186b4810 100644 --- a/packages/x-charts/src/internals/index.ts +++ b/packages/x-charts/src/internals/index.ts @@ -1,8 +1,6 @@ // Components export * from './components/ChartsAxesGradients'; -export * from '../ChartContainer/ResizableContainer'; - // hooks export { useSeries } from '../hooks/useSeries'; export { useInteractionItemProps } from '../hooks/useInteractionItemProps'; diff --git a/scripts/buildApiDocs/chartsSettings/index.ts b/scripts/buildApiDocs/chartsSettings/index.ts index a9631f07f4ea7..2c9dabdcb7e77 100644 --- a/scripts/buildApiDocs/chartsSettings/index.ts +++ b/scripts/buildApiDocs/chartsSettings/index.ts @@ -54,6 +54,9 @@ export default chartsApiPages; translationLanguages: LANGUAGES, skipComponent(filename) { if (filename.includes('/context/')) { + if (filename.endsWith('ChartDataProvider.tsx')) { + return false; + } return true; } return [