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;