Skip to content

Commit c2b2ad2

Browse files
authored
Merge branch 'master' into retain-expansion-state
2 parents c0eea11 + 7f5f253 commit c2b2ad2

File tree

5 files changed

+93
-9
lines changed

5 files changed

+93
-9
lines changed

docs/pages/x/api/charts/pie-arc.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
"description": "Styles applied to the root element when faded.",
1515
"isGlobal": false
1616
},
17+
{
18+
"key": "focusIndicator",
19+
"className": "MuiPieArc-focusIndicator",
20+
"description": "Styles applied to the focus indicator element.",
21+
"isGlobal": false
22+
},
1723
{
1824
"key": "highlighted",
1925
"className": "MuiPieArc-highlighted",

docs/translations/api-docs/charts/pie-arc/pie-arc.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
"nodeName": "the root element",
88
"conditions": "faded"
99
},
10+
"focusIndicator": {
11+
"description": "Styles applied to {{nodeName}}.",
12+
"nodeName": "the focus indicator element"
13+
},
1014
"highlighted": {
1115
"description": "Styles applied to {{nodeName}} when {{conditions}}.",
1216
"nodeName": "the root element",

packages/x-charts/src/PieChart/PieArc.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
'use client';
22
import * as React from 'react';
33
import PropTypes from 'prop-types';
4+
import clsx from 'clsx';
45
import composeClasses from '@mui/utils/composeClasses';
56
import generateUtilityClass from '@mui/utils/generateUtilityClass';
6-
import { styled } from '@mui/material/styles';
7+
import { styled, useTheme } from '@mui/material/styles';
78
import generateUtilityClasses from '@mui/utils/generateUtilityClasses';
89
import { useAnimatePieArc } from '../hooks';
910
import { ANIMATION_DURATION_MS, ANIMATION_TIMING_FUNCTION } from '../internals/animation/animation';
@@ -22,6 +23,8 @@ export interface PieArcClasses {
2223
* Needs to be suffixed with the series ID: `.${pieArcClasses.series}-${seriesId}`.
2324
*/
2425
series: string;
26+
/** Styles applied to the focus indicator element. */
27+
focusIndicator: string;
2528
}
2629

2730
export type PieArcClassKey = keyof PieArcClasses;
@@ -33,6 +36,7 @@ interface PieArcOwnerState {
3336
isFaded: boolean;
3437
isHighlighted: boolean;
3538
isFocused: boolean;
39+
stroke?: string;
3640
classes?: Partial<PieArcClasses>;
3741
}
3842

@@ -45,6 +49,7 @@ export const pieArcClasses: PieArcClasses = generateUtilityClasses('MuiPieArc',
4549
'highlighted',
4650
'faded',
4751
'series',
52+
'focusIndicator',
4853
]);
4954

5055
const useUtilityClasses = (ownerState: PieArcOwnerState) => {
@@ -66,13 +71,11 @@ const PieArcRoot = styled('path', {
6671
name: 'MuiPieArc',
6772
slot: 'Root',
6873
overridesResolver: (_, styles) => styles.arc, // FIXME: Inconsistent naming with slot
69-
})<{ ownerState: PieArcOwnerState }>(({ theme }) => ({
70-
// Got to move stroke to an element prop instead of style.
71-
stroke: (theme.vars || theme).palette.background.paper,
74+
})<{ ownerState: PieArcOwnerState }>({
7275
transitionProperty: 'opacity, fill, filter',
7376
transitionDuration: `${ANIMATION_DURATION_MS}ms`,
7477
transitionTimingFunction: ANIMATION_TIMING_FUNCTION,
75-
}));
78+
});
7679

