Skip to content

Commit

Permalink
[IconButton] Migrate to emotion (#24542)
Browse files Browse the repository at this point in the history
  • Loading branch information
queengooborg authored Jan 23, 2021
1 parent 528d3e5 commit acb2e7d
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 90 deletions.
5 changes: 3 additions & 2 deletions docs/pages/api-docs/icon-button.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"size": {
"type": { "name": "enum", "description": "'medium'<br>&#124;&nbsp;'small'" },
"default": "'medium'"
}
},
"sx": { "type": { "name": "object" } }
},
"name": "IconButton",
"styles": {
Expand All @@ -45,6 +46,6 @@
"filename": "/packages/material-ui/src/IconButton/IconButton.js",
"inheritance": { "component": "ButtonBase", "pathname": "/api/button-base/" },
"demos": "<ul><li><a href=\"/components/buttons/\">Buttons</a></li></ul>",
"styledComponent": false,
"styledComponent": true,
"cssComponent": false
}
3 changes: 2 additions & 1 deletion docs/translations/api-docs/icon-button/icon-button.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"disableFocusRipple": "If <code>true</code>, the keyboard focus ripple is disabled.",
"disableRipple": "If <code>true</code>, the ripple effect is disabled.<br>⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure to highlight the element by applying separate styles with the <code>.Mui-focusedVisible</code> class.",
"edge": "If given, uses a negative margin to counteract the padding on one side (this is often helpful for aligning the left or right side of the icon with content above or below, without ruining the border size and shape).",
"size": "The size of the component. <code>small</code> is equivalent to the dense button styling."
"size": "The size of the component. <code>small</code> is equivalent to the dense button styling.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/basics/#the-sx-prop\">`sx` page</a> for more details."
},
"classDescriptions": {
"root": { "description": "Styles applied to the root element." },
Expand Down
2 changes: 1 addition & 1 deletion framer/scripts/framerConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export const componentSettings = {
template: 'icon.txt',
},
IconButton: {
ignoredProps: ['children', 'edge', 'disableRipple', 'disableFocusRipple'],
ignoredProps: ['children', 'edge', 'disableRipple', 'disableFocusRipple', 'sx'],
propValues: {
icon: "'favorite'",
iconTheme: 'Filled',
Expand Down
8 changes: 7 additions & 1 deletion packages/material-ui/src/IconButton/IconButton.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { PropTypes } from '..';
import * as React from 'react';
import { SxProps } from '@material-ui/system';
import { PropTypes, Theme } from '..';
import { ExtendButtonBase, ExtendButtonBaseTypeMap } from '../ButtonBase';
import { OverrideProps } from '../OverridableComponent';

Expand Down Expand Up @@ -63,6 +65,10 @@ export type IconButtonTypeMap<
* @default 'medium'
*/
size?: 'small' | 'medium';
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
};
defaultComponent: D;
}>;
Expand Down
202 changes: 126 additions & 76 deletions packages/material-ui/src/IconButton/IconButton.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,54 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { chainPropTypes } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { chainPropTypes, deepmerge } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import experimentalStyled from '../styles/experimentalStyled';
import useThemeProps from '../styles/useThemeProps';
import { alpha } from '../styles/colorManipulator';
import ButtonBase from '../ButtonBase';
import capitalize from '../utils/capitalize';
import iconButtonClasses, { getIconButtonUtilityClass } from './iconButtonClasses';

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
const overridesResolver = (props, styles) => {
const { styleProps } = props;

return deepmerge(styles.root || {}, {
...(styleProps.color !== 'default' && styles[`color${capitalize(styleProps.color)}`]),
...(styleProps.edge && styles[`edge${capitalize(styleProps.edge)}`]),
...styles[`size${capitalize(styleProps.size)}`],
[`& .${iconButtonClasses.label}`]: styles.label,
});
};

const useUtilityClasses = (styleProps) => {
const { classes, disabled, color, edge, size } = styleProps;

const slots = {
root: [
'root',
disabled && 'disabled',
color !== 'default' && `color${capitalize(color)}`,
edge && `edge${capitalize(edge)}`,
`size${capitalize(size)}`,
],
label: ['label'],
};

return composeClasses(slots, getIconButtonUtilityClass, classes);
};

const IconButtonRoot = experimentalStyled(
ButtonBase,
{},
{
name: 'MuiIconButton',
slot: 'Root',
overridesResolver,
},
)(
({ theme, styleProps }) => ({
/* Styles applied to the root element. */
textAlign: 'center',
flex: '0 0 auto',
fontSize: theme.typography.pxToRem(24),
Expand All @@ -27,76 +66,79 @@ export const styles = (theme) => ({
backgroundColor: 'transparent',
},
},
'&$disabled': {
backgroundColor: 'transparent',
color: theme.palette.action.disabled,
},
},
/* Styles applied to the root element if `edge="start"`. */
edgeStart: {
marginLeft: -12,
'$sizeSmall&': {
marginLeft: -3,
},
},
/* Styles applied to the root element if `edge="end"`. */
edgeEnd: {
marginRight: -12,
'$sizeSmall&': {
marginRight: -3,
},
},
/* Styles applied to the root element if `color="inherit"`. */
colorInherit: {
color: 'inherit',
},
/* Styles applied to the root element if `color="primary"`. */
colorPrimary: {
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',
/* Styles applied to the root element if `edge="start"`. */
...(styleProps.edge === 'start' && {
marginLeft: styleProps.size === 'small' ? -3 : -12,
}),
/* Styles applied to the root element if `edge="end"`. */
...(styleProps.edge === 'end' && {
marginRight: styleProps.size === 'small' ? -3 : -12,
}),
}),
({ theme, styleProps }) => ({
/* Styles applied to the root element if `color="inherit"`. */
...(styleProps.color === 'inherit' && {
color: 'inherit',
}),
/* Styles applied to the root element if `color="primary"`. */
...(styleProps.color === 'primary' && {
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',
},
},
},
},
/* Styles applied to the root element if `color="secondary"`. */
colorSecondary: {
color: theme.palette.secondary.main,
'&:hover': {
backgroundColor: alpha(theme.palette.secondary.main, theme.palette.action.hoverOpacity),
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent',
}),
/* Styles applied to the root element if `color="secondary"`. */
...(styleProps.color === 'secondary' && {
color: theme.palette.secondary.main,
'&:hover': {
backgroundColor: alpha(theme.palette.secondary.main, theme.palette.action.hoverOpacity),
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
backgroundColor: 'transparent',
},
},
}),
/* Styles applied to the root element if `size="small"`. */
...(styleProps.size === 'small' && {
padding: 3,
fontSize: theme.typography.pxToRem(18),
}),
/* Styles applied to the root element if `disabled={true}`. */
[`&.${iconButtonClasses.disabled}`]: {
backgroundColor: 'transparent',
color: theme.palette.action.disabled,
},
}),
);

