From 43da3657edb9faa5352f13300460df2c1bfc278c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ver=C3=B3nica=20Mil=C3=A1n?= <vmilan@cartodb.com> Date: Tue, 22 Nov 2022 13:31:33 +0100 Subject: [PATCH] Tabs component adapted to the new design system (#537) --- CHANGELOG.md | 1 + .../src/theme/sections/components/buttons.js | 3 + .../theme/sections/components/navigation.js | 73 ++--- .../stories/molecules/ToggleButton.stories.js | 17 +- .../stories/molecules/Tooltip.stories.js | 11 +- .../stories/organisms/Tabs.stories.js | 280 +++++++++++++++--- 6 files changed, 301 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28aeceba1..b77262412 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Not released +- Tabs component adapted to the new design system [#537](https://github.com/CartoDB/carto-react/pull/537/) - Tooltip component adapted to the new design system [#536](https://github.com/CartoDB/carto-react/pull/536/) - Switch component adapted to the new design system [#534](https://github.com/CartoDB/carto-react/pull/534/) - Design review of Toggle component [#533](https://github.com/CartoDB/carto-react/pull/533) diff --git a/packages/react-ui/src/theme/sections/components/buttons.js b/packages/react-ui/src/theme/sections/components/buttons.js index bf2438dfb..41b865fde 100644 --- a/packages/react-ui/src/theme/sections/components/buttons.js +++ b/packages/react-ui/src/theme/sections/components/buttons.js @@ -387,6 +387,9 @@ export const buttonsOverrides = { '&:not(:first-of-type)': { marginLeft: 0 + }, + '& + .MuiDivider-root': { + height: sizeMedium } } }, diff --git a/packages/react-ui/src/theme/sections/components/navigation.js b/packages/react-ui/src/theme/sections/components/navigation.js index b9c372d1c..ad59ec116 100644 --- a/packages/react-ui/src/theme/sections/components/navigation.js +++ b/packages/react-ui/src/theme/sections/components/navigation.js @@ -15,8 +15,7 @@ export const navigationOverrides = { // Tabs MuiTabs: { defaultProps: { - indicatorColor: 'primary', - textColor: 'primary', + iconPosition: 'start', TabIndicatorProps: { classes: { colorPrimary: 'colorPrimary' @@ -24,54 +23,56 @@ export const navigationOverrides = { } }, styleOverrides: { - indicator: { - height: 4, - '&.colorPrimary': { - backgroundColor: commonPalette.text.primary - } - }, - - vertical: { - '& .MuiTabs-indicator': { - width: 4 - }, + root: ({ ownerState }) => ({ + borderBottom: `1px solid ${commonPalette.black[12]}`, - '& .MuiTab-root': { - padding: getSpacing(0, 2), + ...(ownerState.variant !== 'fullWidth' && { + display: 'inline-flex' + }) + }), - '& .MuiTab-wrapper': { - alignItems: 'flex-start' - } - } + vertical: { + borderBottom: 0 } } }, // Tab MuiTab: { + defaultProps: { + iconPosition: 'start' + }, + styleOverrides: { root: { - padding: getSpacing(0, 1), - marginRight: getSpacing(3), - minWidth: '56px!important', - '&[class*="MuiTab-labelIcon"]': { - flexFlow: 'row', - alignItems: 'center' + minHeight: getSpacing(6), + minWidth: getSpacing(6), + padding: getSpacing(0, 2), + paddingTop: '2px', + borderBottom: '2px solid transparent', + ...themeTypography.subtitle2, + color: commonPalette.text.primary, + transition: 'border 300ms cubic-bezier(0.4, 0, 0.2, 1)', + + '&:hover': { + borderBottomColor: commonPalette.text.primary }, - '&[class*="MuiTab-labelIcon"] .MuiSvgIcon-root': { - marginRight: getSpacing(1), - marginBottom: 0 - } - }, - textColorPrimary: { - color: commonPalette.primary.main, - opacity: 1, '&.Mui-selected': { - color: commonPalette.text.primary + pointerEvents: 'none' }, - '&.Mui-disabled': { - color: commonPalette.action.disabled + '.MuiTabs-vertical &': { + paddingTop: 0, + borderBottom: 0, + paddingLeft: '2px', + borderRight: '2px solid transparent', + + '&:hover': { + borderRightColor: commonPalette.text.primary + } } + }, + wrapped: { + maxWidth: '240px' } } }, diff --git a/packages/react-ui/storybook/stories/molecules/ToggleButton.stories.js b/packages/react-ui/storybook/stories/molecules/ToggleButton.stories.js index 5da07a09f..b0a948e86 100644 --- a/packages/react-ui/storybook/stories/molecules/ToggleButton.stories.js +++ b/packages/react-ui/storybook/stories/molecules/ToggleButton.stories.js @@ -113,7 +113,7 @@ const ToggleRow = ({ label, divider, fullWidth, ...rest }) => { ); }; -const SizesTemplate = (args) => { +const IconTemplate = (args) => { return ( <Grid container alignItems='center' spacing={4}> <Grid item> @@ -174,6 +174,9 @@ const DividedTemplate = (args) => { <Grid item> <ToggleRow divider /> </Grid> + <Grid item> + <ToggleRow divider size='small' /> + </Grid> </Grid> ); }; @@ -224,20 +227,20 @@ const BehaviorTemplate = (args) => { export const Playground = ToggleRow.bind({}); -export const Sizes = SizesTemplate.bind({}); +export const Icon = IconTemplate.bind({}); export const Text = TextTemplate.bind({}); -export const HorizontalGroup = GroupTemplate.bind({}); - -export const HorizontalTextGroup = GroupTemplate.bind({}); -HorizontalTextGroup.args = { label: 'Text' }; - export const VerticalGroup = VerticalGroupTemplate.bind({}); VerticalGroup.args = { orientation: 'vertical' }; +export const HorizontalGroup = GroupTemplate.bind({}); + export const DividedGroup = DividedTemplate.bind({}); +export const HorizontalTextGroup = GroupTemplate.bind({}); +HorizontalTextGroup.args = { label: 'Text' }; + export const MultipleSelectionGroup = GroupTemplate.bind({}); MultipleSelectionGroup.args = { exclusive: false }; diff --git a/packages/react-ui/storybook/stories/molecules/Tooltip.stories.js b/packages/react-ui/storybook/stories/molecules/Tooltip.stories.js index 76c6e18ec..2f0264f71 100644 --- a/packages/react-ui/storybook/stories/molecules/Tooltip.stories.js +++ b/packages/react-ui/storybook/stories/molecules/Tooltip.stories.js @@ -2,9 +2,10 @@ import React from 'react'; import { Box, Grid, IconButton, Tooltip } from '@mui/material'; import { InfoOutlined } from '@mui/icons-material'; import makeStyles from '@mui/styles/makeStyles'; -import Typography from '../../../src/components/atoms/Typography'; -import TooltipData from '../../../src/components/organisms/TooltipData'; import { commonPalette } from '../../../src/theme/sections/palette'; +import TooltipData from '../../../src/components/organisms/TooltipData'; +import Typography from '../../../src/components/atoms/Typography'; +import Button from '../../../src/components/atoms/Button'; const options = { title: 'Molecules/Tooltip', @@ -33,7 +34,7 @@ const options = { url: 'https://www.figma.com/file/nmaoLeo69xBJCHm9nc6lEV/CARTO-Components-1.0?node-id=1534%3A36257' }, status: { - type: 'readyToReview' + type: 'validated' } } }; @@ -247,9 +248,7 @@ const TooltipBehaviorTemplate = (args) => { arrow={false} title='When followCursor is true, Tooltip should have arrow={false} property too' > - <IconButton> - <InfoOutlined /> - </IconButton> + <Button variant='outlined'>{'Long Button'}</Button> </Tooltip> </Box> </Grid> diff --git a/packages/react-ui/storybook/stories/organisms/Tabs.stories.js b/packages/react-ui/storybook/stories/organisms/Tabs.stories.js index 695097d55..aa7973323 100644 --- a/packages/react-ui/storybook/stories/organisms/Tabs.stories.js +++ b/packages/react-ui/storybook/stories/organisms/Tabs.stories.js @@ -1,6 +1,7 @@ import React from 'react'; -import { Tab, Tabs } from '@mui/material'; +import { Box, Grid, Tab, Tabs } from '@mui/material'; import { Layers, LocalOffer, Map, Place, Store } from '@mui/icons-material'; +import Typography from '../../../src/components/atoms/Typography'; const options = { title: 'Organisms/Tabs', @@ -9,26 +10,30 @@ const options = { variant: { control: { type: 'select', - options: ['standard', 'filled', 'outlined'] + options: ['default', 'fullWidth'] } }, - indicatorColor: { - default: 'primary', + orientation: { control: { type: 'select', - options: ['default', 'primary', 'secondary'] + options: ['horizontal', 'vertical'] } }, - textColor: { + label: { control: { - type: 'select', - options: ['default', 'primary', 'secondary'] + type: 'text' } }, - orientation: { + wrapped: { + defaultValue: false, control: { - type: 'select', - options: ['horizontal', 'vertical'] + type: 'boolean' + } + }, + disabled: { + defaultValue: true, + control: { + type: 'boolean' } } }, @@ -38,13 +43,31 @@ const options = { url: 'https://www.figma.com/file/nmaoLeo69xBJCHm9nc6lEV/CARTO-Components-1.0?node-id=1534%3A33239' }, status: { - type: 'needUpdate' + type: 'readyToReview' } } }; export default options; -const Template = ({ ...args }) => { +const Template = ({ label, wrapped, disabled, ...args }) => { + const [value, setValue] = React.useState(0); + + const handleChange = (event, newValue) => { + setValue(newValue); + }; + + return ( + <Tabs {...args} value={value} onChange={handleChange} aria-label='tabs example'> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} disabled={disabled} /> + </Tabs> + ); +}; + +const TemplateIcons = ({ label, wrapped, disabled, ...args }) => { const [value, setValue] = React.useState(0); const handleChange = (event, newValue) => { @@ -52,17 +75,17 @@ const Template = ({ ...args }) => { }; return ( - <Tabs value={value} onChange={handleChange} aria-label='tabs example' {...args}> - <Tab label='Maps' /> - <Tab label='Layers' /> - <Tab label='Tag' /> - <Tab label='POIs' /> - <Tab label='Stores' disabled /> + <Tabs {...args} value={value} onChange={handleChange} aria-label='tabs example'> + <Tab icon={<Map />} label={label} wrapped={wrapped} /> + <Tab icon={<Layers />} label={label} wrapped={wrapped} /> + <Tab icon={<LocalOffer />} label={label} wrapped={wrapped} /> + <Tab icon={<Place />} label={label} wrapped={wrapped} /> + <Tab icon={<Store />} label={label} wrapped={wrapped} disabled={disabled} /> </Tabs> ); }; -const TemplateOnlyIcons = ({ ...args }) => { +const TemplateIconsAndText = ({ label, wrapped, disabled, ...args }) => { const [value, setValue] = React.useState(0); const handleChange = (event, newValue) => { @@ -70,17 +93,17 @@ const TemplateOnlyIcons = ({ ...args }) => { }; return ( - <Tabs value={value} onChange={handleChange} aria-label='tabs example' {...args}> - <Tab icon={<Map />} /> - <Tab icon={<Layers />} /> - <Tab icon={<LocalOffer />} /> - <Tab icon={<Place />} /> - <Tab icon={<Store />} disabled /> + <Tabs {...args} value={value} onChange={handleChange} aria-label='tabs example'> + <Tab icon={<Map />} label={label} wrapped={wrapped} /> + <Tab icon={<Layers />} label={label} wrapped={wrapped} /> + <Tab icon={<LocalOffer />} label={label} wrapped={wrapped} /> + <Tab icon={<Place />} label={label} wrapped={wrapped} /> + <Tab icon={<Store />} label={label} wrapped={wrapped} disabled={disabled} /> </Tabs> ); }; -const TemplateIconsLabels = ({ ...args }) => { +const TemplateText = ({ label, wrapped, disabled, ...args }) => { const [value, setValue] = React.useState(0); const handleChange = (event, newValue) => { @@ -88,16 +111,203 @@ const TemplateIconsLabels = ({ ...args }) => { }; return ( - <Tabs value={value} onChange={handleChange} aria-label='tabs example' {...args}> - <Tab label='Maps' icon={<Map />} /> - <Tab label='Layers' icon={<Layers />} /> - <Tab label='Tag' icon={<LocalOffer />} /> - <Tab label='POIs' icon={<Place />} /> - <Tab label='Stores' icon={<Store />} disabled /> + <Tabs {...args} value={value} onChange={handleChange} aria-label='tabs example'> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} disabled={disabled} /> </Tabs> ); }; +const TemplateWrapped = ({ label, wrapped, disabled, ...args }) => { + const [value, setValue] = React.useState(0); + + const handleChange = (event, newValue) => { + setValue(newValue); + }; + + return ( + <Tabs {...args} value={value} onChange={handleChange} aria-label='tabs example'> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} disabled={disabled} /> + </Tabs> + ); +}; + +const TemplateVertical = ({ label, wrapped, disabled, ...args }) => { + const [value, setValue] = React.useState(0); + const [value2, setValue2] = React.useState(0); + const [value3, setValue3] = React.useState(0); + const [value4, setValue4] = React.useState(0); + + const handleChange = (event, newValue) => { + setValue(newValue); + }; + const handleChange2 = (event, newValue) => { + setValue2(newValue); + }; + const handleChange3 = (event, newValue) => { + setValue3(newValue); + }; + const handleChange4 = (event, newValue) => { + setValue4(newValue); + }; + + return ( + <Grid container justifyContent='space-between'> + <Grid item> + <Tabs + {...args} + orientation='vertical' + value={value} + onChange={handleChange} + aria-label='tabs example' + > + <Tab icon={<Map />} /> + <Tab icon={<Layers />} /> + <Tab icon={<LocalOffer />} /> + <Tab icon={<Place />} /> + <Tab icon={<Store />} disabled={disabled} /> + </Tabs> + </Grid> + <Grid item> + <Tabs + {...args} + orientation='vertical' + value={value2} + onChange={handleChange2} + aria-label='tabs example' + > + <Tab icon={<Map />} label={label} wrapped={wrapped} /> + <Tab icon={<Layers />} label={label} wrapped={wrapped} /> + <Tab icon={<LocalOffer />} label={label} wrapped={wrapped} /> + <Tab icon={<Place />} label={label} wrapped={wrapped} /> + <Tab icon={<Store />} label={label} wrapped={wrapped} disabled={disabled} /> + </Tabs> + </Grid> + <Grid item> + <Tabs + {...args} + orientation='vertical' + value={value3} + onChange={handleChange3} + aria-label='tabs example' + > + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} /> + <Tab label={label} wrapped={wrapped} disabled={disabled} /> + </Tabs> + </Grid> + <Grid item> + <Tabs + {...args} + orientation='vertical' + value={value4} + onChange={handleChange4} + aria-label='tabs example' + > + <Tab label='Wrapped Label used for a very long text' wrapped /> + <Tab label='Wrapped Label used for a very long text' wrapped /> + <Tab + label='Wrapped Label used for a very long text' + wrapped + disabled={disabled} + /> + </Tabs> + </Grid> + </Grid> + ); +}; + +const BehaviorTemplate = (args) => { + const [value, setValue] = React.useState(0); + const [value2, setValue2] = React.useState(0); + + const handleChange = (event, newValue) => { + setValue(newValue); + }; + const handleChange2 = (event, newValue) => { + setValue2(newValue); + }; + + return ( + <Grid container direction='column' spacing={6}> + <Grid item container direction='column' alignItems='flex-start'> + <Typography variant='body1'>{'Hug Text'}</Typography> + <Typography variant='body2'>{'Default behavior'}</Typography> + + <Tabs + {...args} + value={value} + onChange={handleChange} + aria-label='tabs example' + {...args} + > + <Tab label='Extra Long Label' icon={<Map />} /> + <Tab label='Label' icon={<Layers />} /> + <Tab label='Long Label' icon={<LocalOffer />} /> + </Tabs> + </Grid> + + <Grid item container direction='column' alignItems='flex-start'> + <Typography variant='body1'>{'Fill Text'}</Typography> + <Typography variant='body2'> + {'"variant=`fullWidth`" with custom width limit'} + </Typography> + + <Box style={{ width: '100%', maxWidth: '534px' }}> + <Tabs + {...args} + value={value2} + onChange={handleChange2} + aria-label='tabs example' + variant='fullWidth' + {...args} + > + <Tab label='Extra Long Label' icon={<Map />} /> + <Tab label='Label' icon={<Layers />} /> + <Tab label='Long Label' icon={<LocalOffer />} /> + </Tabs> + </Box> + </Grid> + </Grid> + ); +}; + +const commonArgs = { label: 'Label', disabled: true }; +const disabledControlsVerticalArgTypes = { + orientation: { table: { disable: true } }, + variant: { table: { disable: true } } +}; +const disabledControlsBehaviorArgTypes = { + label: { table: { disable: true } }, + wrapped: { table: { disable: true } }, + variant: { table: { disable: true } }, + disabled: { table: { disable: true } } +}; + export const Playground = Template.bind({}); -export const OnlyIcons = TemplateOnlyIcons.bind({}); -export const IconsLabels = TemplateIconsLabels.bind({}); +Playground.args = { ...commonArgs }; + +export const Icons = TemplateIcons.bind({}); + +export const IconsAndText = TemplateIconsAndText.bind({}); +IconsAndText.args = { ...commonArgs }; + +export const Text = TemplateText.bind({}); +Text.args = { ...commonArgs }; + +export const WrappedText = TemplateWrapped.bind({}); +WrappedText.args = { label: 'Wrapped Label used for a very long text', wrapped: true }; + +export const Vertical = TemplateVertical.bind({}); +Vertical.args = { ...commonArgs, orientation: 'vertical' }; +Vertical.argTypes = disabledControlsVerticalArgTypes; + +export const Behavior = BehaviorTemplate.bind({}); +Behavior.argTypes = disabledControlsBehaviorArgTypes;