Skip to content

Commit

Permalink
[charts] Support rtl for gradient legend
Browse files Browse the repository at this point in the history
  • Loading branch information
JCQuintas committed Jan 9, 2025
1 parent 296a499 commit a8961c3
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 9 deletions.
172 changes: 170 additions & 2 deletions packages/x-charts/src/ChartsLabel/ChartsLabelGradient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ 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';
import { expect } from 'chai';
import { describeSkipIf, isJSDOM } from 'test/utils/skipIf';
import RtlProvider from '@mui/system/RtlProvider';

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

describeConformance(<ChartsLabelGradient gradientId="ChartsLabelGradient.test-id" />, () => ({
describeConformance(<ChartsLabelGradient gradientId="gradient.test-id" />, () => ({
classes: labelGradientClasses,
inheritComponent: 'div',
render: (node) =>
render(node, {
wrapper: ({ children }) => (
<React.Fragment>
{children}
<Gradient id="ChartsLabelGradient.test-id" />
<Gradient id="gradient.test-id" />
</React.Fragment>
),
}),
Expand All @@ -28,6 +31,163 @@ describe('<ChartsLabelGradient />', () => {
// SKIP
skip: ['themeVariants', 'componentProp', 'componentsProp'],
}));

// JSDOM does not support SVGMatrix
describeSkipIf(isJSDOM)('rotation', () => {
const matrixToRotation = (element: SVGSVGElement | null) => {
if (!element) {
throw new Error('Svg element not found');
}

const matrix = new DOMMatrix(getComputedStyle(element).transform);
return (Math.atan2(matrix.b, matrix.a) * 180) / Math.PI;
};

describe('horizontal', () => {
it('should render a gradient in the correct orientation', () => {
const { container } = render(<ChartsLabelGradient gradientId="gradient.test-id" />);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(0);
});

it('should reverse the gradient', () => {
const { container } = render(<ChartsLabelGradient gradientId="gradient.test-id" reverse />);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(180);
});

it('should rotate the gradient', () => {
const { container } = render(<ChartsLabelGradient gradientId="gradient.test-id" rotate />);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(90);
});

it('should reverse and rotate the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" reverse rotate />,
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(-90);
});
});

describe('vertical', () => {
it('should render a gradient in the correct orientation', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" direction="vertical" />,
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(-90);
});

it('should reverse the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" direction="vertical" reverse />,
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(90);
});

it('should rotate the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" direction="vertical" rotate />,
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(0);
});

it('should reverse and rotate the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" direction="vertical" reverse rotate />,
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(180);
});
});

describe('RTL', () => {
describe('horizontal', () => {
it('should render a gradient in the correct orientation', () => {
const { container } = render(<ChartsLabelGradient gradientId="gradient.test-id" />, {
wrapper: RtlWrapper,
});
const svg = container.querySelector('svg');
// Technically it is -180, but the browser will normalize it to 180
expect(matrixToRotation(svg)).to.equal(180);
});

it('should reverse the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" reverse />,
{ wrapper: RtlWrapper },
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(0);
});

it('should rotate the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" rotate />,
{ wrapper: RtlWrapper },
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(90);
});

it('should reverse and rotate the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" reverse rotate />,
{ wrapper: RtlWrapper },
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(-90);
});
});

describe('vertical', () => {
it('should render a gradient in the correct orientation', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" direction="vertical" />,
{ wrapper: RtlWrapper },
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(-90);
});

it('should reverse the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" direction="vertical" reverse />,
{ wrapper: RtlWrapper },
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(90);
});

it('should rotate the gradient', () => {
const { container } = render(
<ChartsLabelGradient gradientId="gradient.test-id" direction="vertical" rotate />,
{ wrapper: RtlWrapper },
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(180);
});

it('should reverse and rotate the gradient', () => {
const { container } = render(
<ChartsLabelGradient
gradientId="gradient.test-id"
direction="vertical"
reverse
rotate
/>,
{ wrapper: RtlWrapper },
);
const svg = container.querySelector('svg');
expect(matrixToRotation(svg)).to.equal(0);
});
});
});
});
});

function Gradient({ id }: any) {
Expand All @@ -43,3 +203,11 @@ function Gradient({ id }: any) {
</svg>
);
}

function RtlWrapper({ children }: any) {
return (
<RtlProvider value>
<div dir="rtl">{children}</div>
</RtlProvider>
);
}
24 changes: 17 additions & 7 deletions packages/x-charts/src/ChartsLabel/ChartsLabelGradient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { styled, SxProps, Theme } from '@mui/material/styles';
import clsx from 'clsx';
import { useRtl } from '@mui/system/RtlProvider';
import {
ChartsLabelGradientClasses,
useUtilityClasses,
Expand Down Expand Up @@ -43,32 +44,40 @@ export interface ChartsLabelGradientProps {
sx?: SxProps<Theme>;
}

const applyRtl = (isRtl: boolean | undefined, value: number) => (isRtl ? value - 180 : value);

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

if (rotate && !reverse) {
return direction === 'vertical' ? 0 : 90;
return direction === 'vertical' ? applyRtl(isRtl, 0) : 90;
}

if (rotate && reverse) {
return direction === 'vertical' ? 180 : -90;
return direction === 'vertical' ? applyRtl(isRtl, 180) : -90;
}

return direction === 'vertical' ? -90 : 0;
return direction === 'vertical' ? -90 : applyRtl(isRtl, 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);
})<{ ownerState: ChartsLabelGradientProps & { isRtl: boolean } }>(({ ownerState }) => {
const rotation = getRotation(
ownerState.direction,
ownerState.reverse,
ownerState.rotate,
ownerState.isRtl,
);

return {
display: 'flex',
Expand Down Expand Up @@ -118,11 +127,12 @@ const ChartsLabelGradient = consumeThemeProps(
function ChartsLabelGradient(props: ChartsLabelGradientProps, ref: React.Ref<HTMLDivElement>) {
const { gradientId, direction, classes, className, rotate, reverse, thickness, ...other } =
props;
const isRtl = useRtl();

return (
<Root
className={clsx(classes?.root, className)}
ownerState={props}
ownerState={{ ...props, isRtl }}
aria-hidden="true"
ref={ref}
{...other}
Expand Down

0 comments on commit a8961c3

Please sign in to comment.