Skip to content

Commit ccd10f7

Browse files
authored
[Checkbox][Radio] Respect global disableRipple from MuiButtonBase defaultProps (#48795)
1 parent 0182cde commit ccd10f7

4 files changed

Lines changed: 85 additions & 1 deletion

File tree

packages/mui-material/src/Checkbox/Checkbox.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ const Checkbox = React.forwardRef(function Checkbox(inProps, ref) {
162162
checkedIcon: React.cloneElement(indeterminateIcon, {
163163
fontSize: indeterminateIcon.props.fontSize ?? size,
164164
}),
165-
disableRipple,
165+
// Forward the raw prop so an unset value stays `undefined` and ButtonBase resolves its
166+
// own default — letting a global `MuiButtonBase.defaultProps.disableRipple` apply here.
167+
disableRipple: props.disableRipple,
166168
slots,
167169
slotProps: {
168170
input: mergeSlotProps(

packages/mui-material/src/Checkbox/Checkbox.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,4 +254,34 @@ describe('<Checkbox />', () => {
254254
await ripple.startTouch(checkbox);
255255
expect(checkbox.querySelector('.touch-ripple')).to.equal(null);
256256
});
257+
258+
it('should respect a global disableRipple from MuiButtonBase defaultProps', async () => {
259+
const theme = createTheme({
260+
components: { MuiButtonBase: { defaultProps: { disableRipple: true } } },
261+
});
262+
render(
263+
<ThemeProvider theme={theme}>
264+
<Checkbox TouchRippleProps={{ className: 'touch-ripple' }} />
265+
</ThemeProvider>,
266+
);
267+
268+
const checkbox = screen.getByRole('checkbox').parentElement;
269+
await ripple.startTouch(checkbox);
270+
expect(checkbox.querySelector('.touch-ripple')).to.equal(null);
271+
});
272+
273+
it('should let an explicit disableRipple={false} override a global disableRipple', async () => {
274+
const theme = createTheme({
275+
components: { MuiButtonBase: { defaultProps: { disableRipple: true } } },
276+
});
277+
render(
278+
<ThemeProvider theme={theme}>
279+
<Checkbox disableRipple={false} TouchRippleProps={{ className: 'touch-ripple' }} />
280+
</ThemeProvider>,
281+
);
282+
283+
const checkbox = screen.getByRole('checkbox').parentElement;
284+
await ripple.startTouch(checkbox);
285+
expect(checkbox.querySelector('.touch-ripple')).not.to.equal(null);
286+
});
257287
});

packages/mui-material/src/Radio/Radio.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ const Radio = React.forwardRef(function Radio(inProps, ref) {
195195
disabled,
196196
name,
197197
checked,
198+
// Forward the raw prop so an unset value stays `undefined` and ButtonBase resolves its
199+
// own default — letting a global `MuiButtonBase.defaultProps.disableRipple` apply here.
200+
disableRipple: props.disableRipple,
198201
slots,
199202
slotProps: {
200203
// Do not forward `slotProps.root` again because it's already handled by the `RootSlot` in this file.

packages/mui-material/src/Radio/Radio.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ButtonBase from '@mui/material/ButtonBase';
66
import { ThemeProvider, createTheme } from '@mui/material/styles';
77
import switchBaseClasses from '../internal/switchBaseClasses';
88
import describeConformance from '../../test/describeConformance';
9+
import * as ripple from '../../test/ripple';
910

1011
describe('<Radio />', () => {
1112
const { render } = createRenderer();
@@ -146,4 +147,52 @@ describe('<Radio />', () => {
146147

147148
expect(screen.queryByRole('radio', { name: 'A' })).not.to.equal(null);
148149
});
150+
151+
describe('prop: disableRipple', () => {
152+
it('should have a ripple by default', async () => {
153+
render(<Radio TouchRippleProps={{ className: 'touch-ripple' }} />);
154+
155+
const radio = screen.getByRole('radio').parentElement;
156+
await ripple.startTouch(radio);
157+
expect(radio.querySelector('.touch-ripple')).not.to.equal(null);
158+
});
159+
160+
it('should not have a ripple when disableRipple is set', async () => {
161+
render(<Radio disableRipple TouchRippleProps={{ className: 'touch-ripple' }} />);
162+
163+
const radio = screen.getByRole('radio').parentElement;
164+
await ripple.startTouch(radio);
165+
expect(radio.querySelector('.touch-ripple')).to.equal(null);
166+
});
167+
168+
it('should respect a global disableRipple from MuiButtonBase defaultProps', async () => {
169+
const theme = createTheme({
170+
components: { MuiButtonBase: { defaultProps: { disableRipple: true } } },
171+
});
172+
render(
173+
<ThemeProvider theme={theme}>
174+
<Radio TouchRippleProps={{ className: 'touch-ripple' }} />
175+
</ThemeProvider>,
176+
);
177+
178+
const radio = screen.getByRole('radio').parentElement;
179+
await ripple.startTouch(radio);
180+
expect(radio.querySelector('.touch-ripple')).to.equal(null);
181+
});
182+
183+
it('should let an explicit disableRipple={false} override a global disableRipple', async () => {
184+
const theme = createTheme({
185+
components: { MuiButtonBase: { defaultProps: { disableRipple: true } } },
186+
});
187+
render(
188+
<ThemeProvider theme={theme}>
189+
<Radio disableRipple={false} TouchRippleProps={{ className: 'touch-ripple' }} />
190+
</ThemeProvider>,
191+
);
192+
193+
const radio = screen.getByRole('radio').parentElement;
194+
await ripple.startTouch(radio);
195+
expect(radio.querySelector('.touch-ripple')).not.to.equal(null);
196+
});
197+
});
149198
});

0 commit comments

Comments
 (0)