From 6fb06f9f0820d82642bfae8feb84e56ddac95dc7 Mon Sep 17 00:00:00 2001 From: Sandra Marcela Herrera Arriaga Date: Thu, 12 Dec 2019 07:50:31 -0600 Subject: [PATCH] [ButtonGroup] Add orientation prop (#18762) --- docs/pages/api/button-group.md | 12 +- docs/src/modules/components/Demo.js | 2 +- .../components/buttons/GroupOrientation.js | 17 +++ .../components/buttons/GroupOrientation.tsx | 17 +++ .../components/buttons/GroupSizesColors.js | 39 +++++ .../components/buttons/GroupSizesColors.tsx | 41 +++++ .../components/buttons/GroupedButtons.js | 138 ++++------------- .../components/buttons/GroupedButtons.tsx | 140 +++++------------- docs/src/pages/components/buttons/buttons.md | 14 +- .../src/ButtonGroup/ButtonGroup.d.ts | 14 +- .../src/ButtonGroup/ButtonGroup.js | 72 ++++++++- 11 files changed, 278 insertions(+), 228 deletions(-) create mode 100644 docs/src/pages/components/buttons/GroupOrientation.js create mode 100644 docs/src/pages/components/buttons/GroupOrientation.tsx create mode 100644 docs/src/pages/components/buttons/GroupSizesColors.js create mode 100644 docs/src/pages/components/buttons/GroupSizesColors.tsx diff --git a/docs/pages/api/button-group.md b/docs/pages/api/button-group.md index bdaf6901e34887..07a906a7f944c1 100644 --- a/docs/pages/api/button-group.md +++ b/docs/pages/api/button-group.md @@ -32,6 +32,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi | disableFocusRipple | bool | false | If `true`, the button keyboard focus ripple will be disabled. `disableRipple` must also be true. | | disableRipple | bool | false | If `true`, the button ripple effect will be disabled. | | fullWidth | bool | false | If `true`, the buttons will take up the full width of its container. | +| orientation | 'vertical'
| 'horizontal'
| 'horizontal' | The group orientation. | | size | 'small'
| 'medium'
| 'large'
| 'medium' | The size of the button. `small` is equivalent to the dense button styling. | | variant | 'text'
| 'outlined'
| 'contained'
| 'outlined' | The variant to use. | @@ -48,18 +49,27 @@ Any other props supplied will be provided to the root element (native element). |:-----|:-------------|:------------| | root | .MuiButtonGroup-root | Styles applied to the root element. | contained | .MuiButtonGroup-contained | Styles applied to the root element if `variant="contained"`. +| disabled | .Mui-disabled | Pseudo-class applied to child elements if `disabled={true}`. | fullWidth | .MuiButtonGroup-fullWidth | Styles applied to the root element if `fullWidth={true}`. +| vertical | .MuiButtonGroup-vertical | Styles applied to the root element if `orientation="vertical"`. | grouped | .MuiButtonGroup-grouped | Styles applied to the children. +| groupedHorizontal | .MuiButtonGroup-groupedHorizontal | Styles applied to the children if `orientation="horizontal"`. +| groupedVertical | .MuiButtonGroup-groupedVertical | Styles applied to the children if `orientation="vertical"`. | groupedText | .MuiButtonGroup-groupedText | Styles applied to the children if `variant="text"`. +| groupedTextHorizontal | .MuiButtonGroup-groupedTextHorizontal | Styles applied to the children if `variant="text"` and `orientation="horizontal"`. +| groupedTextVertical | .MuiButtonGroup-groupedTextVertical | Styles applied to the children if `variant="text"` and `orientation="vertical"`. | groupedTextPrimary | .MuiButtonGroup-groupedTextPrimary | Styles applied to the children if `variant="text"` and `color="primary"`. | groupedTextSecondary | .MuiButtonGroup-groupedTextSecondary | Styles applied to the children if `variant="text"` and `color="secondary"`. | groupedOutlined | .MuiButtonGroup-groupedOutlined | Styles applied to the children if `variant="outlined"`. +| groupedOutlinedHorizontal | .MuiButtonGroup-groupedOutlinedHorizontal | Styles applied to the children if `variant="outlined"` and `orientation="horizontal"`. +| groupedOutlinedVertical | .MuiButtonGroup-groupedOutlinedVertical | Styles applied to the children if `variant="outlined"` and `orientation="vertical"`. | groupedOutlinedPrimary | .MuiButtonGroup-groupedOutlinedPrimary | Styles applied to the children if `variant="outlined"` and `color="primary"`. | groupedOutlinedSecondary | .MuiButtonGroup-groupedOutlinedSecondary | Styles applied to the children if `variant="outlined"` and `color="secondary"`. | groupedContained | .MuiButtonGroup-groupedContained | Styles applied to the children if `variant="contained"`. +| groupedContainedHorizontal | .MuiButtonGroup-groupedContainedHorizontal | Styles applied to the children if `variant="contained"` and `orientation="horizontal"`. +| groupedContainedVertical | .MuiButtonGroup-groupedContainedVertical | Styles applied to the children if `variant="contained"` and `orientation="vertical"`. | groupedContainedPrimary | .MuiButtonGroup-groupedContainedPrimary | Styles applied to the children if `variant="contained"` and `color="primary"`. | groupedContainedSecondary | .MuiButtonGroup-groupedContainedSecondary | Styles applied to the children if `variant="contained"` and `color="secondary"`. -| disabled | .Mui-disabled | Pseudo-class applied to child elements if `disabled={true}`. You can override the style of the component thanks to one of these customization points: diff --git a/docs/src/modules/components/Demo.js b/docs/src/modules/components/Demo.js index b358e76ff797a0..7a39b301991b80 100644 --- a/docs/src/modules/components/Demo.js +++ b/docs/src/modules/components/Demo.js @@ -301,7 +301,7 @@ function Demo(props) { !demoOptions.hideHeader && demoOptions.defaultCodeOpen !== false && jsx !== demoData.raw && - jsx.split(/\n/).length <= 15; + jsx.split(/\n/).length <= 16; let showCodeLabel; if (codeOpen) { diff --git a/docs/src/pages/components/buttons/GroupOrientation.js b/docs/src/pages/components/buttons/GroupOrientation.js new file mode 100644 index 00000000000000..d734964c20a2e3 --- /dev/null +++ b/docs/src/pages/components/buttons/GroupOrientation.js @@ -0,0 +1,17 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; + +export default function GroupOrientation() { + return ( + + + + + + ); +} diff --git a/docs/src/pages/components/buttons/GroupOrientation.tsx b/docs/src/pages/components/buttons/GroupOrientation.tsx new file mode 100644 index 00000000000000..d734964c20a2e3 --- /dev/null +++ b/docs/src/pages/components/buttons/GroupOrientation.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; + +export default function GroupOrientation() { + return ( + + + + + + ); +} diff --git a/docs/src/pages/components/buttons/GroupSizesColors.js b/docs/src/pages/components/buttons/GroupSizesColors.js new file mode 100644 index 00000000000000..bf008c58e05bf9 --- /dev/null +++ b/docs/src/pages/components/buttons/GroupSizesColors.js @@ -0,0 +1,39 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(theme => ({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + '& > *': { + margin: theme.spacing(1), + }, + }, +})); + +export default function GroupSizesColors() { + const classes = useStyles(); + + return ( +
+ + + + + + + + + + + + + + + +
+ ); +} diff --git a/docs/src/pages/components/buttons/GroupSizesColors.tsx b/docs/src/pages/components/buttons/GroupSizesColors.tsx new file mode 100644 index 00000000000000..1795ce6d0584aa --- /dev/null +++ b/docs/src/pages/components/buttons/GroupSizesColors.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import Button from '@material-ui/core/Button'; +import ButtonGroup from '@material-ui/core/ButtonGroup'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); + +export default function GroupSizesColors() { + const classes = useStyles(); + + return ( +
+ + + + + + + + + + + + + + + +
+ ); +} diff --git a/docs/src/pages/components/buttons/GroupedButtons.js b/docs/src/pages/components/buttons/GroupedButtons.js index 2255e554426e26..8014baf81b135b 100644 --- a/docs/src/pages/components/buttons/GroupedButtons.js +++ b/docs/src/pages/components/buttons/GroupedButtons.js @@ -1,115 +1,39 @@ import React from 'react'; -import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; import ButtonGroup from '@material-ui/core/ButtonGroup'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(theme => ({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + '& > *': { + margin: theme.spacing(1), + }, + }, +})); export default function GroupedButtons() { + const classes = useStyles(); + return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + + +
); } diff --git a/docs/src/pages/components/buttons/GroupedButtons.tsx b/docs/src/pages/components/buttons/GroupedButtons.tsx index 2255e554426e26..15256556ac62e2 100644 --- a/docs/src/pages/components/buttons/GroupedButtons.tsx +++ b/docs/src/pages/components/buttons/GroupedButtons.tsx @@ -1,115 +1,41 @@ import React from 'react'; -import Grid from '@material-ui/core/Grid'; import Button from '@material-ui/core/Button'; import ButtonGroup from '@material-ui/core/ButtonGroup'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + '& > *': { + margin: theme.spacing(1), + }, + }, + }), +); export default function GroupedButtons() { + const classes = useStyles(); + return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ + + + + + + + + + + + + + + +
); } diff --git a/docs/src/pages/components/buttons/buttons.md b/docs/src/pages/components/buttons/buttons.md index 747a424eed350d..b6df6213d1d98f 100644 --- a/docs/src/pages/components/buttons/buttons.md +++ b/docs/src/pages/components/buttons/buttons.md @@ -47,8 +47,6 @@ In cards, text buttons help maintain an emphasis on card content. are medium-emphasis buttons. They contain actions that are important, but aren’t the primary action in an app. -### Alternatives - Outlined buttons are also a lower emphasis alternative to contained buttons, or a higher emphasis alternative to text buttons. @@ -56,11 +54,19 @@ or a higher emphasis alternative to text buttons. ## Grouped Buttons -The ButtonGroup component can be used to group outlined (the default) or contained buttons. +The `ButtonGroup` component can be used to group buttons. {{"demo": "pages/components/buttons/GroupedButtons.js"}} -## Split Button +### Group sizes and colors + +{{"demo": "pages/components/buttons/GroupSizesColors.js"}} + +### Group orientation + +{{"demo": "pages/components/buttons/GroupOrientation.js"}} + +### Split Button ButtonGroup can also be used to create a split button. The dropdown can change the button action (as in this example), or be used to immediately trigger a related action. diff --git a/packages/material-ui/src/ButtonGroup/ButtonGroup.d.ts b/packages/material-ui/src/ButtonGroup/ButtonGroup.d.ts index 745c31d4355a2a..368fe962495050 100644 --- a/packages/material-ui/src/ButtonGroup/ButtonGroup.d.ts +++ b/packages/material-ui/src/ButtonGroup/ButtonGroup.d.ts @@ -9,6 +9,7 @@ export interface ButtonGroupTypeMap

