Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 46 additions & 30 deletions docs/pages/material-ui/api/switch.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@
},
"default": "'medium'"
},
"slotProps": {
"type": {
"name": "shape",
"description": "{ input?: func<br>&#124;&nbsp;object, root?: func<br>&#124;&nbsp;object, switchBase?: func<br>&#124;&nbsp;object, thumb?: func<br>&#124;&nbsp;object, track?: func<br>&#124;&nbsp;object }"
},
"default": "{}"
},
"slots": {
"type": {
"name": "shape",
"description": "{ input?: elementType, root?: elementType, switchBase?: elementType, thumb?: elementType, track?: elementType }"
},
"default": "{}"
},
"sx": {
"type": {
"name": "union",
Expand All @@ -61,6 +75,38 @@
"import Switch from '@mui/material/Switch';",
"import { Switch } from '@mui/material';"
],
"slots": [
{
"name": "root",
"description": "The component that renders the root slot.",
"default": "'span'",
"class": "MuiSwitch-root"
},
{
"name": "track",
"description": "The component that renders the track slot.",
"default": "'span'",
"class": "MuiSwitch-track"
},
{
"name": "thumb",
"description": "The component that renders the thumb slot.",
"default": "'span'",
"class": "MuiSwitch-thumb"
},
{
"name": "switchBase",
"description": "The component that renders the switchBase slot.",
"default": "SwitchBase",
"class": "MuiSwitch-switchBase"
},
{
"name": "input",
"description": "The component that renders the switchBase's input slot.",
"default": "SwitchBaseInput",
"class": "MuiSwitch-input"
}
],
"classes": [
{
"key": "checked",
Expand Down Expand Up @@ -98,18 +144,6 @@
"description": "Styles applied to the root element if `edge=\"start\"`.",
"isGlobal": false
},
{
"key": "input",
"className": "MuiSwitch-input",
"description": "Styles applied to the internal SwitchBase component's input element.",
"isGlobal": false
},
{
"key": "root",
"className": "MuiSwitch-root",
"description": "Styles applied to the root element.",
"isGlobal": false
},
{
"key": "sizeMedium",
"className": "MuiSwitch-sizeMedium",
Expand All @@ -121,24 +155,6 @@
"className": "MuiSwitch-sizeSmall",
"description": "Styles applied to the root element if `size=\"small\"`.",
"isGlobal": false
},
{
"key": "switchBase",
"className": "MuiSwitch-switchBase",
"description": "Styles applied to the internal `SwitchBase` component's `root` class.",
"isGlobal": false
},
{
"key": "thumb",
"className": "MuiSwitch-thumb",
"description": "Styles used to create the thumb passed to the internal `SwitchBase` component `icon` prop.",
"isGlobal": false
},
{
"key": "track",
"className": "MuiSwitch-track",
"description": "Styles applied to the track element.",
"isGlobal": false
}
],
"spread": true,
Expand Down
24 changes: 10 additions & 14 deletions docs/translations/api-docs/switch/switch.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"size": {
"description": "The size of the component. <code>small</code> is equivalent to the dense switch styling."
},
"slotProps": { "description": "The props used for each slot inside." },
"slots": { "description": "The components used for each slot inside." },
"sx": {
"description": "The system prop that allows defining system overrides as well as additional CSS styles."
},
Expand Down Expand Up @@ -69,11 +71,6 @@
"nodeName": "the root element",
"conditions": "<code>edge=\"start\"</code>"
},
"input": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the internal SwitchBase component&#39;s input element"
},
"root": { "description": "Styles applied to the root element." },
"sizeMedium": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the root element",
Expand All @@ -83,14 +80,13 @@
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the root element",
"conditions": "<code>size=\"small\"</code>"
},
"switchBase": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the internal <code>SwitchBase</code> component&#39;s <code>root</code> class"
},
"thumb": {
"description": "Styles used to create the thumb passed to the internal <code>SwitchBase</code> component <code>icon</code> prop."
},
"track": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the track element" }
}
},
"slotDescriptions": {
"input": "The component that renders the switchBase&#39;s input slot.",
"root": "The component that renders the root slot.",
"switchBase": "The component that renders the switchBase slot.",
"thumb": "The component that renders the thumb slot.",
"track": "The component that renders the track slot."
}
}
75 changes: 74 additions & 1 deletion packages/mui-material/src/Switch/Switch.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,88 @@ import * as React from 'react';
import { SxProps } from '@mui/system';
import { OverridableStringUnion } from '@mui/types';
import { InternalStandardProps as StandardProps, Theme } from '..';
import { CreateSlotsAndSlotProps, SlotProps } from '../utils/types';
import { SwitchBaseProps } from '../internal/SwitchBase';
import { SwitchClasses } from './switchClasses';

