Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ToggleButton] Migrate ToggleButtonGroup to emotion #24878

Merged
3 changes: 2 additions & 1 deletion docs/pages/api-docs/toggle-button-group.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
},
"default": "'medium'"
},
"sx": { "type": { "name": "object" } },
"value": { "type": { "name": "any" } }
},
"name": "ToggleButtonGroup",
Expand All @@ -28,6 +29,6 @@
"filename": "/packages/material-ui/src/ToggleButtonGroup/ToggleButtonGroup.js",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/toggle-button/\">Toggle Button</a></li></ul>",
"styledComponent": false,
"styledComponent": true,
"cssComponent": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"onChange": "Callback fired when the value changes.<br><br><strong>Signature:</strong><br><code>function(event: object, value: any) =&gt; void</code><br><em>event:</em> The event source of the callback.<br><em>value:</em> of the selected buttons. When <code>exclusive</code> is true this is a single value; when false an array of selected values. If no value is selected and <code>exclusive</code> is true the value is null; when false an empty array.",
"orientation": "The component orientation (layout flow direction).",
"size": "The size of the component.",
"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.",
"value": "The currently selected value within the group or an array of selected values when <code>exclusive</code> is false.<br>The value must have reference equality with the option in order to be selected."
},
"classDescriptions": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as React from 'react';
import { SxProps } from '@material-ui/system';
import { InternalStandardProps as StandardProps } from '..';
import { Theme } from '../styles';

export interface ToggleButtonGroupProps
extends StandardProps<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'children'> {
Expand Down Expand Up @@ -46,6 +48,10 @@ export interface ToggleButtonGroupProps
* @default 'medium'
*/
size?: 'small' | 'medium' | 'large';
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
/**
* The currently selected value within the group or an array of selected
* values when `exclusive` is false.
Expand Down
154 changes: 94 additions & 60 deletions packages/material-ui/src/ToggleButtonGroup/ToggleButtonGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,99 @@ import * as React from 'react';
import { isFragment } from 'react-is';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { withStyles } from '../styles';
import { 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 { capitalize } from '../utils';
import isValueSelected from './isValueSelected';
import toggleButtonGroupClasses, {
getToggleButtonGroupUtilityClass,
} from './toggleButtonGroupClasses';

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
display: 'inline-flex',
borderRadius: theme.shape.borderRadius,
const overridesResolver = (props, styles) => {
const { styleProps } = props;

return deepmerge(styles.root || {}, {
...(styleProps.orientation === 'vertical' && styles.vertical),
[`& .${toggleButtonGroupClasses.grouped}`]: {
...styles.grouped,
...styles[`grouped${capitalize(styleProps.orientation)}`],
},
});
};

const useUtilityClasses = (styleProps) => {
const { classes, orientation } = styleProps;

const slots = {
root: ['root', orientation === 'vertical' && 'vertical'],
grouped: ['grouped', `grouped${capitalize(orientation)}`],
};

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

const ToggleButtonGroupRoot = experimentalStyled(
'div',
{},
{
name: 'MuiToggleButtonGroup',
slot: 'Root',
overridesResolver,
},
)(({ styleProps, theme }) => ({
/* Styles applied to the root element. */
display: 'inline-flex',
borderRadius: theme.shape.borderRadius,
/* Styles applied to the root element if `orientation="vertical"`. */
vertical: {
...(styleProps.orientation === 'vertical' && {
flexDirection: 'column',
},
}),
/* Styles applied to the children. */
grouped: {},
/* Styles applied to the children if `orientation="horizontal"`. */
groupedHorizontal: {
'&:not(:first-child)': {
marginLeft: -1,
borderLeft: '1px solid transparent',
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
},
'&:not(:last-child)': {
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
},
'&.Mui-selected + &.Mui-selected': {
borderLeft: 0,
marginLeft: 0,
},
},
/* Styles applied to the children if `orientation="vertical"`. */
groupedVertical: {
'&:not(:first-child)': {
marginTop: -1,
borderTop: '1px solid transparent',
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
},
'&:not(:last-child)': {
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
},
'&.Mui-selected + &.Mui-selected': {
borderTop: 0,
marginTop: 0,
},
[`& .${toggleButtonGroupClasses.grouped}`]: {
/* Styles applied to the children if `orientation="horizontal"`. */
...(styleProps.orientation === 'horizontal'
? {
'&:not(:first-of-type)': {
marginLeft: -1,
borderLeft: '1px solid transparent',
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
},
'&:not(:last-of-type)': {
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
},
'&.Mui-selected + &.Mui-selected': {
borderLeft: 0,
marginLeft: 0,
},
}
: {
/* Styles applied to the children if `orientation="vertical"`. */
'&:not(:first-of-type)': {
marginTop: -1,
borderTop: '1px solid transparent',
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
},
'&:not(:last-of-type)': {
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
},
'&.Mui-selected + &.Mui-selected': {
borderTop: 0,
marginTop: 0,
},
}),
},
});
}));