const IconButtonLabel = experimentalStyled(
'span',
{},
{
name: 'MuiIconButton',
slot: 'Label',
},
/* Pseudo-class applied to the root element if `disabled={true}`. */
disabled: {},
/* Styles applied to the root element if `size="small"`. */
sizeSmall: {
padding: 3,
fontSize: theme.typography.pxToRem(18),
},
)({
/* Styles applied to the children container element. */
label: {
width: '100%',
display: 'flex',
alignItems: 'inherit',
justifyContent: 'inherit',
},
width: '100%',
display: 'flex',
alignItems: 'inherit',
justifyContent: 'inherit',
});

/**
* Refer to the [Icons](/components/icons/) section of the documentation
* regarding the available icon options.
*/
const IconButton = React.forwardRef(function IconButton(props, ref) {
const IconButton = React.forwardRef(function IconButton(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiIconButton' });
const {
edge = false,
children,
classes,
className,
color = 'default',
disabled = false,
Expand All @@ -105,27 +147,31 @@ const IconButton = React.forwardRef(function IconButton(props, ref) {
...other
} = props;

const styleProps = {
...props,
edge,
color,
disabled,
disableFocusRipple,
size,
};

const classes = useUtilityClasses(styleProps);

return (
<ButtonBase
className={clsx(
classes.root,
{
[classes[`color${capitalize(color)}`]]: color !== 'default',
[classes.disabled]: disabled,
[classes[`size${capitalize(size)}`]]: size !== 'medium',
[classes.edgeStart]: edge === 'start',
[classes.edgeEnd]: edge === 'end',
},
className,
)}
<IconButtonRoot
className={clsx(classes.root, className)}
centerRipple
focusRipple={!disableFocusRipple}
disabled={disabled}
ref={ref}
styleProps={styleProps}
{...other}
>
<span className={classes.label}>{children}</span>
</ButtonBase>
<IconButtonLabel className={classes.label} styleProps={styleProps}>
{children}
</IconButtonLabel>
</IconButtonRoot>
);
});

Expand Down Expand Up @@ -199,6 +245,10 @@ IconButton.propTypes = {
* @default 'medium'
*/
size: PropTypes.oneOf(['medium', 'small']),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
};

export default withStyles(styles, { name: 'MuiIconButton' })(IconButton);
export default IconButton;
17 changes: 8 additions & 9 deletions packages/material-ui/src/IconButton/IconButton.test.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import * as React from 'react';
import { expect } from 'chai';
import PropTypes from 'prop-types';
import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils';
import { createMount, createClientRender, describeConformanceV5 } from 'test/utils';
import Icon from '../Icon';
import ButtonBase from '../ButtonBase';
import IconButton from './IconButton';
import classes from './iconButtonClasses';

describe('<IconButton />', () => {
let classes;
const mount = createMount();
const render = createClientRender({ strict: false });

before(() => {
classes = getClasses(<IconButton />);
});

describeConformance(<IconButton>book</IconButton>, () => ({
describeConformanceV5(<IconButton>book</IconButton>, () => ({
classes,
inheritComponent: ButtonBase,
mount,
refInstanceof: window.HTMLButtonElement,
skip: ['componentProp'],
muiName: 'MuiIconButton',
testVariantProps: { edge: 'end', disabled: true },
testDeepOverrides: { slotName: 'label', slotClassName: classes.label },
skip: ['componentProp', 'componentsProp'],
}));

it('should render an inner label span (bloody safari)', () => {
Expand Down Expand Up @@ -108,7 +107,7 @@ describe('<IconButton />', () => {
it('should raise a warning about onClick in children because of Firefox', () => {
expect(() => {
PropTypes.checkPropTypes(
IconButton.Naked.propTypes,
IconButton.propTypes,
{ classes: {}, children: <svg onClick={() => {}} /> },
'prop',
'MockedName',
Expand Down
18 changes: 18 additions & 0 deletions packages/material-ui/src/IconButton/iconButtonClasses.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface IconButtonClasses {
root: string;
disabled: string;
colorInherit: string;
colorPrimary: string;
colorSecondary: string;
edgeStart: string;
edgeEnd: string;
sizeSmall: string;
sizeMedium: string;
label: string;
}

declare const iconButtonClasses: IconButtonClasses;

export function getIconButtonUtilityClass(slot: string): string;

export default iconButtonClasses;
20 changes: 20 additions & 0 deletions packages/material-ui/src/IconButton/iconButtonClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled';

export function getIconButtonUtilityClass(slot) {
return generateUtilityClass('MuiIconButton', slot);
}

const iconButtonClasses = generateUtilityClasses('MuiIconButton', [
'root',
'disabled',
'colorInherit',
'colorPrimary',
'colorSecondary',
'edgeStart',
'edgeEnd',
'sizeSmall',
'sizeMedium',
'label',
]);

export default iconButtonClasses;
3 changes: 3 additions & 0 deletions packages/material-ui/src/IconButton/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { default } from './IconButton';
export * from './IconButton';

export { default as iconButtonClasses } from './iconButtonClasses';
export * from './iconButtonClasses';
Loading

0 comments on commit acb2e7d

Please sign in to comment.