Skip to content

Commit

Permalink
[material-ui][SwitchBase] Deprecate inputProps and complete slots, …
Browse files Browse the repository at this point in the history
…slotProps (mui#45076)
  • Loading branch information
siriwatknp committed Feb 18, 2025
1 parent f7cc248 commit a6f1c94
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 36 deletions.
34 changes: 33 additions & 1 deletion packages/mui-material/src/internal/SwitchBase.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,39 @@ import * as React from 'react';
import { InternalStandardProps as StandardProps } from '..';
import { ButtonBaseProps } from '../ButtonBase';
import { SwitchBaseClasses } from './switchBaseClasses';
import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types';

interface SwitchBaseSlots {
/**
* The component that renders the root slot.
* @default ButtonBase
*/
root: React.ElementType;
/**
* The component that renders the input slot.
* @default 'input'
*/
input: React.ElementType;
}

type SwitchBaseSlotsAndSlotProps = CreateSlotsAndSlotProps<
SwitchBaseSlots,
{
/**
* Props forwarded to the root slot.
* By default, the avaible props are based on the [ButtonBase](https://mui.com/material-ui/api/button-base/#props) component.
*/
root: SlotProps<React.ElementType<ButtonBaseProps>, {}, SwitchBaseOwnerState>;
/**
* Props forwarded to the input slot.
*/
input: SlotProps<'input', {}, SwitchBaseOwnerState>;
}
>;

export interface SwitchBaseProps
extends StandardProps<ButtonBaseProps, 'children' | 'onChange' | 'type' | 'value'> {
extends StandardProps<ButtonBaseProps, 'children' | 'onChange' | 'type' | 'value'>,
SwitchBaseSlotsAndSlotProps {
autoFocus?: boolean;
/**
* If `true`, the component is checked.
Expand Down Expand Up @@ -80,6 +110,8 @@ export interface SwitchBaseProps
value?: unknown;
}

export interface SwitchBaseOwnerState extends Omit<SwitchBaseProps, 'slots' | 'slotProps'> {}

declare const SwitchBase: React.JSXElementConstructor<SwitchBaseProps>;

export default SwitchBase;
123 changes: 88 additions & 35 deletions packages/mui-material/src/internal/SwitchBase.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import refType from '@mui/utils/refType';
import composeClasses from '@mui/utils/composeClasses';
import capitalize from '../utils/capitalize';
Expand All @@ -11,6 +10,7 @@ import useControlled from '../utils/useControlled';
import useFormControl from '../FormControl/useFormControl';
import ButtonBase from '../ButtonBase';
import { getSwitchBaseUtilityClass } from './switchBaseClasses';
import useSlot from '../utils/useSlot';

const useUtilityClasses = (ownerState) => {
const { classes, checked, disabled, edge } = ownerState;
Expand Down Expand Up @@ -81,7 +81,6 @@ const SwitchBase = React.forwardRef(function SwitchBase(props, ref) {
autoFocus,
checked: checkedProp,
checkedIcon,
className,
defaultChecked,
disabled: disabledProp,
disableFocusRipple = false,
Expand All @@ -99,6 +98,8 @@ const SwitchBase = React.forwardRef(function SwitchBase(props, ref) {
tabIndex,
type,
value,
slots = {},
slotProps = {},
...other
} = props;
const [checked, setCheckedState] = useControlled({
Expand Down Expand Up @@ -166,41 +167,77 @@ const SwitchBase = React.forwardRef(function SwitchBase(props, ref) {

const classes = useUtilityClasses(ownerState);

const externalForwardedProps = {
slots,
slotProps: {
input: inputProps,
...slotProps,
},
};

const [RootSlot, rootSlotProps] = useSlot('root', {
ref,
elementType: SwitchBaseRoot,
className: classes.root,
shouldForwardComponentProp: true,
externalForwardedProps: {
...externalForwardedProps,
component: 'span',
...other,
},
getSlotProps: (handlers) => ({
...handlers,
onFocus: (event) => {
handlers.onFocus?.(event);
handleFocus(event);
},
onBlur: (event) => {
handlers.onBlur?.(event);
handleBlur(event);
},
}),
ownerState,
additionalProps: {
centerRipple: true,
focusRipple: !disableFocusRipple,
disabled,
role: undefined,
tabIndex: null,
},
});

const [InputSlot, inputSlotProps] = useSlot('input', {
ref: inputRef,
elementType: SwitchBaseInput,
className: classes.input,
externalForwardedProps,
getSlotProps: (handlers) => ({
onChange: (event) => {
handlers.onChange?.(event);
handleInputChange(event);
},
}),
ownerState,
additionalProps: {
autoFocus,
checked: checkedProp,
defaultChecked,
disabled,
id: hasLabelFor ? id : undefined,
name,
readOnly,
required,
tabIndex,
type,
...(type === 'checkbox' && value === undefined ? {} : { value }),
},
});

return (
<SwitchBaseRoot
component="span"
className={clsx(classes.root, className)}
centerRipple
focusRipple={!disableFocusRipple}
disabled={disabled}
tabIndex={null}
role={undefined}
onFocus={handleFocus}
onBlur={handleBlur}
ownerState={ownerState}
ref={ref}
{...other}
>
<SwitchBaseInput
autoFocus={autoFocus}
checked={checkedProp}
defaultChecked={defaultChecked}
className={classes.input}
disabled={disabled}
id={hasLabelFor ? id : undefined}
name={name}
onChange={handleInputChange}
readOnly={readOnly}
ref={inputRef}
required={required}
ownerState={ownerState}
tabIndex={tabIndex}
type={type}
{...(type === 'checkbox' && value === undefined ? {} : { value })}
{...inputProps}
/>
<RootSlot {...rootSlotProps}>
<InputSlot {...inputSlotProps} />
{checked ? checkedIcon : icon}
</SwitchBaseRoot>
</RootSlot>
);
});

Expand Down Expand Up @@ -292,6 +329,22 @@ SwitchBase.propTypes = {
* If `true`, the `input` element is required.
*/
required: PropTypes.bool,
/**
* The props used for each slot inside.
* @default {}
*/
slotProps: PropTypes.shape({
input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
}),
/**
* The components used for each slot inside.
* @default {}
*/
slots: PropTypes.shape({
input: PropTypes.elementType,
root: PropTypes.elementType,
}),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
Expand Down
14 changes: 14 additions & 0 deletions packages/mui-material/src/internal/SwitchBase.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import * as ripple from '../../test/ripple';
describe('<SwitchBase />', () => {
const { render } = createRenderer();

function CustomRoot({ centerRipple, focusRipple, ownerState, ...props }) {
return <div {...props} />;
}

describeConformance(
<SwitchBase checkedIcon="checked" icon="unchecked" type="checkbox" />,
() => ({
Expand All @@ -21,6 +25,15 @@ describe('<SwitchBase />', () => {
refInstanceof: window.HTMLSpanElement,
testComponentPropWith: 'div',
testVariantProps: { disabled: true },
slots: {
root: {
expectedClassName: classes.root,
testWithElement: CustomRoot,
},
input: {
expectedClassName: classes.input,
},
},
skip: ['componentsProp', 'themeDefaultProps', 'themeStyleOverrides', 'themeVariants'],
}),
);
Expand Down Expand Up @@ -260,6 +273,7 @@ describe('<SwitchBase />', () => {

describe('prop: inputProps', () => {
it('should be able to add aria', () => {
// TODO: remove this test in v7 because `inputProps` is deprecated
const { getByRole } = render(
<SwitchBase
icon="unchecked"
Expand Down

0 comments on commit a6f1c94

Please sign in to comment.