const ToggleButtonGroup = React.forwardRef(function ToggleButtonGroup(props, ref) {
const ToggleButtonGroup = React.forwardRef(function ToggleButtonGroup(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiToggleButtonGroup' });
const {
children,
classes,
className,
exclusive = false,
onChange,
Expand All @@ -66,6 +103,8 @@ const ToggleButtonGroup = React.forwardRef(function ToggleButtonGroup(props, ref
value,
...other
} = props;
const styleProps = { ...props, orientation, size };
const classes = useUtilityClasses(styleProps);

const handleChange = (event, buttonValue) => {
if (!onChange) {
Expand Down Expand Up @@ -94,16 +133,11 @@ const ToggleButtonGroup = React.forwardRef(function ToggleButtonGroup(props, ref
};

return (
<div
<ToggleButtonGroupRoot
role="group"
className={clsx(
classes.root,
{
[classes.vertical]: orientation === 'vertical',
},
className,
)}
className={clsx(classes.root, className)}
ref={ref}
styleProps={styleProps}
{...other}
>
{React.Children.map(children, (child) => {
Expand All @@ -123,11 +157,7 @@ const ToggleButtonGroup = React.forwardRef(function ToggleButtonGroup(props, ref
}

return React.cloneElement(child, {
className: clsx(
classes.grouped,
classes[`grouped${capitalize(orientation)}`],
child.props.className,
),
className: clsx(classes.grouped, child.props.className),
onChange: exclusive ? handleExclusiveChange : handleChange,
selected:
child.props.selected === undefined
Expand All @@ -136,7 +166,7 @@ const ToggleButtonGroup = React.forwardRef(function ToggleButtonGroup(props, ref
size: child.props.size || size,
});
})}
</div>
</ToggleButtonGroupRoot>
);
});

Expand Down Expand Up @@ -181,6 +211,10 @@ ToggleButtonGroup.propTypes = {
* @default 'medium'
*/
size: PropTypes.oneOf(['large', 'medium', 'small']),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
/**
* The currently selected value within the group or an array of selected
* values when `exclusive` is false.
Expand All @@ -190,4 +224,4 @@ ToggleButtonGroup.propTypes = {
value: PropTypes.any,
};

export default withStyles(styles, { name: 'MuiToggleButtonGroup' })(ToggleButtonGroup);
export default ToggleButtonGroup;
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { getClasses, createMount, describeConformance, createClientRender } from 'test/utils';
import ToggleButtonGroup from './ToggleButtonGroup';
import ToggleButton from '../ToggleButton';
import { createMount, describeConformanceV5, createClientRender } from 'test/utils';
import ToggleButtonGroup, {
toggleButtonGroupClasses as classes,
} from '@material-ui/core/ToggleButtonGroup';
import ToggleButton from '@material-ui/core/ToggleButton';

describe('<ToggleButtonGroup />', () => {
const mount = createMount();
let classes;
const render = createClientRender();

before(() => {
classes = getClasses(
<ToggleButtonGroup>
<ToggleButton value="hello" />
</ToggleButtonGroup>,
);
});

describeConformance(<ToggleButtonGroup />, () => ({
describeConformanceV5(<ToggleButtonGroup />, () => ({
classes,
inheritComponent: 'div',
mount,
muiName: 'MuiToggleButtonGroup',
refInstanceof: window.HTMLDivElement,
skip: ['componentProp'],
skip: ['componentProp', 'componentsProp'],
testVariantProps: { size: 'small' },
testStateOverrides: { prop: 'orientation', value: 'vertical', styleKey: 'vertical' },
}));

it('renders a `group`', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/material-ui/src/ToggleButtonGroup/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { default } from './ToggleButtonGroup';
export * from './ToggleButtonGroup';

export { default as toggleButtonGroupClasses } from './toggleButtonGroupClasses';
export * from './toggleButtonGroupClasses';
3 changes: 3 additions & 0 deletions packages/material-ui/src/ToggleButtonGroup/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export { default } from './ToggleButtonGroup';

export { default as toggleButtonGroupClasses } from './toggleButtonGroupClasses';
export * from './toggleButtonGroupClasses';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ToggleButtonGroupClassKey } from './ToggleButtonGroup';

declare const toggleButtonGroupClasses: Record<ToggleButtonGroupClassKey, string>;

export function getToggleButtonGroupUtilityClass(slot: string): string;

export default toggleButtonGroupClasses;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled';

export function getToggleButtonGroupUtilityClass(slot) {
return generateUtilityClass('MuiToggleButtonGroup', slot);
}

const toggleButtonGroupClasses = generateUtilityClasses('MuiToggleButtonGroup', [
'root',
'vertical',
'grouped',
'groupedHorizontal',
'groupedVertical',
]);

export default toggleButtonGroupClasses;