Skip to content

Commit

Permalink
[charts] HTML Labels (mui#15813)
Browse files Browse the repository at this point in the history
Signed-off-by: Jose C Quintas Jr <[email protected]>
Co-authored-by: Alexandre Fauquette <[email protected]>
  • Loading branch information
JCQuintas and alexfauquette authored Dec 11, 2024
1 parent 46c46ff commit 99ce15c
Show file tree
Hide file tree
Showing 12 changed files with 653 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/x-charts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@react-spring/web": "^9.7.5",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-is": "^18.3.1",
"reselect": "^5.1.1",
"use-sync-external-store": "^1.4.0"
},
Expand All @@ -73,6 +74,7 @@
"@react-spring/core": "^9.7.5",
"@react-spring/shared": "^9.7.5",
"@types/prop-types": "^15.7.14",
"@types/react-is": "^18.3.0",
"@types/use-sync-external-store": "^0.0.6",
"csstype": "^3.1.3",
"rimraf": "^6.0.1"
Expand Down
23 changes: 23 additions & 0 deletions packages/x-charts/src/ChartsLabel/ChartsLabel.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { createRenderer } from '@mui/internal-test-utils/createRenderer';
import { describeConformance } from 'test/utils/describeConformance';
import { ChartsLabel } from '@mui/x-charts/ChartsLabel/ChartsLabel';
import { labelClasses } from '@mui/x-charts/ChartsLabel/labelClasses';
import { createTheme, ThemeProvider } from '@mui/material/styles';

describe('<ChartsLabel />', () => {
const { render } = createRenderer();

describeConformance(<ChartsLabel />, () => ({
classes: labelClasses,
inheritComponent: 'div',
render,
muiName: 'MuiChartsLabel',
testComponentPropWith: 'div',
refInstanceof: window.HTMLSpanElement,
ThemeProvider,
createTheme,
// SKIP
skip: ['themeVariants', 'componentProp', 'componentsProp'],
}));
});
63 changes: 63 additions & 0 deletions packages/x-charts/src/ChartsLabel/ChartsLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { styled, SxProps, Theme } from '@mui/material/styles';
import clsx from 'clsx';
import { ChartsLabelClasses, useUtilityClasses } from './labelClasses';
import { consumeThemeProps } from '../internals/consumeThemeProps';

export interface ChartsLabelProps {
/**
* Override or extend the styles applied to the component.
*/
classes?: Partial<ChartsLabelClasses>;
children?: React.ReactNode;
className?: string;
sx?: SxProps<Theme>;
}

const Root = styled('span', {
name: 'MuiChartsLabel',
slot: 'Root',
overridesResolver: (props, styles) => styles.root,
})<{ ownerState: ChartsLabelProps }>(({ theme }) => ({
...theme.typography.caption,
color: (theme.vars || theme).palette.text.primary,
lineHeight: undefined,
display: 'flex',
}));

/**
* @ignore - internal component.
*
* Generates the label mark for the tooltip and legend.
*/
const ChartsLabel = consumeThemeProps(
'MuiChartsLabel',
{
classesResolver: useUtilityClasses,
},
function ChartsLabel(props: ChartsLabelProps, ref: React.Ref<HTMLSpanElement>) {
const { children, className, classes, ...other } = props;

return (
<Root className={clsx(classes?.root, className)} ownerState={props} ref={ref} {...other}>
{children}
</Root>
);
},
);

ChartsLabel.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,
/**
* Override or extend the styles applied to the component.
*/
classes: PropTypes.object,
} as any;

export { ChartsLabel };
45 changes: 45 additions & 0 deletions packages/x-charts/src/ChartsLabel/ChartsLabelGradient.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as React from 'react';
import { createRenderer } from '@mui/internal-test-utils/createRenderer';
import { describeConformance } from 'test/utils/describeConformance';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { ChartsLabelGradient } from '@mui/x-charts/ChartsLabel/ChartsLabelGradient';
import { labelGradientClasses } from '@mui/x-charts/ChartsLabel/labelGradientClasses';

describe('<ChartsLabelGradient />', () => {
const { render } = createRenderer();

describeConformance(<ChartsLabelGradient gradientId="ChartsLabelGradient.test-id" />, () => ({
classes: labelGradientClasses,
inheritComponent: 'div',
render: (node) =>
render(node, {
wrapper: ({ children }) => (
<React.Fragment>
{children}
<Gradient id="ChartsLabelGradient.test-id" />
</React.Fragment>
),
}),
muiName: 'MuiChartsLabelGradient',
testComponentPropWith: 'div',
refInstanceof: window.HTMLDivElement,
ThemeProvider,
createTheme,
// SKIP
skip: ['themeVariants', 'componentProp', 'componentsProp'],
}));
});