disableFocusRipple?: boolean; disableRipple?: boolean; fullWidth?: boolean; + orientation?: 'vertical' | 'horizontal'; size?: 'small' | 'medium' | 'large'; variant?: 'text' | 'outlined' | 'contained'; }; @@ -21,18 +22,27 @@ declare const ButtonGroup: OverridableComponent; export type ButtonGroupClassKey = | 'root' | 'contained' + | 'disabled' | 'fullWidth' + | 'vertical' | 'grouped' + | 'groupedHorizontal' + | 'groupedVertical' | 'groupedText' + | 'groupedTextHorizontal' + | 'groupedTextVertical' | 'groupedTextPrimary' | 'groupedTextSecondary' | 'groupedOutlined' + | 'groupedOutlinedHorizontal' + | 'groupedOutlinedVertical' | 'groupedOutlinedPrimary' | 'groupedOutlinedSecondary' | 'groupedContained' + | 'groupedContainedHorizontal' + | 'groupedContainedVertical' | 'groupedContainedPrimary' - | 'groupedContainedSecondary' - | 'disabled'; + | 'groupedContainedSecondary'; export type ButtonGroupProps< D extends React.ElementType = ButtonGroupTypeMap['defaultComponent'], diff --git a/packages/material-ui/src/ButtonGroup/ButtonGroup.js b/packages/material-ui/src/ButtonGroup/ButtonGroup.js index 9fb122ae6a4523..cf6504af9331f2 100644 --- a/packages/material-ui/src/ButtonGroup/ButtonGroup.js +++ b/packages/material-ui/src/ButtonGroup/ButtonGroup.js @@ -21,13 +21,22 @@ export const styles = theme => ({ contained: { boxShadow: theme.shadows[2], }, + /* Pseudo-class applied to child elements if `disabled={true}`. */ + disabled: {}, /* Styles applied to the root element if `fullWidth={true}`. */ fullWidth: { width: '100%', }, + /* Styles applied to the root element if `orientation="vertical"`. */ + vertical: { + flexDirection: 'column', + }, /* Styles applied to the children. */ grouped: { minWidth: 40, + }, + /* Styles applied to the children if `orientation="horizontal"`. */ + groupedHorizontal: { '&:not(:first-child)': { borderTopLeftRadius: 0, borderBottomLeftRadius: 0, @@ -37,14 +46,35 @@ export const styles = theme => ({ borderBottomRightRadius: 0, }, }, + /* Styles applied to the children if `orientation="vertical"`. */ + groupedVertical: { + '&:not(:first-child)': { + borderTopRightRadius: 0, + borderTopLeftRadius: 0, + }, + '&:not(:last-child)': { + borderBottomRightRadius: 0, + borderBottomLeftRadius: 0, + }, + }, /* Styles applied to the children if `variant="text"`. */ - groupedText: { + groupedText: {}, + /* Styles applied to the children if `variant="text"` and `orientation="horizontal"`. */ + groupedTextHorizontal: { '&:not(:last-child)': { borderRight: `1px solid ${ theme.palette.type === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)' }`, }, }, + /* Styles applied to the children if `variant="text"` and `orientation="vertical"`. */ + groupedTextVertical: { + '&:not(:last-child)': { + borderBottom: `1px solid ${ + theme.palette.type === 'light' ? 'rgba(0, 0, 0, 0.23)' : 'rgba(255, 255, 255, 0.23)' + }`, + }, + }, /* Styles applied to the children if `variant="text"` and `color="primary"`. */ groupedTextPrimary: { '&:not(:last-child)': { @@ -58,7 +88,9 @@ export const styles = theme => ({ }, }, /* Styles applied to the children if `variant="outlined"`. */ - groupedOutlined: { + groupedOutlined: {}, + /* Styles applied to the children if `variant="outlined"` and `orientation="horizontal"`. */ + groupedOutlinedHorizontal: { '&:not(:first-child)': { marginLeft: -1, }, @@ -66,6 +98,15 @@ export const styles = theme => ({ borderRightColor: 'transparent', }, }, + /* Styles applied to the children if `variant="outlined"` and `orientation="vertical"`. */ + groupedOutlinedVertical: { + '&:not(:first-child)': { + marginTop: -1, + }, + '&:not(:last-child)': { + borderBottomColor: 'transparent', + }, + }, /* Styles applied to the children if `variant="outlined"` and `color="primary"`. */ groupedOutlinedPrimary: { '&:hover': { @@ -81,6 +122,9 @@ export const styles = theme => ({ /* Styles applied to the children if `variant="contained"`. */ groupedContained: { boxShadow: 'none', + }, + /* Styles applied to the children if `variant="contained"` and `orientation="horizontal"`. */ + groupedContainedHorizontal: { '&:not(:last-child)': { borderRight: `1px solid ${theme.palette.grey[400]}`, '&$disabled': { @@ -88,20 +132,28 @@ export const styles = theme => ({ }, }, }, + + /* Styles applied to the children if `variant="contained"` and `orientation="vertical"`. */ + groupedContainedVertical: { + '&:not(:last-child)': { + borderBottom: `1px solid ${theme.palette.grey[400]}`, + '&$disabled': { + borderBottom: `1px solid ${theme.palette.action.disabled}`, + }, + }, + }, /* Styles applied to the children if `variant="contained"` and `color="primary"`. */ groupedContainedPrimary: { '&:not(:last-child)': { - borderRight: `1px solid ${theme.palette.primary.dark}`, + borderColor: theme.palette.primary.dark, }, }, /* Styles applied to the children if `variant="contained"` and `color="secondary"`. */ groupedContainedSecondary: { '&:not(:last-child)': { - borderRight: `1px solid ${theme.palette.secondary.dark}`, + borderColor: theme.palette.secondary.dark, }, }, - /* Pseudo-class applied to child elements if `disabled={true}`. */ - disabled: {}, }); const ButtonGroup = React.forwardRef(function ButtonGroup(props, ref) { @@ -115,6 +167,7 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(props, ref) { disableFocusRipple = false, disableRipple = false, fullWidth = false, + orientation = 'horizontal', size = 'medium', variant = 'outlined', ...other @@ -122,7 +175,9 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(props, ref) { const buttonClassName = clsx( classes.grouped, + classes[`grouped${capitalize(orientation)}`], classes[`grouped${capitalize(variant)}`], + classes[`grouped${capitalize(variant)}${capitalize(orientation)}`], classes[`grouped${capitalize(variant)}${color !== 'default' ? capitalize(color) : ''}`], { [classes.disabled]: disabled, @@ -136,6 +191,7 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(props, ref) { classes.root, { [classes.contained]: variant === 'contained', + [classes.vertical]: orientation === 'vertical', [classes.fullWidth]: fullWidth, }, className, @@ -214,6 +270,10 @@ ButtonGroup.propTypes = { * If `true`, the buttons will take up the full width of its container. */ fullWidth: PropTypes.bool, + /** + * The group orientation. + */ + orientation: PropTypes.oneOf(['vertical', 'horizontal']), /** * The size of the button. * `small` is equivalent to the dense button styling.