diff --git a/docs/pages/api-docs/radio.json b/docs/pages/api-docs/radio.json index c7499080bd807c..24bb15c5606d7e 100644 --- a/docs/pages/api-docs/radio.json +++ b/docs/pages/api-docs/radio.json @@ -23,6 +23,7 @@ "type": { "name": "enum", "description": "'medium'
| 'small'" }, "default": "'medium'" }, + "sx": { "type": { "name": "object" } }, "value": { "type": { "name": "any" } } }, "name": "Radio", @@ -36,6 +37,6 @@ "filename": "/packages/material-ui/src/Radio/Radio.js", "inheritance": { "component": "IconButton", "pathname": "/api/icon-button/" }, "demos": "", - "styledComponent": false, + "styledComponent": true, "cssComponent": false } diff --git a/docs/src/pages/components/radio-buttons/RadioButtons.js b/docs/src/pages/components/radio-buttons/RadioButtons.js index 5973ff989332ca..650a2ac80c2c6b 100644 --- a/docs/src/pages/components/radio-buttons/RadioButtons.js +++ b/docs/src/pages/components/radio-buttons/RadioButtons.js @@ -1,17 +1,14 @@ import * as React from 'react'; -import { withStyles } from '@material-ui/core/styles'; +import { experimentalStyled as styled } from '@material-ui/core/styles'; import { green } from '@material-ui/core/colors'; import Radio from '@material-ui/core/Radio'; -const GreenRadio = withStyles({ - root: { - color: green[400], - '&$checked': { - color: green[600], - }, +const GreenRadio = styled(Radio)({ + color: green[400], + '&.Mui-checked': { + color: green[600], }, - checked: {}, -})((props) => ); +}); export default function RadioButtons() { const [selectedValue, setSelectedValue] = React.useState('a'); diff --git a/docs/src/pages/components/radio-buttons/RadioButtons.tsx b/docs/src/pages/components/radio-buttons/RadioButtons.tsx index a769efdf3d809f..a337bbdcdd0b61 100644 --- a/docs/src/pages/components/radio-buttons/RadioButtons.tsx +++ b/docs/src/pages/components/radio-buttons/RadioButtons.tsx @@ -1,17 +1,14 @@ import * as React from 'react'; -import { withStyles } from '@material-ui/core/styles'; +import { experimentalStyled as styled } from '@material-ui/core/styles'; import { green } from '@material-ui/core/colors'; -import Radio, { RadioProps } from '@material-ui/core/Radio'; +import Radio from '@material-ui/core/Radio'; -const GreenRadio = withStyles({ - root: { - color: green[400], - '&$checked': { - color: green[600], - }, +const GreenRadio = styled(Radio)({ + color: green[400], + '&.Mui-checked': { + color: green[600], }, - checked: {}, -})((props: RadioProps) => ); +}); export default function RadioButtons() { const [selectedValue, setSelectedValue] = React.useState('a'); diff --git a/docs/translations/api-docs/radio/radio.json b/docs/translations/api-docs/radio/radio.json index 97f080c3fb2405..790eb163168949 100644 --- a/docs/translations/api-docs/radio/radio.json +++ b/docs/translations/api-docs/radio/radio.json @@ -15,6 +15,7 @@ "onChange": "Callback fired when the state is changed.

Signature:
function(event: object) => void
event: The event source of the callback. You can pull out the new value by accessing event.target.value (string). You can pull out the new checked state by accessing event.target.checked (boolean).", "required": "If true, the input element is required.", "size": "The size of the component. small is equivalent to the dense radio styling.", + "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.", "value": "The value of the component. The DOM API casts this to a string." }, "classDescriptions": { diff --git a/framer/scripts/framerConfig.js b/framer/scripts/framerConfig.js index 70abc26ce22bda..1ac67631276411 100644 --- a/framer/scripts/framerConfig.js +++ b/framer/scripts/framerConfig.js @@ -249,7 +249,7 @@ export const componentSettings = { template: 'paper.txt', }, Radio: { - ignoredProps: ['checked', 'checkedIcon', 'icon', 'onChange', 'required', 'type', 'value'], + ignoredProps: ['checked', 'checkedIcon', 'icon', 'onChange', 'required', 'sx', 'type', 'value'], propValues: { label: "'Radio'", width: "'100%'", diff --git a/packages/material-ui/src/Radio/Radio.d.ts b/packages/material-ui/src/Radio/Radio.d.ts index 0ae2a583dd00d8..cbceadd0179f48 100644 --- a/packages/material-ui/src/Radio/Radio.d.ts +++ b/packages/material-ui/src/Radio/Radio.d.ts @@ -1,5 +1,6 @@ import * as React from 'react'; -import { InternalStandardProps as StandardProps } from '..'; +import { SxProps } from '@material-ui/system'; +import { InternalStandardProps as StandardProps, Theme } from '..'; import { SwitchBaseProps } from '../internal/SwitchBase'; export interface RadioProps @@ -42,6 +43,10 @@ export interface RadioProps * @default 'medium' */ size?: 'small' | 'medium'; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; } export type RadioClassKey = keyof NonNullable; diff --git a/packages/material-ui/src/Radio/Radio.js b/packages/material-ui/src/Radio/Radio.js index 39c6ccf93aa580..54c99379b15a85 100644 --- a/packages/material-ui/src/Radio/Radio.js +++ b/packages/material-ui/src/Radio/Radio.js @@ -1,71 +1,88 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { refType } from '@material-ui/utils'; +import { deepmerge, refType } from '@material-ui/utils'; +import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled'; import SwitchBase from '../internal/SwitchBase'; +import useThemeProps from '../styles/useThemeProps'; import RadioButtonIcon from './RadioButtonIcon'; import { alpha } from '../styles/colorManipulator'; import capitalize from '../utils/capitalize'; import createChainedFunction from '../utils/createChainedFunction'; -import withStyles from '../styles/withStyles'; import useRadioGroup from '../RadioGroup/useRadioGroup'; +import { getRadioUtilityClass } from './radioClasses'; +import experimentalStyled, { shouldForwardProp } from '../styles/experimentalStyled'; -export const styles = (theme) => ({ - /* Styles applied to the root element. */ - root: { - color: theme.palette.text.secondary, - }, - /* Pseudo-class applied to the root element if `checked={true}`. */ - checked: {}, - /* Pseudo-class applied to the root element if `disabled={true}`. */ - disabled: {}, - /* Styles applied to the root element if `color="primary"`. */ - colorPrimary: { - '&$checked': { - color: theme.palette.primary.main, - '&:hover': { - backgroundColor: alpha(theme.palette.primary.main, theme.palette.action.hoverOpacity), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - }, - '&$disabled': { - color: theme.palette.action.disabled, - }, +const overridesResolver = (props, styles) => { + const { styleProps } = props; + + return deepmerge(styles.root || {}, styles[`color${capitalize(styleProps.color)}`]); +}; + +const useUtilityClasses = (styleProps) => { + const { classes, color } = styleProps; + + const slots = { + root: ['root', `color${capitalize(color)}`], + }; + + return { + ...classes, + ...composeClasses(slots, getRadioUtilityClass, classes), + }; +}; + +const RadioRoot = experimentalStyled( + SwitchBase, + { shouldForwardProp: (prop) => shouldForwardProp(prop) || prop === 'classes' }, + { + name: 'MuiRadio', + slot: 'Root', + overridesResolver, }, - /* Styles applied to the root element if `color="secondary"`. */ - colorSecondary: { - '&$checked': { - color: theme.palette.secondary.main, +)(({ theme, styleProps }) => ({ + /* Styles applied to the root element. */ + color: theme.palette.text.secondary, + /* Styles applied to the root element unless `color="default"`. */ + ...(styleProps.color !== 'default' && { + '&.Mui-checked': { + color: theme.palette[styleProps.color].main, '&:hover': { - backgroundColor: alpha(theme.palette.secondary.main, theme.palette.action.hoverOpacity), + backgroundColor: alpha( + theme.palette[styleProps.color].main, + theme.palette.action.hoverOpacity, + ), // Reset on touch devices, it doesn't add specificity '@media (hover: none)': { backgroundColor: 'transparent', }, }, }, - '&$disabled': { - color: theme.palette.action.disabled, - }, + }), + '&.Mui-disabled': { + color: theme.palette.action.disabled, }, -}); +})); const defaultCheckedIcon = ; const defaultIcon = ; -const Radio = React.forwardRef(function Radio(props, ref) { +const Radio = React.forwardRef(function Radio(inProps, ref) { + const props = useThemeProps({ props: inProps, name: 'MuiRadio' }); const { checked: checkedProp, - classes, color = 'secondary', name: nameProp, onChange: onChangeProp, size = 'medium', ...other } = props; + const styleProps = { + ...props, + color, + size, + }; + + const classes = useUtilityClasses(styleProps); const radioGroup = useRadioGroup(); let checked = checkedProp; @@ -82,18 +99,15 @@ const Radio = React.forwardRef(function Radio(props, ref) { } return ( - ', () => { const render = createClientRender(); - let classes; const mount = createMount(); - before(() => { - classes = getClasses(); - }); - - describeConformance(, () => ({ + describeConformanceV5(, () => ({ classes, inheritComponent: IconButton, + render, mount, + muiName: 'MuiRadio', + testVariantProps: { color: 'primary' }, refInstanceof: window.HTMLSpanElement, - skip: ['componentProp'], + skip: ['componentProp', 'componentsProp'], })); describe('styleSheet', () => { diff --git a/packages/material-ui/src/Radio/index.d.ts b/packages/material-ui/src/Radio/index.d.ts index 9aef9464bf6c10..e3e9d1bea1a73e 100644 --- a/packages/material-ui/src/Radio/index.d.ts +++ b/packages/material-ui/src/Radio/index.d.ts @@ -1,2 +1,5 @@ export { default } from './Radio'; export * from './Radio'; + +export { default as radioClasses } from './radioClasses'; +export * from './radioClasses'; diff --git a/packages/material-ui/src/Radio/index.js b/packages/material-ui/src/Radio/index.js index 61c95671a551bb..659d29e9fd37fb 100644 --- a/packages/material-ui/src/Radio/index.js +++ b/packages/material-ui/src/Radio/index.js @@ -1 +1,4 @@ export { default } from './Radio'; + +export { default as radioClasses } from './radioClasses'; +export * from './radioClasses'; diff --git a/packages/material-ui/src/Radio/radioClasses.d.ts b/packages/material-ui/src/Radio/radioClasses.d.ts new file mode 100644 index 00000000000000..f315a60ead62f6 --- /dev/null +++ b/packages/material-ui/src/Radio/radioClasses.d.ts @@ -0,0 +1,9 @@ +import { RadioClassKey } from './Radio'; + +export type RadioClasses = Record; + +declare const radioClasses: RadioClasses; + +export function getRadioUtilityClass(slot: string): string; + +export default radioClasses; diff --git a/packages/material-ui/src/Radio/radioClasses.js b/packages/material-ui/src/Radio/radioClasses.js new file mode 100644 index 00000000000000..c8ea3b7ca086fb --- /dev/null +++ b/packages/material-ui/src/Radio/radioClasses.js @@ -0,0 +1,15 @@ +import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled'; + +export function getRadioUtilityClass(slot) { + return generateUtilityClass('MuiRadio', slot); +} + +const radioClasses = generateUtilityClasses('MuiRadio', [ + 'root', + 'checked', + 'disabled', + 'colorPrimary', + 'colorSecondary', +]); + +export default radioClasses;