7780
export type PieArcProps = Omit<React.SVGProps<SVGPathElement>, 'ref' | 'id'> &
7881
PieArcOwnerState & {
@@ -89,6 +92,7 @@ export type PieArcProps = Omit<React.SVGProps<SVGPathElement>, 'ref' | 'id'> &
8992

9093
const PieArc = React.forwardRef<SVGPathElement, PieArcProps>(function PieArc(props, ref) {
9194
const {
95+
className,
9296
classes: innerClasses,
9397
color,
9498
dataIndex,
@@ -104,9 +108,13 @@ const PieArc = React.forwardRef<SVGPathElement, PieArcProps>(function PieArc(pro
104108
outerRadius,
105109
paddingAngle,
106110
skipAnimation,
111+
stroke: strokeProp,
107112
...other
108113
} = props;
109114

115+
const theme = useTheme();
116+
const stroke = strokeProp ?? (theme.vars || theme).palette.background.paper;
117+
110118
const ownerState = {
111119
id,
112120
dataIndex,
@@ -135,15 +143,15 @@ const PieArc = React.forwardRef<SVGPathElement, PieArcProps>(function PieArc(pro
135143
onClick={onClick}
136144
cursor={onClick ? 'pointer' : 'unset'}
137145
ownerState={ownerState}
138-
className={classes.root}
146+
className={clsx(classes.root, className)}
139147
fill={ownerState.color}
140148
opacity={ownerState.isFaded ? 0.3 : 1}
141149
filter={ownerState.isHighlighted ? 'brightness(120%)' : 'none'}
150+
stroke={stroke}
142151
strokeWidth={1}
143152
strokeLinejoin="round"
144153
data-highlighted={ownerState.isHighlighted || undefined}
145154
data-faded={ownerState.isFaded || undefined}
146-
data-focused={isFocused || undefined}
147155
{...other}
148156
{...interactionProps}
149157
{...animatedProps}

packages/x-charts/src/PieChart/PieArcPlot.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
'use client';
22
import * as React from 'react';
33
import PropTypes from 'prop-types';
4-
import { PieArc, PieArcProps } from './PieArc';
4+
import { useTheme } from '@mui/material/styles';
5+
import { useFocusedItem } from '../hooks/useFocusedItem';
6+
import { PieArc, PieArcProps, pieArcClasses } from './PieArc';
57
import {
68
ComputedPieRadius,
79
DefaultizedPieSeriesType,
@@ -74,6 +76,8 @@ function PieArcPlot(props: PieArcPlotProps) {
7476
...other
7577
} = props;
7678

79+
const theme = useTheme();
80+
7781
const transformedData = useTransformData({
7882
innerRadius,
7983
outerRadius,
@@ -85,6 +89,9 @@ function PieArcPlot(props: PieArcPlotProps) {
8589
data,
8690
});
8791

92+
const { dataIndex: focusedIndex = -1 } = useFocusedItem() ?? {};
93+
const focusedItem = focusedIndex !== -1 ? transformedData[focusedIndex] : null;
94+
8895
if (data.length === 0) {
8996
return null;
9097
}
@@ -118,6 +125,27 @@ function PieArcPlot(props: PieArcPlotProps) {
118125
{...slotProps?.pieArc}
119126
/>
120127
))}
128+
{/* Render the focus indicator last, so it can align nicely over all arcs */}
129+
{focusedItem && (
130+
<Arc
131+
startAngle={focusedItem.startAngle}
132+
endAngle={focusedItem.endAngle}
133+
paddingAngle={focusedItem.paddingAngle}
134+
innerRadius={focusedItem.innerRadius}
135+
color={focusedItem.color}
136+
outerRadius={focusedItem.outerRadius}
137+
cornerRadius={focusedItem.cornerRadius}
138+
skipAnimation
139+
stroke={(theme.vars ?? theme).palette.text.primary}
140+
id={id}
141+
className={pieArcClasses.focusIndicator}
142+
dataIndex={focusedIndex}
143+
isFaded={false}
144+
isHighlighted={false}
145+
isFocused={false}
146+
strokeWidth={3}
147+
/>
148+
)}
121149
</g>
122150
);
123151
}

packages/x-charts/src/PieChart/PieChart.test.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { createRenderer, screen } from '@mui/internal-test-utils/createRenderer';
2+
import { createRenderer, screen, act } from '@mui/internal-test-utils';
33
import { describeConformance } from 'test/utils/describeConformance';
44
import { pieArcClasses, PieChart } from '@mui/x-charts/PieChart';
55

@@ -75,4 +75,42 @@ describe('<PieChart />', () => {
7575

7676
expect(screen.queryByRole('tooltip')).to.equal(null);
7777
});
78+
79+
it('should show focus indicator when navigating with keyboard', async () => {
80+
const { container, user } = render(
81+
<PieChart
82+
enableKeyboardNavigation
83+
data-testid="chart"
84+
height={100}
85+
width={100}
86+
series={[
87+
{
88+
data: [
89+
{ id: 0, value: 10 },
90+
{ id: 1, value: 20 },
91+
],
92+
},
93+
]}
94+
hideLegend
95+
/>,
96+
);
97+
98+
// by default does not show focus indicator
99+
expect(container.querySelector(`.${pieArcClasses.focusIndicator}`)).not.toBeTruthy();
100+
101+
// focus the chart
102+
await act(async () => screen.getByTestId('chart').focus());
103+
104+
// Focus the first arc
105+
await user.keyboard('{ArrowRight}');
106+
expect(
107+
container.querySelector(`.${pieArcClasses.focusIndicator}.MuiPieArc-data-index-0`),
108+
).toBeTruthy();
109+
110+
// Focus the second arc
111+
await user.keyboard('{ArrowRight}');
112+
expect(
113+
container.querySelector(`.${pieArcClasses.focusIndicator}.MuiPieArc-data-index-1`),
114+
).toBeTruthy();
115+
});
78116
});

0 commit comments

Comments
 (0)