export interface SwitchPropsSizeOverrides {}

export interface SwitchPropsColorOverrides {}

export interface SwitchRootSlotPropsOverrides {}
export interface SwitchTrackSlotPropsOverrides {}
export interface SwitchThumbSlotPropsOverrides {}
export interface SwitchSwitchBaseSlotPropsOverrides {}
export interface SwitchInputSlotPropsOverrides {}

export interface SwitchSlots {
/**
* The component that renders the root slot.
* @default 'span'
*/
root: React.ElementType;
/**
* The component that renders the track slot.
* @default 'span'
*/
track: React.ElementType;
/**
* The component that renders the thumb slot.
* @default 'span'
*/
thumb: React.ElementType;
/**
* The component that renders the switchBase slot.
* @default SwitchBase
*/
switchBase: React.ElementType;
/**
* The component that renders the switchBase's input slot.
* @default SwitchBaseInput
*/
input: React.ElementType;
}

export type SwitchSlotsAndSlotProps = CreateSlotsAndSlotProps<
SwitchSlots,
{
/**
* Props forwarded to the root slot.
* By default, the avaible props are based on the span element.
*/
root: SlotProps<'span', SwitchRootSlotPropsOverrides, SwitchOwnerState>;
/**
* Props forwarded to the track slot.
* By default, the avaible props are based on the span element.
*/
track: SlotProps<'span', SwitchTrackSlotPropsOverrides, SwitchOwnerState>;
/**
* Props forwarded to the thumb slot.
* By default, the avaible props are based on the span element.
*/
thumb: SlotProps<'span', SwitchThumbSlotPropsOverrides, SwitchOwnerState>;
/**
* Props forwarded to the switchBase slot.
* By default, the avaible props are based on the internal SwitchBase component.
*/
switchBase: SlotProps<
React.ElementType<SwitchBaseProps>,
SwitchSwitchBaseSlotPropsOverrides,
SwitchOwnerState
>;
/**
* Props forwarded to the input slot.
* By default, the avaible props are based on the input element.
*/
input: SlotProps<'input', SwitchInputSlotPropsOverrides, SwitchOwnerState>;
}
>;

export interface SwitchOwnerState extends Omit<SwitchProps, 'slots' | 'slotProps'> {}

