diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97f628306..f205abcff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
## Not released
+- ToggleButtonGroup component: Add support for variant and backgroundColor [#824](https://github.com/CartoDB/carto-react/pull/824)
+
## 2.3
### 2.3.7 (2024-01-11)
diff --git a/UPGRADE.md b/UPGRADE.md
index fe7612a5c..8cfa7c3bc 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -204,6 +204,18 @@ So, instead of Mui Button, the component you should use to create buttons is thi
For external use: `import { Button } from '@carto/react-ui';`.
+### ToggleButtonGroup
+
+We have a `ToggleButtonGroup` component that uses `Mui ToggleButtonGroup` and extends it with some extra props:
+
+- variant
+- backgroundColor
+
+So, instead of Mui ToggleButtonGroup, the component you should use is this one:
+`react-ui/src/components/atoms/ToggleButtonGroup`
+
+For external use: `import { ToggleButtonGroup } from '@carto/react-ui';`.
+
### AppBar
We have a custom component to build the basic structure and styles on top of AppBar Mui component.
diff --git a/packages/react-ui/src/components/atoms/ToggleButtonGroup.d.ts b/packages/react-ui/src/components/atoms/ToggleButtonGroup.d.ts
new file mode 100644
index 000000000..b292f506d
--- /dev/null
+++ b/packages/react-ui/src/components/atoms/ToggleButtonGroup.d.ts
@@ -0,0 +1,9 @@
+import { ToggleButtonGroupProps as MuiToggleButtonGroupProps } from '@mui/material';
+
+export type ToggleButtonGroupProps = MuiToggleButtonGroupProps & {
+ variant?: 'contained' | 'floating' | 'unbounded';
+ backgroundColor?: 'primary' | 'secondary' | 'transparent';
+};
+
+declare const ToggleButtonGroup: (props: ToggleButtonGroupProps) => JSX.Element;
+export default ToggleButtonGroup;
diff --git a/packages/react-ui/src/components/atoms/ToggleButtonGroup.js b/packages/react-ui/src/components/atoms/ToggleButtonGroup.js
new file mode 100644
index 000000000..3e84bbd79
--- /dev/null
+++ b/packages/react-ui/src/components/atoms/ToggleButtonGroup.js
@@ -0,0 +1,110 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { ToggleButtonGroup as MuiToggleButtonGroup, styled } from '@mui/material';
+
+const StyledToggleButtonGroup = styled(MuiToggleButtonGroup, {
+ shouldForwardProp: (prop) => !['variant', 'backgroundColor'].includes(prop)
+})(({ variant, backgroundColor, theme }) => ({
+ // Variants
+ ...(variant === 'contained' && {
+ boxShadow: 'none'
+ }),
+ ...(variant === 'unbounded' && {
+ boxShadow: 'none',
+ borderRadius: theme.spacing(0.5),
+
+ '& .MuiDivider-root': {
+ height: theme.spacing(4),
+
+ '&.MuiToggleButtonGroup-groupedHorizontal': {
+ height: theme.spacing(4)
+ },
+ '&.MuiToggleButtonGroup-groupedVertical': {
+ height: 'auto',
+ width: theme.spacing(4),
+ margin: `${theme.spacing(0.5, 0, 1)} !important`,
+ borderRadius: '0 !important'
+ }
+ },
+
+ '& .MuiToggleButton-sizeSmall': {
+ margin: 0,
+
+ '&.MuiToggleButtonGroup-grouped:not(.MuiDivider-root)': {
+ margin: 0
+ },
+ '& + .MuiDivider-root.MuiToggleButtonGroup-groupedHorizontal': {
+ height: theme.spacing(3)
+ },
+ '& + .MuiDivider-root.MuiToggleButtonGroup-groupedVertical': {
+ height: 'auto',
+ width: theme.spacing(3)
+ }
+ },
+
+ '.MuiToggleButtonGroup-grouped:not(.MuiDivider-root)': {
+ margin: 0,
+
+ '&:first-of-type': {
+ marginLeft: 0
+ },
+ '&:not(:last-of-type)': {
+ marginRight: theme.spacing(0.5)
+ }
+ },
+ '&.MuiToggleButtonGroup-horizontal:not(.MuiDivider-root)': {
+ '.MuiToggleButtonGroup-grouped': {
+ margin: theme.spacing(0, 0.5)
+ }
+ },
+ '&.MuiToggleButtonGroup-vertical:not(.MuiDivider-root)': {
+ '.MuiToggleButtonGroup-grouped': {
+ margin: theme.spacing(0, 0, 0.5),
+
+ '&:not(:last-of-type)': {
+ marginRight: 0
+ },
+ '&:last-of-type': {
+ marginBottom: 0
+ }
+ }
+ }
+ }),
+
+ // Colors
+ ...(backgroundColor === 'primary' && {
+ backgroundColor: theme.palette.background.paper
+ }),
+ ...(backgroundColor === 'secondary' && {
+ backgroundColor: theme.palette.background.default
+ }),
+ ...(backgroundColor === 'transparent' && {
+ backgroundColor: 'transparent'
+ })
+}));
+
+const ToggleButtonGroup = ({ children, variant, backgroundColor, ...rest }) => {
+ const isUnbounded = variant === 'unbounded';
+ const defaultColor = isUnbounded ? 'transparent' : 'primary';
+
+ return (
+
+ {children}
+
+ );
+};
+
+ToggleButtonGroup.defaultProps = {
+ variant: 'floating'
+};
+
+ToggleButtonGroup.propTypes = {
+ variant: PropTypes.oneOf(['floating', 'contained', 'unbounded']),
+ backgroundColor: PropTypes.oneOf(['primary', 'secondary', 'transparent'])
+};
+
+export default ToggleButtonGroup;
diff --git a/packages/react-ui/src/index.d.ts b/packages/react-ui/src/index.d.ts
index 7f2494631..3899b5a77 100644
--- a/packages/react-ui/src/index.d.ts
+++ b/packages/react-ui/src/index.d.ts
@@ -37,8 +37,11 @@ import Typography, {
TypographyProps
} from './components/atoms/Typography';
import Button, { ButtonProps } from './components/atoms/Button';
-import PasswordField, { PasswordFieldProps } from './components/atoms/PasswordField';
import SelectField, { SelectFieldProps } from './components/atoms/SelectField';
+import PasswordField, { PasswordFieldProps } from './components/atoms/PasswordField';
+import ToggleButtonGroup, {
+ ToggleButtonGroupProps
+} from './components/atoms/ToggleButtonGroup';
import UploadField, {
UploadFieldProps
} from './components/molecules/UploadField/UploadField';
@@ -101,6 +104,8 @@ export {
CartoFontWeight,
Button,
ButtonProps,
+ ToggleButtonGroup,
+ ToggleButtonGroupProps,
PasswordField,
PasswordFieldProps,
SelectField,
diff --git a/packages/react-ui/src/index.js b/packages/react-ui/src/index.js
index a9c7165f8..19960221f 100644
--- a/packages/react-ui/src/index.js
+++ b/packages/react-ui/src/index.js
@@ -31,6 +31,7 @@ import CircleIcon from './assets/icons/CircleIcon';
import ArrowDropIcon from './assets/icons/ArrowDropIcon';
import Typography from './components/atoms/Typography';
import Button from './components/atoms/Button';
+import ToggleButtonGroup from './components/atoms/ToggleButtonGroup';
import PasswordField from './components/atoms/PasswordField';
import SelectField from './components/atoms/SelectField';
import UploadField from './components/molecules/UploadField/UploadField';
@@ -86,6 +87,7 @@ export {
LegendRamp,
Typography,
Button,
+ ToggleButtonGroup,
PasswordField,
SelectField,
UploadField,
diff --git a/packages/react-ui/src/theme/sections/components/buttons.js b/packages/react-ui/src/theme/sections/components/buttons.js
index 6cfc974a9..06c484452 100644
--- a/packages/react-ui/src/theme/sections/components/buttons.js
+++ b/packages/react-ui/src/theme/sections/components/buttons.js
@@ -397,9 +397,25 @@ export const buttonsOverrides = {
borderRadius: radius
},
'.MuiDivider-root': {
- height: sizeLarge,
- margin: theme.spacing(0, 1),
- marginLeft: theme.spacing(0.5)
+ '&.MuiToggleButtonGroup-groupedHorizontal': {
+ height: sizeLarge,
+ margin: theme.spacing(0, 1),
+ marginLeft: theme.spacing(0.5)
+ },
+ '&.MuiToggleButtonGroup-groupedVertical': {
+ width: sizeLarge,
+ margin: theme.spacing(1, 0),
+ marginTop: theme.spacing(0.5)
+ }
+ },
+
+ '.MuiToggleButton-sizeSmall': {
+ '& + .MuiDivider-root.MuiToggleButtonGroup-groupedHorizontal': {
+ height: sizeMedium
+ },
+ '& + .MuiDivider-root.MuiToggleButtonGroup-groupedVertical': {
+ width: sizeMedium
+ }
}
}),
// Styles applied to the children if orientation="horizontal"
@@ -412,18 +428,15 @@ export const buttonsOverrides = {
marginLeft: 0,
borderLeft: 'none'
},
- '&:first-of-type': {
+ '&:first-of-type:not(.MuiDivider-root)': {
marginLeft: theme.spacing(1)
},
- '&.MuiToggleButton-sizeSmall': {
+ '&.MuiToggleButton-sizeSmall:not(.MuiDivider-root)': {
height: sizeSmall,
margin: theme.spacing(0.5),
'&:not(:first-of-type)': {
marginLeft: 0
- },
- '& + .MuiDivider-root': {
- height: sizeMedium
}
}
}),
@@ -434,7 +447,11 @@ export const buttonsOverrides = {
'&.MuiToggleButton-root': {
marginLeft: theme.spacing(1),
- marginBottom: theme.spacing(0.5)
+ marginBottom: theme.spacing(0.5),
+
+ '&:last-of-type': {
+ marginBottom: theme.spacing(1)
+ }
},
'&.MuiToggleButton-sizeSmall': {
width: sizeSmall,
@@ -442,6 +459,9 @@ export const buttonsOverrides = {
'&:not(:first-of-type)': {
marginTop: 0
+ },
+ '&:last-of-type': {
+ marginBottom: theme.spacing(0.5)
}
}
})
diff --git a/packages/react-ui/storybook/stories/molecules/ToggleButton.stories.js b/packages/react-ui/storybook/stories/molecules/ToggleButton.stories.js
index 9de133081..526fb8fce 100644
--- a/packages/react-ui/storybook/stories/molecules/ToggleButton.stories.js
+++ b/packages/react-ui/storybook/stories/molecules/ToggleButton.stories.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { ToggleButtonGroup, ToggleButton, Grid, Divider } from '@mui/material';
+import { ToggleButton, Grid, Divider } from '@mui/material';
import {
CheckCircleOutline,
FormatAlignCenter,
@@ -8,11 +8,26 @@ import {
FormatAlignRight
} from '@mui/icons-material';
import Typography from '../../../src/components/atoms/Typography';
+import ToggleButtonGroup from '../../../src/components/atoms/ToggleButtonGroup';
+import { DocContainer, DocHighlight, DocLink } from '../../utils/storyStyles';
const options = {
title: 'Molecules/Toggle Button',
component: ToggleButtonGroup,
argTypes: {
+ variant: {
+ defaultValue: 'floating',
+ control: {
+ type: 'select',
+ options: ['floating', 'contained', 'unbounded']
+ }
+ },
+ backgroundColor: {
+ control: {
+ type: 'select',
+ options: ['primary', 'secondary', 'transparent']
+ }
+ },
size: {
defaultValue: 'medium',
control: {
@@ -57,13 +72,13 @@ const options = {
url: 'https://www.figma.com/file/nmaoLeo69xBJCHm9nc6lEV/CARTO-Components-1.0?node-id=1534%3A36258'
},
status: {
- type: 'validated'
+ type: 'readyToReview'
}
}
};
export default options;
-const Toggle = ({ label, exclusive, ...rest }) => {
+const Toggle = ({ label, ...rest }) => {
const [selected, setSelected] = React.useState(false);
return (
@@ -81,8 +96,10 @@ const Toggle = ({ label, exclusive, ...rest }) => {
);
};
-const ToggleRow = ({ label, divider, fullWidth, exclusive, ...rest }) => {
+const ToggleRow = ({ label, divider, fullWidth, exclusive, orientation, ...rest }) => {
const [selected, setSelected] = React.useState(() => ['AlignLeft']);
+ const isVertical = orientation === 'vertical';
+ const dividerOrientation = isVertical ? 'horizontal' : 'vertical';
const handleAlignment = (event, newAlignment) => {
setSelected(newAlignment);
@@ -95,6 +112,7 @@ const ToggleRow = ({ label, divider, fullWidth, exclusive, ...rest }) => {
onChange={handleAlignment}
fullWidth={fullWidth}
exclusive={exclusive}
+ orientation={orientation}
aria-label='text alignment'
>
@@ -103,7 +121,9 @@ const ToggleRow = ({ label, divider, fullWidth, exclusive, ...rest }) => {
{label ? label : }
- {divider && }
+ {divider && (
+
+ )}
{label ? label : }
@@ -114,7 +134,33 @@ const ToggleRow = ({ label, divider, fullWidth, exclusive, ...rest }) => {
);
};
-const IconTemplate = ({ exclusive, ...args }) => {
+const DocTemplate = () => {
+ return (
+
+ We have our own
+
+ ToggleButtonGroup
+
+ component that extends Mui ToggleButtonGroup with some props (variant and
+ backgroundColor support).
+
+ So, instead of Mui ToggleButtonGroup, you should use this one:
+
+ react-ui/src/components/atoms/ToggleButtonGroup
+
+
+
+ For external use:
+
+ {'import { ToggleButtonGroup } from "@carto/react-ui";'}
+
+ .
+
+
+ );
+};
+
+const IconTemplate = (args) => {
return (
@@ -130,7 +176,7 @@ const IconTemplate = ({ exclusive, ...args }) => {
);
};
-const TextTemplate = ({ exclusive, ...args }) => {
+const TextTemplate = (args) => {
return (
@@ -143,7 +189,7 @@ const TextTemplate = ({ exclusive, ...args }) => {
);
};
-const GroupTemplate = ({ exclusive, ...args }) => {
+const GroupTemplate = (args) => {
return (
@@ -156,7 +202,7 @@ const GroupTemplate = ({ exclusive, ...args }) => {
);
};
-const VerticalGroupTemplate = ({ exclusive, ...args }) => {
+const VerticalGroupTemplate = (args) => {
return (
@@ -169,7 +215,7 @@ const VerticalGroupTemplate = ({ exclusive, ...args }) => {
);
};
-const DividedTemplate = ({ exclusive, ...args }) => {
+const DividedTemplate = (args) => {
return (
@@ -182,6 +228,38 @@ const DividedTemplate = ({ exclusive, ...args }) => {
);
};
+const VariantTemplate = (args) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const BgColorTemplate = (args) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
const BehaviorTemplate = ({ exclusive, ...args }) => {
const [selected, setSelected] = React.useState(() => ['opt1']);
const [selected2, setSelected2] = React.useState(() => ['opt1']);
@@ -234,6 +312,8 @@ const BehaviorTemplate = ({ exclusive, ...args }) => {
export const Playground = ToggleRow.bind({});
+export const Guide = DocTemplate.bind({});
+
export const Icon = IconTemplate.bind({});
export const Text = TextTemplate.bind({});
@@ -251,4 +331,8 @@ HorizontalTextGroup.args = { label: 'Text' };
export const MultipleSelectionGroup = GroupTemplate.bind({});
MultipleSelectionGroup.args = { exclusive: false };
+export const Variant = VariantTemplate.bind({});
+
+export const BackgroundColor = BgColorTemplate.bind({});
+
export const Behavior = BehaviorTemplate.bind({});