Skip to content

Commit

Permalink
[material-ui][Checkbox] Add slots and slotProps (#45361)
Browse files Browse the repository at this point in the history
  • Loading branch information
siriwatknp authored Feb 20, 2025
1 parent a6e05ee commit d06c212
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 47 deletions.
38 changes: 30 additions & 8 deletions docs/pages/material-ui/api/checkbox.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
"id": { "type": { "name": "string" } },
"indeterminate": { "type": { "name": "bool" }, "default": "false" },
"indeterminateIcon": { "type": { "name": "node" }, "default": "<IndeterminateCheckBoxIcon />" },
"inputProps": { "type": { "name": "object" } },
"inputRef": { "type": { "name": "custom", "description": "ref" } },
"inputProps": {
"type": { "name": "object" },
"deprecated": true,
"deprecationInfo": "Use <code>slotProps.input</code> instead. This prop will be removed in v7. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
},
"onChange": {
"type": { "name": "func" },
"signature": {
Expand All @@ -34,6 +37,17 @@
},
"default": "'medium'"
},
"slotProps": {
"type": {
"name": "shape",
"description": "{ input?: func<br>&#124;&nbsp;object, root?: func<br>&#124;&nbsp;object }"
},
"default": "{}"
},
"slots": {
"type": { "name": "shape", "description": "{ input?: elementType, root?: elementType }" },
"default": "{}"
},
"sx": {
"type": {
"name": "union",
Expand All @@ -48,6 +62,20 @@
"import Checkbox from '@mui/material/Checkbox';",
"import { Checkbox } from '@mui/material';"
],
"slots": [
{
"name": "root",
"description": "The component that renders the root slot.",
"default": "SwitchBase",
"class": "MuiCheckbox-root"
},
{
"name": "input",
"description": "The component that renders the input slot.",
"default": "SwitchBase's input",
"class": null
}
],
"classes": [
{
"key": "checked",
Expand Down Expand Up @@ -79,12 +107,6 @@
"description": "State class applied to the root element if `indeterminate={true}`.",
"isGlobal": false
},
{
"key": "root",
"className": "MuiCheckbox-root",
"description": "Class name applied to the root element.",
"isGlobal": false
},
{
"key": "sizeMedium",
"className": "MuiCheckbox-sizeMedium",
Expand Down
12 changes: 10 additions & 2 deletions docs/pages/material-ui/api/radio.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,16 @@
"disableRipple": { "type": { "name": "bool" }, "default": "false" },
"icon": { "type": { "name": "node" }, "default": "<RadioButtonIcon />" },
"id": { "type": { "name": "string" } },
"inputProps": { "type": { "name": "object" } },
"inputRef": { "type": { "name": "custom", "description": "ref" } },
"inputProps": {
"type": { "name": "object" },
"deprecated": true,
"deprecationInfo": "Use <code>slotProps.input</code> instead. This prop will be removed in v7. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
},
"inputRef": {
"type": { "name": "custom", "description": "ref" },
"deprecated": true,
"deprecationInfo": "Use <code>slotProps.input.ref</code> instead. This prop will be removed in v7. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
},
"name": { "type": { "name": "string" } },
"onChange": {
"type": { "name": "func" },
Expand Down
12 changes: 10 additions & 2 deletions docs/pages/material-ui/api/switch.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@
},
"icon": { "type": { "name": "node" } },
"id": { "type": { "name": "string" } },
"inputProps": { "type": { "name": "object" } },
"inputRef": { "type": { "name": "custom", "description": "ref" } },
"inputProps": {
"type": { "name": "object" },
"deprecated": true,
"deprecationInfo": "Use <code>slotProps.input</code> instead. This prop will be removed in v7. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
},
"inputRef": {
"type": { "name": "custom", "description": "ref" },
"deprecated": true,
"deprecationInfo": "Use <code>slotProps.input.ref</code> instead. This prop will be removed in v7. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
},
"onChange": {
"type": { "name": "func" },
"signature": {
Expand Down
8 changes: 6 additions & 2 deletions docs/translations/api-docs/checkbox/checkbox.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"inputProps": {
"description": "<a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes\">Attributes</a> applied to the <code>input</code> element."
},
"inputRef": { "description": "Pass a ref to the <code>input</code> element." },
"onChange": {
"description": "Callback fired when the state is changed.",
"typeDescriptions": {
Expand All @@ -36,6 +35,8 @@
"size": {
"description": "The size of the component. <code>small</code> is equivalent to the dense checkbox 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,7 +70,6 @@
"nodeName": "the root element",
"conditions": "<code>indeterminate={true}</code>"
},
"root": { "description": "Class name applied to the root element." },
"sizeMedium": {
"description": "State class applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the root element",
Expand All @@ -80,5 +80,9 @@
"nodeName": "the root element",
"conditions": "<code>size=\"small\"</code>"
}
},
"slotDescriptions": {
"input": "The component that renders the input slot.",
"root": "The component that renders the root slot."
}
}
54 changes: 45 additions & 9 deletions packages/mui-material/src/Checkbox/Checkbox.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,57 @@ 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 { CheckboxClasses } from './checkboxClasses';

export interface CheckboxPropsSizeOverrides {}

export interface CheckboxPropsColorOverrides {}

export interface CheckboxRootSlotPropsOverrides {}

export interface CheckboxInputSlotPropsOverrides {}

export interface CheckboxSlots {
/**
* The component that renders the root slot.
* @default SwitchBase
*/
root: React.ElementType;
/**
* The component that renders the input slot.
* @default SwitchBase's input
*/
input: React.ElementType;
}

export type CheckboxSlotsAndSlotProps = CreateSlotsAndSlotProps<
CheckboxSlots,
{
/**
* Props forwarded to the root slot.
* By default, the avaible props are based on the div element.
*/
root: SlotProps<
React.ElementType<SwitchBaseProps>,
CheckboxRootSlotPropsOverrides,
CheckboxOwnerState
>;
/**
* Props forwarded to the input slot.
* By default, the avaible props are based on the input element.
*/
input: SlotProps<'input', CheckboxInputSlotPropsOverrides, CheckboxOwnerState>;
}
>;

export interface CheckboxProps
extends StandardProps<SwitchBaseProps, 'checkedIcon' | 'color' | 'icon' | 'type'> {
extends StandardProps<
SwitchBaseProps,
'checkedIcon' | 'color' | 'icon' | 'type' | 'slots' | 'slotProps'
>,
CheckboxSlotsAndSlotProps {
/**
* If `true`, the component is checked.
*/
Expand Down Expand Up @@ -66,14 +108,6 @@ export interface CheckboxProps
* @default <IndeterminateCheckBoxIcon />
*/
indeterminateIcon?: React.ReactNode;
/**
* [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element.
*/
inputProps?: SwitchBaseProps['inputProps'];
/**
* Pass a ref to the `input` element.
*/
inputRef?: React.Ref<HTMLInputElement>;
/**
* Callback fired when the state is changed.
*
Expand Down Expand Up @@ -103,6 +137,8 @@ export interface CheckboxProps
value?: SwitchBaseProps['value'];
}

export interface CheckboxOwnerState extends Omit<CheckboxProps, 'slots' | 'slotProps'> {}

/**
*
* Demos:
Expand Down
80 changes: 56 additions & 24 deletions packages/mui-material/src/Checkbox/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
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 { alpha } from '@mui/system/colorManipulator';
import SwitchBase from '../internal/SwitchBase';
Expand All @@ -16,6 +15,8 @@ import { styled } from '../zero-styled';
import memoTheme from '../utils/memoTheme';
import createSimplePaletteValueFilter from '../utils/createSimplePaletteValueFilter';
import { useDefaultProps } from '../DefaultPropsProvider';
import { mergeSlotProps } from '../utils';
import useSlot from '../utils/useSlot';

const useUtilityClasses = (ownerState) => {
const { classes, indeterminate, color, size } = ownerState;
Expand Down Expand Up @@ -122,6 +123,8 @@ const Checkbox = React.forwardRef(function Checkbox(inProps, ref) {
size = 'medium',
disableRipple = false,
className,
slots = {},
slotProps = {},
...other
} = props;

Expand All @@ -138,27 +141,43 @@ const Checkbox = React.forwardRef(function Checkbox(inProps, ref) {

const classes = useUtilityClasses(ownerState);

return (
<CheckboxRoot
type="checkbox"
inputProps={{
'data-indeterminate': indeterminate,
...inputProps,
}}
icon={React.cloneElement(icon, {
const externalInputProps = slotProps.input ?? inputProps;

const [RootSlot, rootSlotProps] = useSlot('root', {
ref,
elementType: CheckboxRoot,
className: clsx(classes.root, className),
shouldForwardComponentProp: true,
externalForwardedProps: {
slots,
slotProps,
...other,
},
ownerState,
additionalProps: {
type: 'checkbox',
icon: React.cloneElement(icon, {
fontSize: icon.props.fontSize ?? size,
})}
checkedIcon={React.cloneElement(indeterminateIcon, {
}),
checkedIcon: React.cloneElement(indeterminateIcon, {
fontSize: indeterminateIcon.props.fontSize ?? size,
})}
ownerState={ownerState}
ref={ref}
className={clsx(classes.root, className)}
disableRipple={disableRipple}
{...other}
classes={classes}
/>
);
}),
disableRipple,
slots,
slotProps: {
input: mergeSlotProps(
typeof externalInputProps === 'function'
? externalInputProps(ownerState)
: externalInputProps,
{
'data-indeterminate': indeterminate,
},
),
},
},
});

return <RootSlot {...rootSlotProps} classes={classes} />;
});

Checkbox.propTypes /* remove-proptypes */ = {
Expand Down Expand Up @@ -231,12 +250,9 @@ Checkbox.propTypes /* remove-proptypes */ = {
indeterminateIcon: PropTypes.node,
/**
* [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element.
* @deprecated Use `slotProps.input` instead. This prop will be removed in v7. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details.
*/
inputProps: PropTypes.object,
/**
* Pass a ref to the `input` element.
*/
inputRef: refType,
/**
* Callback fired when the state is changed.
*
Expand All @@ -258,6 +274,22 @@ Checkbox.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]),
}),
/**
* 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
33 changes: 33 additions & 0 deletions packages/mui-material/src/Checkbox/Checkbox.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react';
import Checkbox from '@mui/material/Checkbox';
import { expectType } from '@mui/types';

// deprecated props
<Checkbox
inputProps={{
'aria-label': 'Checkbox',
onChange: () => {},
}}
inputRef={null}
/>;

<Checkbox
slots={{
root: 'div',
input: 'input',
}}
slotProps={{
root: {
className: 'root',
disableRipple: true,
hidden: true,
},
input: {
ref: (elm) => {
expectType<HTMLInputElement | null, typeof elm>(elm);
},
'aria-label': 'Checkbox',
className: 'input',
},
}}
/>;
Loading

0 comments on commit d06c212

Please # to comment.