export interface SwitchProps
extends StandardProps<SwitchBaseProps, 'checkedIcon' | 'color' | 'icon'> {
extends StandardProps<SwitchBaseProps, 'checkedIcon' | 'color' | 'icon' | 'slots' | 'slotProps'>,
SwitchSlotsAndSlotProps {
/**
* The icon to display when the component is checked.
*/
Expand Down
88 changes: 83 additions & 5 deletions packages/mui-material/src/Switch/Switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { styled } from '../zero-styled';
import memoTheme from '../utils/memoTheme';
import { useDefaultProps } from '../DefaultPropsProvider';
import switchClasses, { getSwitchUtilityClass } from './switchClasses';
import useSlot from '../utils/useSlot';

const useUtilityClasses = (ownerState) => {
const { classes, edge, size, color, checked, disabled } = ownerState;
Expand Down Expand Up @@ -222,7 +223,16 @@ const SwitchThumb = styled('span', {

const Switch = React.forwardRef(function Switch(inProps, ref) {
const props = useDefaultProps({ props: inProps, name: 'MuiSwitch' });
const { className, color = 'primary', edge = false, size = 'medium', sx, ...other } = props;
const {
className,
color = 'primary',
edge = false,
size = 'medium',
sx,
slots = {},
slotProps = {},
...other
} = props;

const ownerState = {
...props,
Expand All @@ -232,10 +242,40 @@ const Switch = React.forwardRef(function Switch(inProps, ref) {
};

const classes = useUtilityClasses(ownerState);
const icon = <SwitchThumb className={classes.thumb} ownerState={ownerState} />;

const externalForwardedProps = {
slots,
slotProps,
};

const [RootSlot, rootSlotProps] = useSlot('root', {
className: clsx(classes.root, className),
elementType: SwitchRoot,
externalForwardedProps,
ownerState,
additionalProps: {
sx,
},
});

const [ThumbSlot, thumbSlotProps] = useSlot('thumb', {
className: classes.thumb,
elementType: SwitchThumb,
externalForwardedProps,
ownerState,
});

const icon = <ThumbSlot {...thumbSlotProps} />;

const [TrackSlot, trackSlotProps] = useSlot('track', {
className: classes.track,
elementType: SwitchTrack,
externalForwardedProps,
ownerState,
});

return (
<SwitchRoot className={clsx(classes.root, className)} sx={sx} ownerState={ownerState}>
<RootSlot {...rootSlotProps}>
<SwitchSwitchBase
type="checkbox"
icon={icon}
Expand All @@ -247,9 +287,25 @@ const Switch = React.forwardRef(function Switch(inProps, ref) {
...classes,
root: classes.switchBase,
}}
slots={{
...(slots.switchBase && { root: slots.switchBase }),
...(slots.input && { input: slots.input }),
}}
slotProps={{
...(slotProps.switchBase && {
root:
typeof slotProps.switchBase === 'function'
? slotProps.switchBase(ownerState)
: slotProps.switchBase,
}),
...(slotProps.input && {
input:
typeof slotProps.input === 'function' ? slotProps.input(ownerState) : slotProps.input,
}),
}}
/>
<SwitchTrack className={classes.track} ownerState={ownerState} />
</SwitchRoot>
<TrackSlot {...trackSlotProps} />
</RootSlot>
);
});

Expand Down Expand Up @@ -345,6 +401,28 @@ Switch.propTypes /* remove-proptypes */ = {
PropTypes.oneOf(['medium', 'small']),
PropTypes.string,
]),
/**
* 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]),
switchBase: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
thumb: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
track: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
}),
/**
* The components used for each slot inside.
* @default {}
*/
slots: PropTypes.shape({
input: PropTypes.elementType,
root: PropTypes.elementType,
switchBase: PropTypes.elementType,
thumb: PropTypes.elementType,
track: PropTypes.elementType,
}),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
Expand Down
22 changes: 22 additions & 0 deletions packages/mui-material/src/Switch/Switch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import describeConformance from '../../test/describeConformance';
describe('<Switch />', () => {
const { render } = createRenderer();

function CustomSwitchBase({ centerRipple, focusRipple, ownerState, ...props }) {
return <div data-testid="custom" {...props} />;
}

describeConformance(<Switch />, () => ({
classes,
render,
Expand All @@ -16,6 +20,24 @@ describe('<Switch />', () => {
{ slotName: 'track', slotClassName: classes.track },
{ slotName: 'input', slotClassName: classes.input },
],
slots: {
root: {
expectedClassName: classes.root,
},
track: {
expectedClassName: classes.track,
},
thumb: {
expectedClassName: classes.thumb,
},
switchBase: {
expectedClassName: classes.switchBase,
testWithElement: CustomSwitchBase,
},
input: {
expectedClassName: classes.input,
},
},
refInstanceof: window.HTMLSpanElement,
skip: [
'componentProp',
Expand Down