function Gradient({ id }: any) {
return (
<svg width="0" height="0" viewBox="0 0 0 0" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id={id} x1="0" y1="0" x2="1" y2="0" gradientUnits="objectBoundingBox">
<stop offset="0" stopColor="#CAD4EE" />
<stop offset="0.5" stopColor="#4254FB" />
<stop offset="1" stopColor="#091159" />
</linearGradient>
</defs>
</svg>
);
}
166 changes: 166 additions & 0 deletions packages/x-charts/src/ChartsLabel/ChartsLabelGradient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { styled, SxProps, Theme } from '@mui/material/styles';
import clsx from 'clsx';
import {
ChartsLabelGradientClasses,
useUtilityClasses,
labelGradientClasses,
} from './labelGradientClasses';
import { consumeThemeProps } from '../internals/consumeThemeProps';

export interface ChartsLabelGradientProps {
/**
* A unique identifier for the gradient.
*
* The `gradientId` will be used as `fill="url(#gradientId)"`.
*/
gradientId: string;
/**
* The direction of the gradient.
*
* @default 'row'
*/
direction?: 'column' | 'row';
/**
* If `true`, the gradient will be reversed.
*/
reverse?: boolean;
/**
* If provided, the gradient will be rotated by 90deg.
*
* Useful for linear gradients that are not in the correct orientation.
*/
rotate?: boolean;
/**
* Override or extend the styles applied to the component.
*/
classes?: Partial<ChartsLabelGradientClasses>;
className?: string;
sx?: SxProps<Theme>;
}

const getRotation = (direction?: 'column' | 'row', reverse?: boolean, rotate?: boolean) => {
if (!rotate && reverse) {
return direction === 'column' ? 90 : 180;
}

if (rotate && !reverse) {
return direction === 'column' ? 0 : 90;
}

if (rotate && reverse) {
return direction === 'column' ? 180 : -90;
}

return direction === 'column' ? -90 : 0;
};

const Root = styled('div', {
name: 'MuiChartsLabelGradient',
slot: 'Root',
overridesResolver: (props, styles) => styles.root,
})<{ ownerState: ChartsLabelGradientProps }>(({ ownerState }) => {
const rotation = getRotation(ownerState.direction, ownerState.reverse, ownerState.rotate);

return {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
[`.${labelGradientClasses.mask}`]: {
borderRadius: 2,
overflow: 'hidden',
},
[`&.${labelGradientClasses.row}`]: {
width: '100%',
[`.${labelGradientClasses.mask}`]: {
height: 12,
width: '100%',
},
},
[`&.${labelGradientClasses.column}`]: {
height: '100%',
[`.${labelGradientClasses.mask}`]: {
width: 12,
height: '100%',
'> svg': {
height: '100%',
},
},
},
svg: {
transform: `rotate(${rotation}deg)`,
display: 'block',
},
};
});

/**
* @ignore - internal component.
*
* Generates the label Gradient for the tooltip and legend.
*/
const ChartsLabelGradient = consumeThemeProps(
'MuiChartsLabelGradient',
{
defaultProps: {
direction: 'row',
},
classesResolver: useUtilityClasses,
},
function ChartsLabelGradient(props: ChartsLabelGradientProps, ref: React.Ref<HTMLDivElement>) {
const { gradientId, direction, classes, className, ...other } = props;

return (
<Root
className={clsx(classes?.root, className)}
ownerState={props}
aria-hidden="true"
ref={ref}
{...other}
>
<div className={classes?.mask}>
<svg viewBox="0 0 24 24">
<rect width="24" height="24" fill={`url(#${gradientId})`} />
</svg>
</div>
</Root>
);
},
);

ChartsLabelGradient.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "pnpm proptypes" |
// ----------------------------------------------------------------------
/**
* Override or extend the styles applied to the component.
*/
classes: PropTypes.object,
/**
* The direction of the gradient.
*
* @default 'row'
*/
direction: PropTypes.oneOf(['column', 'row']),
/**
* A unique identifier for the gradient.
*
* The `gradientId` will be used as `fill="url(#gradientId)"`.
*/
gradientId: PropTypes.string.isRequired,
/**
* If `true`, the gradient will be reversed.
*/
reverse: PropTypes.bool,
/**
* If provided, the gradient will be rotated by 90deg.
*
* Useful for linear gradients that are not in the correct orientation.
*/
rotate: PropTypes.bool,
} as any;

export { ChartsLabelGradient };
23 changes: 23 additions & 0 deletions packages/x-charts/src/ChartsLabel/ChartsLabelMark.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import { createRenderer } from '@mui/internal-test-utils/createRenderer';
import { describeConformance } from 'test/utils/describeConformance';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { ChartsLabelMark } from '@mui/x-charts/ChartsLabel/ChartsLabelMark';
import { labelMarkClasses } from '@mui/x-charts/ChartsLabel/labelMarkClasses';

describe('<ChartsLabelMark />', () => {
const { render } = createRenderer();

describeConformance(<ChartsLabelMark />, () => ({
classes: labelMarkClasses,
inheritComponent: 'div',
render,
muiName: 'MuiChartsLabelMark',
testComponentPropWith: 'div',
refInstanceof: window.HTMLDivElement,
ThemeProvider,
createTheme,
// SKIP
skip: ['themeVariants', 'componentProp', 'componentsProp'],
}));
});
Loading

0 comments on commit 99ce15c

Please sign in to comment.