diff --git a/CHANGELOG.md b/CHANGELOG.md index f1ed0505b..7b81ee0a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Not released +- Breaking changes in Mui v5: styles and theme [#514](https://github.com/CartoDB/carto-react/pull/514/) - Add final Figma links into Storybook [#515](https://github.com/CartoDB/carto-react/pull/515) - Breakpoints for the new design system [#513](https://github.com/CartoDB/carto-react/pull/513/) - New Typography component to extend Mui Typography [#506](https://github.com/CartoDB/carto-react/pull/506) diff --git a/UPGRADE.md b/UPGRADE.md index f3d778d32..f5193cbe6 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,12 @@ # Upgrade to the new design system +## Breaking changes in Mui v5 + +Please, follow the Mui guides related to breaking changes in components and styles: + +- [Styles](https://mui.com/material-ui/migration/v5-style-changes/) +- [Components](https://mui.com/material-ui/migration/v5-component-changes/) + ## MUI theme [carto-theme.js](https://github.com/CartoDB/carto-react/blob/master/packages/react-ui/src/theme/carto-theme.js) file splitted in sections: @@ -14,6 +21,10 @@ Also added some files for shared constants (`themeConstants.js`) and useful func Removed unused custom `createTheme` function in `carto-theme.js`. +We have a new custom spacing constant in carto-theme, `spacingValue`, which you should use instead of the common `theme.spacing()` function in cases where you need to do value calculations, because since Mui v5, theme.spacing is no longer a number, but a string in this format: `number + px`. + +Note that if you're using `calc()` in your styles, you can keep using `theme.spacing()` as usual. + # Typography `responsiveFontSizes` simplified due we want to resize only a few variants through the theme. diff --git a/packages/react-ui/src/theme/carto-theme.js b/packages/react-ui/src/theme/carto-theme.js index 3eac76257..da070fd03 100644 --- a/packages/react-ui/src/theme/carto-theme.js +++ b/packages/react-ui/src/theme/carto-theme.js @@ -1,4 +1,7 @@ -import { themeComponentsOverrides, themeComponentsProps } from './sections/components'; +import { dataDisplayOverrides } from './sections/components/dataDisplay'; +import { buttonsOverrides } from './sections/components/buttons'; +import { formsOverrides } from './sections/components/forms'; +import { navigationOverrides } from './sections/components/navigation'; import { CssBaseline } from './sections/cssBaseline'; import { commonPalette } from './sections/palette'; import { themeShadows } from './sections/shadows'; @@ -99,13 +102,17 @@ export const cartoThemeOptions = { snackbar: 1400, tooltip: 1500 }, - overrides: { + + // Styles and props overrides for components + components: { MuiCssBaseline: { - ...CssBaseline + styleOverrides: { + ...CssBaseline + } }, - ...themeComponentsOverrides - }, - - // Props - props: { ...themeComponentsProps } + ...buttonsOverrides, + ...formsOverrides, + ...navigationOverrides, + ...dataDisplayOverrides + } }; diff --git a/packages/react-ui/src/theme/sections/components.js b/packages/react-ui/src/theme/sections/components.js deleted file mode 100644 index f1ceec53b..000000000 --- a/packages/react-ui/src/theme/sections/components.js +++ /dev/null @@ -1,902 +0,0 @@ -import { getSpacing } from '../themeUtils'; -import { commonPalette } from './palette'; -import { themeTypography } from './typography'; - -const round = (value) => Math.round(value * 1e5) / 1e5; -const pxToRem = (size) => `${round(size / themeTypography.htmlFontSize)}rem`; - -themeTypography.pxToRem = pxToRem; -themeTypography.round = round; - -export const themeComponentsOverrides = { - // Button - MuiButton: { - contained: { - boxShadow: 'none' - }, - outlined: { - border: `2px solid ${commonPalette.text.primary}`, - padding: '4px 14px', - '&:hover': { - borderWidth: '2px' - }, - '&$disabled': { - borderWidth: '2px' - } - }, - outlinedPrimary: { - border: `2px solid ${commonPalette.primary.main}`, - '&:hover': { - borderWidth: '2px' - } - }, - outlinedSecondary: { - border: `2px solid ${commonPalette.secondary.main}`, - '&:hover': { - borderWidth: '2px' - }, - '&$disabled': { - borderWidth: '2px' - } - }, - containedSizeSmall: { - padding: '2px 12px', - fontSize: pxToRem(12) - }, - outlinedSizeSmall: { - padding: '2px 12px', - fontSize: pxToRem(12) - }, - textSizeSmall: { - padding: '2px 12px', - fontSize: pxToRem(12) - }, - containedSizeLarge: { - padding: '16px 24px', - fontSize: pxToRem(16) - }, - containedSecondary: { - '&:hover': { - backgroundColor: commonPalette.secondary.light - } - }, - outlinedSizeLarge: { - padding: '16px 24px', - fontSize: pxToRem(16) - }, - textSizeLarge: { - padding: '16px 24px', - fontSize: pxToRem(16) - }, - startIcon: { - marginRight: 6, - marginLeft: -4, - '&$iconSizeSmall': { - marginLeft: -4 - }, - '&$iconSizeLarge': { - marginRight: 8 - } - }, - endIcon: { - marginRight: -4, - marginLeft: 6, - '&$iconSizeSmall': { - marginRight: -4 - }, - '&$iconSizeLarge': { - marginLeft: 8 - } - }, - iconSizeSmall: { - '& > *:first-child': { - fontSize: 20 - } - }, - iconSizeMedium: { - '& > *:first-child': { - fontSize: 24 - } - }, - iconSizeLarge: { - '& > *:first-child': { - fontSize: 24 - } - } - }, - MuiIconButton: { - root: { - padding: getSpacing(0.75), - borderRadius: getSpacing(0.5), - color: commonPalette.text.primary - }, - sizeSmall: { - padding: getSpacing(0.25) - } - }, - - MuiInputBase: { - root: { - '&$disabled .MuiInputAdornment-root': { - color: commonPalette.action.disabled - }, - '&$disabled .MuiTypography-root': { - color: commonPalette.action.disabled - } - } - }, - MuiOutlinedInput: { - root: { - '&$disabled': { - backgroundColor: commonPalette.action.hover - } - }, - - input: { - ...themeTypography.body1, - height: `${themeTypography.body1.lineHeight}em`, - padding: getSpacing(3, 2, 1) - }, - - inputMarginDense: { - ...themeTypography.body2, - height: `${themeTypography.body2.lineHeight}em`, - padding: getSpacing(1, 1.5), - paddingTop: getSpacing(1), - paddingBottom: getSpacing(1) - }, - - adornedStart: { - '&$marginDense': { - paddingLeft: getSpacing(1.5) - } - }, - adornedEnd: { - '&$marginDense': { - paddingRight: getSpacing(1.5) - } - }, - - notchedOutline: { - border: `2px solid ${commonPalette.text.disabled}` - }, - - multiline: { - padding: getSpacing(2.75, 2, 1.25) - } - }, - MuiInputLabel: { - root: { - ...themeTypography.body1 - }, - - formControl: { - transform: 'translate(16px, 20px) scale(1)', - - '&$shrink': { - ...themeTypography.caption, - transform: 'translate(16px, 8px) scale(1)' - }, - - '&$marginDense': { - ...themeTypography.caption, - transform: 'translate(0, -20px) scale(1)', - - '&$shrink': { - ...themeTypography.caption, - transform: 'translate(0, -20px) scale(1)' - } - } - }, - - outlined: { - '&$shrink': { - ...themeTypography.caption, - transform: 'translate(16px, 8px) scale(1)' - }, - - '&$marginDense': { - ...themeTypography.caption, - transform: 'translate(0, -20px) scale(1)', - - '&$shrink': { - transform: 'translate(0, -20px) scale(1)' - } - } - } - }, - MuiInputAdornment: { - root: { - ...themeTypography.body1, - alignItems: 'baseline', - marginBottom: getSpacing(1.5), - color: commonPalette.text.secondary, - - '&:disabled': { - color: commonPalette.action.disabled - }, - - '& .MuiSvgIcon-root': { - fontSize: `${themeTypography.body1.lineHeight}em` - } - }, - - positionStart: { - marginLeft: getSpacing(0.25) - }, - - positionEnd: { - marginRight: getSpacing(0.25) - }, - - marginDense: { - marginBottom: getSpacing(0), - alignItems: 'center', - ...themeTypography.body2, - - '& .MuiTypography-root': { - ...themeTypography.body2 - }, - - '& .MuiSvgIcon-root': { - fontSize: `${themeTypography.body2.lineHeight}em` - } - } - }, - MuiFormHelperText: { - root: { - ...themeTypography.caption, - '&$contained': { - marginTop: getSpacing(1) - } - }, - - marginDense: { - '&$contained': { - marginLeft: getSpacing(0) - } - } - }, - - // Select - MuiFormControl: { - root: { - width: '100%' - } - }, - MuiSelect: { - selectMenu: {}, - - root: { - '&:hover': { - backgroundColor: 'transparent' - } - }, - - select: { - '&:focus': { - backgroundColor: 'transparent' - } - } - }, - - // Menu - MuiMenuItem: { - root: { - ...themeTypography.body2 - } - }, - - // Autocomplete - MuiAutocomplete: { - inputRoot: { - '&[class*="MuiOutlinedInput-root"]': { - padding: getSpacing(3, 1.25, 0.5), - - '& .MuiAutocomplete-input': { - padding: getSpacing(0, 1.25, 0.5) - } - }, - '&.MuiInputBase-marginDense.MuiOutlinedInput-root $input.MuiOutlinedInput-inputMarginDense': - { - paddingTop: getSpacing(0.25), - paddingBottom: getSpacing(0.25) - } - } - }, - - // Checkbox - MuiCheckbox: { - root: { - ...themeTypography.body2, - padding: getSpacing(0.75), - borderRadius: '50%', - - '& + .MuiFormControlLabel-label': { - ...themeTypography.body2, - marginLeft: getSpacing(0.25), - color: commonPalette.text.primary - }, - - '& .MuiSvgIcon-root': { - fontSize: getSpacing(3) - } - } - }, - - // RadioButton - MuiRadio: { - root: { - ...themeTypography.body2, - padding: getSpacing(0.75), - borderRadius: '50%', - - '& + .MuiFormControlLabel-label': { - ...themeTypography.body2, - marginLeft: getSpacing(0.25), - color: commonPalette.text.primary - }, - - '& .MuiSvgIcon-root': { - fontSize: getSpacing(3) - } - } - }, - - // Tabs - MuiTabs: { - indicator: { - height: 4, - '&.colorPrimary': { - backgroundColor: commonPalette.text.primary - } - }, - - vertical: { - '& $indicator': { - width: 4 - }, - - '& .MuiTab-root': { - padding: getSpacing(0, 2), - - '& .MuiTab-wrapper': { - alignItems: 'flex-start' - } - } - } - }, - - // Tab - MuiTab: { - root: { - padding: getSpacing(0, 1), - marginRight: getSpacing(3), - minWidth: '56px!important', - '&[class*="MuiTab-labelIcon"] .MuiTab-wrapper': { - flexFlow: 'row', - alignItems: 'center' - }, - '&[class*="MuiTab-labelIcon"] .MuiTab-wrapper > .MuiSvgIcon-root': { - marginRight: getSpacing(1), - marginBottom: 0 - } - }, - textColorPrimary: { - color: commonPalette.primary.main, - opacity: 1, - '&$selected': { - color: commonPalette.text.primary - }, - '&$disabled': { - color: commonPalette.action.disabled - } - } - }, - - MuiDivider: { - root: { - backgroundColor: commonPalette.divider - }, - light: { - backgroundColor: commonPalette.grey[50] - } - }, - - // Switch - MuiSwitch: { - root: { - height: getSpacing(4.5), - width: getSpacing(6), - padding: getSpacing(1), - overflow: 'visible', - - '& + .MuiFormControlLabel-label': { - ...themeTypography.body2, - marginLeft: getSpacing(0.25), - color: commonPalette.text.primary - } - }, - - switchBase: { - padding: getSpacing(1.5), - borderRadius: '50%', - transform: 'translate(1px, 1px)', - color: commonPalette.text.secondary, - - '&$checked': { - '& input': { - left: getSpacing(-1.5) - }, - - transform: 'translate(13px, 1px)', - color: commonPalette.common.white, - - '& + $track': { - opacity: 1 - } - } - }, - - thumb: { - width: getSpacing(1.25), - height: getSpacing(1.25), - boxShadow: 'none' - }, - - input: { - width: getSpacing(6), - left: 0 - }, - - track: { - height: 'auto', - border: `2px solid ${commonPalette.text.secondary}`, - borderRadius: getSpacing(2), - opacity: 1, - backgroundColor: commonPalette.common.white - }, - - colorPrimary: { - '&$checked': { - color: commonPalette.common.white, - - '& + $track': { - backgroundColor: commonPalette.primary.main, - borderColor: 'transparent' - }, - - '&$disabled': { - color: commonPalette.grey[100], - - '& + $track': { - backgroundColor: commonPalette.text.disabled - } - } - }, - - '&$disabled': { - color: commonPalette.text.disabled, - - '& + $track': { - opacity: 1, - backgroundColor: commonPalette.common.white, - borderColor: commonPalette.text.disabled - } - } - }, - - colorSecondary: { - '&$checked': { - color: commonPalette.common.white, - - '& + $track': { - backgroundColor: commonPalette.secondary.main, - borderColor: 'transparent' - }, - - '&$disabled': { - color: commonPalette.grey[100], - - '& + $track': { - backgroundColor: commonPalette.text.disabled - } - } - }, - - '&$disabled': { - color: commonPalette.text.disabled, - - '& + $track': { - opacity: 1, - backgroundColor: commonPalette.common.white, - borderColor: commonPalette.text.disabled - } - } - }, - - sizeSmall: { - height: getSpacing(4.5), - width: getSpacing(6), - padding: getSpacing(1), - - '& $switchBase': { - padding: getSpacing(1.5), - transform: 'translate(0, 1px)', - - '&$checked': { - transform: 'translate(15px, 1px)' - } - }, - '& $thumb': { - width: getSpacing(1.25), - height: getSpacing(1.25) - } - } - }, - - // Breadcrumbs - MuiBreadcrumbs: { - li: { - '& .MuiTypography-root': { - ...themeTypography.body2, - display: 'flex', - flexDirection: 'row', - alignItems: 'center' - }, - '& .MuiSvgIcon-root': { - fontSize: `${themeTypography.body2.lineHeight}em`, - marginRight: getSpacing(1) - } - }, - - separator: { - marginLeft: getSpacing(0.5), - marginRight: getSpacing(0.5) - } - }, - - // Lists - MuiList: { - root: { - // Indent sublevels, ugly but needed to avoid issues with hover - '& .MuiList-root': { - '& .MuiListItem-root': { - paddingLeft: getSpacing(4) - }, - - '& .MuiList-root': { - '& .MuiListItem-root': { - paddingLeft: getSpacing(6) - }, - - '& .MuiList-root': { - '& .MuiListItem-root': { - paddingLeft: getSpacing(8) - }, - - '& .MuiList-root': { - '& .MuiListItem-root': { - paddingLeft: getSpacing(10) - } - } - } - } - } - } - }, - - MuiListItemIcon: { - root: { - minWidth: getSpacing(5.75), - marginLeft: getSpacing(0.75), - - '& .MuiSvgIcon-root': { - fontSize: getSpacing(3) - } - } - }, - - MuiListItemAvatar: { - root: { - '& .MuiAvatar-root': { - height: getSpacing(4.5), - width: getSpacing(4.5) - }, - '& .MuiSvgIcon-root': { - fontSize: getSpacing(2.5) - } - } - }, - - // Tooltip - MuiTooltip: { - tooltip: { - ...themeTypography.caption, - backgroundColor: commonPalette.black[90] - }, - - arrow: { - color: commonPalette.black[90] - } - }, - - // Dialog - MuiDialogTitle: { - root: { - padding: getSpacing(3, 3, 2) - } - }, - - MuiDialogContent: { - root: { - '& .MuiFormGroup-root': { - padding: getSpacing(1, 0) - } - } - }, - - // Slider - MuiSlider: { - root: {} - }, - - // MuiToggleButtonGroup - MuiToggleButtonGroup: { - groupedHorizontal: { - '&:not(:last-child)': { - marginRight: getSpacing(0.25), - borderTopRightRadius: getSpacing(0.5), - borderBottomRightRadius: getSpacing(0.5) - }, - '&:not(:first-child)': { - marginLeft: 0, - borderLeft: 'none', - borderTopLeftRadius: getSpacing(0.5), - borderBottomLeftRadius: getSpacing(0.5) - } - }, - groupedVertical: { - '&:not(:last-child)': { - marginBottom: getSpacing(0.25), - borderBottomLeftRadius: getSpacing(0.5), - borderBottomRightRadius: getSpacing(0.5) - }, - '&:not(:first-child)': { - borderTopLeftRadius: getSpacing(0.5), - borderTopRightRadius: getSpacing(0.5) - } - } - }, - - MuiTablePagination: { - select: { - paddingRight: getSpacing(7.5), - paddingLeft: getSpacing(1.5) - }, - input: { - height: getSpacing(4), - border: `2px solid ${commonPalette.divider}`, - borderRadius: getSpacing(0.5), - fontWeight: themeTypography.fontWeightMedium, - '& .MuiSelect-icon': { - top: '50%', - transform: 'translateY(-50%)', - width: getSpacing(2.25), - height: getSpacing(2.25), - right: getSpacing(0.75) - } - }, - caption: { - ...themeTypography.caption, - '&:first-of-type': { - color: commonPalette.text.secondary - } - }, - toolbar: { - minHeight: 0, - marginTop: getSpacing(1) - }, - actions: { - '& button:last-child': { - marginLeft: getSpacing(2) - } - } - }, - - MuiTableCell: { - head: { - ...themeTypography.caption, - color: commonPalette.text.secondary - }, - stickyHeader: { - backgroundColor: commonPalette.common.white - } - }, - - // MuiToggleButton - MuiToggleButton: { - root: { - width: getSpacing(4.5), - height: getSpacing(4.5), - border: 'none', - borderRadius: getSpacing(0.5), - color: commonPalette.grey[500], - '&$selected': { - color: commonPalette.primary.main, - backgroundColor: commonPalette.primary.background, - '&:hover': { - backgroundColor: commonPalette.primary.background - } - } - }, - sizeSmall: { - width: getSpacing(3), - height: getSpacing(3), - '& .MuiSvgIcon-root': { - maxWidth: getSpacing(2.5), - maxHeight: getSpacing(2.5) - } - }, - sizeLarge: { - width: getSpacing(7), - height: getSpacing(7) - } - }, - - MuiChip: { - root: { - backgroundColor: commonPalette.grey[100], - '&:hover': { - backgroundColor: commonPalette.grey[200] - }, - '& .MuiAvatar-root': { - backgroundColor: '#7f3c8d', - color: commonPalette.common.white - } - }, - colorPrimary: { - '&$disabled': { - backgroundColor: commonPalette.grey[100], - color: commonPalette.text.primary - }, - '&:hover': { - backgroundColor: commonPalette.primary.dark - } - }, - colorSecondary: { - '&$disabled': { - backgroundColor: commonPalette.grey[100] - }, - '&:hover': { - backgroundColor: commonPalette.secondary.light - } - }, - label: { - fontFamily: '"Open Sans", sans-serif', - letterSpacing: 0.25 - }, - labelSmall: { - fontSize: themeTypography.caption.fontSize, - fontWeight: themeTypography.fontWeightLight - }, - outlined: { - transition: `border-color 250ms cubic-bezier(0.4, 0, 0.2, 1), color 250ms cubic-bezier(0.4, 0, 0.2, 1)`, - '&$disabled': { - backgroundColor: 'transparent' - }, - '&:hover': { - backgroundColor: 'transparent', - borderColor: commonPalette.grey[200], - '&$clickable': { - backgroundColor: 'transparent' - } - } - }, - outlinedPrimary: { - '&:hover': { - backgroundColor: 'transparent', - borderColor: commonPalette.primary.dark, - color: commonPalette.primary.dark, - '&$clickable': { - backgroundColor: 'transparent' - } - } - }, - outlinedSecondary: { - '&:hover': { - backgroundColor: 'transparent', - borderColor: commonPalette.secondary.dark, - color: commonPalette.secondary.dark, - '&$clickable': { - backgroundColor: 'transparent' - } - } - }, - clickable: { - '&:focus': { - webkitTapHighlightColor: 'none' - } - } - } -}; - -export const themeComponentsProps = { - MuiButtonBase: { - disableRipple: true - }, - MuiButton: { - disableElevation: true - }, - MuiTextField: { - variant: 'outlined' - }, - MuiSelect: { - variant: 'outlined', - MenuProps: { - getContentAnchorEl: null, - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left' - } - } - }, - MuiOutlinedInput: { - notched: false - }, - MuiCheckbox: { - size: 'small', - color: 'primary' - }, - MuiRadio: { - size: 'small', - color: 'primary' - }, - MuiSwitch: { - color: 'primary' - }, - MuiInputAdornment: { - disableTypography: true - }, - MuiListItemText: { - primaryTypographyProps: { - variant: 'body2', - style: { fontWeight: themeTypography.fontWeightBold }, - noWrap: true - }, - secondaryTypographyProps: { variant: 'caption' } - }, - MuiSkeleton: { - animation: 'wave' - }, - MuiTabs: { - indicatorColor: 'primary', - textColor: 'primary', - TabIndicatorProps: { - classes: { - colorPrimary: 'colorPrimary' - } - } - }, - MuiTypography: { - color: 'textPrimary' - }, - MuiDialogContentText: { - variant: 'body2' - }, - MuiToggleButtonGroup: { - orientation: 'horizontal', - exclusive: true - }, - CircularProgress: { - size: 40, - thickness: 4 - }, - MuiSlider: { - color: 'primary', - marks: false - }, - MuiDialog: { - maxWidth: 'md' - } -}; diff --git a/packages/react-ui/src/theme/sections/components/buttons.js b/packages/react-ui/src/theme/sections/components/buttons.js new file mode 100644 index 000000000..7be93add4 --- /dev/null +++ b/packages/react-ui/src/theme/sections/components/buttons.js @@ -0,0 +1,194 @@ +import { getPixelToRem, getSpacing } from '../../themeUtils'; +import { commonPalette } from '../palette'; + +export const buttonsOverrides = { + // Button + MuiButton: { + defaultProps: { + disableElevation: true + }, + + styleOverrides: { + contained: { + boxShadow: 'none' + }, + outlined: { + border: `2px solid ${commonPalette.text.primary}`, + padding: '4px 14px', + '&:hover': { + borderWidth: '2px' + }, + '&.Mui-disabled': { + borderWidth: '2px' + } + }, + outlinedPrimary: { + border: `2px solid ${commonPalette.primary.main}`, + '&:hover': { + borderWidth: '2px' + } + }, + outlinedSecondary: { + border: `2px solid ${commonPalette.secondary.main}`, + '&:hover': { + borderWidth: '2px' + }, + '&.Mui-disabled': { + borderWidth: '2px' + } + }, + containedSizeSmall: { + padding: '2px 12px', + fontSize: getPixelToRem(12) + }, + outlinedSizeSmall: { + padding: '2px 12px', + fontSize: getPixelToRem(12) + }, + textSizeSmall: { + padding: '2px 12px', + fontSize: getPixelToRem(12) + }, + containedSizeLarge: { + padding: '16px 24px', + fontSize: getPixelToRem(16) + }, + containedSecondary: { + '&:hover': { + backgroundColor: commonPalette.secondary.light + } + }, + outlinedSizeLarge: { + padding: '16px 24px', + fontSize: getPixelToRem(16) + }, + textSizeLarge: { + padding: '16px 24px', + fontSize: getPixelToRem(16) + }, + startIcon: { + marginRight: 6, + marginLeft: -4, + '&.MuiButton-iconSizeSmall': { + marginLeft: -4 + }, + '&.MuiButton-iconSizeLarge': { + marginRight: 8 + } + }, + endIcon: { + marginRight: -4, + marginLeft: 6, + '&.MuiButton-iconSizeSmall': { + marginRight: -4 + }, + '&.MuiButton-iconSizeLarge': { + marginLeft: 8 + } + }, + iconSizeSmall: { + '& > *:first-child': { + fontSize: 20 + } + }, + iconSizeMedium: { + '& > *:first-child': { + fontSize: 24 + } + }, + iconSizeLarge: { + '& > *:first-child': { + fontSize: 24 + } + } + } + }, + + // Button Base + MuiButtonBase: { + defaultProps: { + disableRipple: true + } + }, + + // Icon Button + MuiIconButton: { + styleOverrides: { + root: { + padding: getSpacing(0.75), + borderRadius: getSpacing(0.5), + color: commonPalette.text.primary + }, + sizeSmall: { + padding: getSpacing(0.25) + } + } + }, + + // MuiToggleButton + MuiToggleButton: { + styleOverrides: { + root: { + width: getSpacing(4.5), + height: getSpacing(4.5), + border: 'none', + borderRadius: getSpacing(0.5), + color: commonPalette.grey[500], + '&.Mui-selected': { + color: commonPalette.primary.main, + backgroundColor: commonPalette.primary.background, + '&:hover': { + backgroundColor: commonPalette.primary.background + } + } + }, + sizeSmall: { + width: getSpacing(3), + height: getSpacing(3), + '& .MuiSvgIcon-root': { + maxWidth: getSpacing(2.5), + maxHeight: getSpacing(2.5) + } + }, + sizeLarge: { + width: getSpacing(7), + height: getSpacing(7) + } + } + }, + + // MuiToggleButtonGroup + MuiToggleButtonGroup: { + defaultProps: { + orientation: 'horizontal', + exclusive: true + }, + + styleOverrides: { + groupedHorizontal: { + '&:not(:last-child)': { + marginRight: getSpacing(0.25), + borderTopRightRadius: getSpacing(0.5), + borderBottomRightRadius: getSpacing(0.5) + }, + '&:not(:first-child)': { + marginLeft: 0, + borderLeft: 'none', + borderTopLeftRadius: getSpacing(0.5), + borderBottomLeftRadius: getSpacing(0.5) + } + }, + groupedVertical: { + '&:not(:last-child)': { + marginBottom: getSpacing(0.25), + borderBottomLeftRadius: getSpacing(0.5), + borderBottomRightRadius: getSpacing(0.5) + }, + '&:not(:first-child)': { + borderTopLeftRadius: getSpacing(0.5), + borderTopRightRadius: getSpacing(0.5) + } + } + } + } +}; diff --git a/packages/react-ui/src/theme/sections/components/dataDisplay.js b/packages/react-ui/src/theme/sections/components/dataDisplay.js new file mode 100644 index 000000000..64cf0fa3d --- /dev/null +++ b/packages/react-ui/src/theme/sections/components/dataDisplay.js @@ -0,0 +1,270 @@ +import { getSpacing } from '../../themeUtils'; +import { commonPalette } from '../palette'; +import { themeTypography } from '../typography'; + +export const dataDisplayOverrides = { + // Divider + MuiDivider: { + styleOverrides: { + root: { + backgroundColor: commonPalette.divider + }, + light: { + backgroundColor: commonPalette.grey[50] + } + } + }, + + // List + MuiList: { + styleOverrides: { + root: { + // Indent sublevels, ugly but needed to avoid issues with hover + '& .MuiList-root': { + '& .MuiListItem-root': { + paddingLeft: getSpacing(4) + }, + + '& .MuiList-root': { + '& .MuiListItem-root': { + paddingLeft: getSpacing(6) + }, + + '& .MuiList-root': { + '& .MuiListItem-root': { + paddingLeft: getSpacing(8) + }, + + '& .MuiList-root': { + '& .MuiListItem-root': { + paddingLeft: getSpacing(10) + } + } + } + } + } + } + } + }, + + // List Item + MuiListItemText: { + defaultProps: { + primaryTypographyProps: { + variant: 'body2', + style: { fontWeight: themeTypography.fontWeightBold }, + noWrap: true + }, + secondaryTypographyProps: { variant: 'caption' } + } + }, + MuiListItemIcon: { + styleOverrides: { + root: { + minWidth: getSpacing(5.75), + marginLeft: getSpacing(0.75), + + '& .MuiSvgIcon-root': { + fontSize: getSpacing(3) + } + } + } + }, + MuiListItemAvatar: { + styleOverrides: { + root: { + '& .MuiAvatar-root': { + height: getSpacing(4.5), + width: getSpacing(4.5) + }, + '& .MuiSvgIcon-root': { + fontSize: getSpacing(2.5) + } + } + } + }, + + // Tooltip + MuiTooltip: { + styleOverrides: { + tooltip: { + ...themeTypography.caption, + backgroundColor: commonPalette.black[90] + }, + + arrow: { + color: commonPalette.black[90] + } + } + }, + + // Dialog + MuiDialog: { + defaultProps: { + maxWidth: 'md' + } + }, + MuiDialogTitle: { + styleOverrides: { + root: { + padding: getSpacing(3, 3, 2) + } + } + }, + MuiDialogContent: { + styleOverrides: { + root: { + '& .MuiFormGroup-root': { + padding: getSpacing(1, 0) + } + } + } + }, + MuiDialogContentText: { + defaultProps: { + variant: 'body2' + } + }, + + // Table + MuiTablePagination: { + styleOverrides: { + select: { + paddingRight: getSpacing(7.5), + paddingLeft: getSpacing(1.5) + }, + input: { + height: getSpacing(4), + border: `2px solid ${commonPalette.divider}`, + borderRadius: getSpacing(0.5), + fontWeight: themeTypography.fontWeightMedium, + '& .MuiSelect-icon': { + top: '50%', + transform: 'translateY(-50%)', + width: getSpacing(2.25), + height: getSpacing(2.25), + right: getSpacing(0.75) + } + }, + caption: { + ...themeTypography.caption, + '&:first-of-type': { + color: commonPalette.text.secondary + } + }, + toolbar: { + minHeight: 0, + marginTop: getSpacing(1) + }, + actions: { + '& button:last-child': { + marginLeft: getSpacing(2) + } + } + } + }, + MuiTableCell: { + styleOverrides: { + head: { + ...themeTypography.caption, + color: commonPalette.text.secondary + }, + stickyHeader: { + backgroundColor: commonPalette.common.white + } + } + }, + + // Chip + MuiChip: { + styleOverrides: { + root: { + backgroundColor: commonPalette.grey[100], + '&:hover': { + backgroundColor: commonPalette.grey[200] + }, + '& .MuiAvatar-root': { + backgroundColor: '#7f3c8d', + color: commonPalette.common.white + } + }, + colorPrimary: { + '&.Mui-disabled': { + backgroundColor: commonPalette.grey[100], + color: commonPalette.text.primary + }, + '&:hover': { + backgroundColor: commonPalette.primary.dark + } + }, + colorSecondary: { + '&.Mui-disabled': { + backgroundColor: commonPalette.grey[100] + }, + '&:hover': { + backgroundColor: commonPalette.secondary.light + } + }, + label: { + fontFamily: '"Open Sans", sans-serif', + letterSpacing: 0.25 + }, + labelSmall: { + fontSize: themeTypography.caption.fontSize, + fontWeight: themeTypography.fontWeightLight + }, + outlined: { + transition: `border-color 250ms cubic-bezier(0.4, 0, 0.2, 1), color 250ms cubic-bezier(0.4, 0, 0.2, 1)`, + '&.Mui-disabled': { + backgroundColor: 'transparent' + }, + '&:hover': { + backgroundColor: 'transparent', + borderColor: commonPalette.grey[200], + '&.MuiChip-clickable': { + backgroundColor: 'transparent' + } + } + }, + outlinedPrimary: { + '&:hover': { + backgroundColor: 'transparent', + borderColor: commonPalette.primary.dark, + color: commonPalette.primary.dark, + '&.MuiChip-clickable': { + backgroundColor: 'transparent' + } + } + }, + outlinedSecondary: { + '&:hover': { + backgroundColor: 'transparent', + borderColor: commonPalette.secondary.dark, + color: commonPalette.secondary.dark, + '&.MuiChip-clickable': { + backgroundColor: 'transparent' + } + } + }, + clickable: { + '&:focus': { + webkitTapHighlightColor: 'none' + } + } + } + }, + + // Skeleton + MuiSkeleton: { + defaultProps: { + animation: 'wave' + } + }, + + // Typography + MuiTypography: { + defaultProps: { + color: 'textPrimary' + } + } +}; diff --git a/packages/react-ui/src/theme/sections/components/forms.js b/packages/react-ui/src/theme/sections/components/forms.js new file mode 100644 index 000000000..f92f21517 --- /dev/null +++ b/packages/react-ui/src/theme/sections/components/forms.js @@ -0,0 +1,440 @@ +import { getSpacing } from '../../themeUtils'; +import { commonPalette } from '../palette'; +import { themeTypography } from '../typography'; + +export const formsOverrides = { + // Input + MuiInputBase: { + styleOverrides: { + root: { + '&.Mui-disabled .MuiInputAdornment-root': { + color: commonPalette.action.disabled + }, + '&.Mui-disabled .MuiTypography-root': { + color: commonPalette.action.disabled + } + } + } + }, + + // Outlined Input + MuiOutlinedInput: { + defaultProps: { + notched: false + }, + styleOverrides: { + root: { + '&.Mui-disabled': { + backgroundColor: commonPalette.action.hover + } + }, + + input: { + ...themeTypography.body1, + height: `${themeTypography.body1.lineHeight}em`, + padding: getSpacing(3, 2, 1) + }, + + inputMarginDense: { + ...themeTypography.body2, + height: `${themeTypography.body2.lineHeight}em`, + padding: getSpacing(1, 1.5), + paddingTop: getSpacing(1), + paddingBottom: getSpacing(1) + }, + + adornedStart: { + '&.MuiFormControl-marginDense': { + paddingLeft: getSpacing(1.5) + } + }, + adornedEnd: { + '&.MuiFormControl-marginDense': { + paddingRight: getSpacing(1.5) + } + }, + + notchedOutline: { + border: `2px solid ${commonPalette.text.disabled}` + }, + + multiline: { + padding: getSpacing(2.75, 2, 1.25) + } + } + }, + + // Label + MuiInputLabel: { + styleOverrides: { + root: { + ...themeTypography.body1 + }, + + formControl: { + transform: 'translate(16px, 20px) scale(1)', + + '&.MuiInputLabel-shrink': { + ...themeTypography.caption, + transform: 'translate(16px, 8px) scale(1)' + }, + + '&.MuiFormControl-marginDense': { + ...themeTypography.caption, + transform: 'translate(0, -20px) scale(1)', + + '&.MuiInputLabel-shrink': { + ...themeTypography.caption, + transform: 'translate(0, -20px) scale(1)' + } + } + }, + + outlined: { + '&.MuiInputLabel-shrink': { + ...themeTypography.caption, + transform: 'translate(16px, 8px) scale(1)' + }, + + '&.MuiFormControl-marginDense': { + ...themeTypography.caption, + transform: 'translate(0, -20px) scale(1)', + + '&.MuiInputLabel-shrink': { + transform: 'translate(0, -20px) scale(1)' + } + } + } + } + }, + + // Input Adornment + MuiInputAdornment: { + defaultProps: { + disableTypography: true + }, + + styleOverrides: { + root: { + ...themeTypography.body1, + alignItems: 'baseline', + marginBottom: getSpacing(1.5), + color: commonPalette.text.secondary, + + '&:disabled': { + color: commonPalette.action.disabled + }, + + '& .MuiSvgIcon-root': { + fontSize: `${themeTypography.body1.lineHeight}em` + } + }, + + positionStart: { + marginLeft: getSpacing(0.25) + }, + + positionEnd: { + marginRight: getSpacing(0.25) + }, + + marginDense: { + marginBottom: getSpacing(0), + alignItems: 'center', + ...themeTypography.body2, + + '& .MuiTypography-root': { + ...themeTypography.body2 + }, + + '& .MuiSvgIcon-root': { + fontSize: `${themeTypography.body2.lineHeight}em` + } + } + } + }, + + // Text Field + MuiTextField: { + defaultProps: { + variant: 'outlined' + } + }, + + // Form Helper Text + MuiFormHelperText: { + styleOverrides: { + root: { + ...themeTypography.caption, + '&.MuiFormHelperText-contained': { + marginTop: getSpacing(1) + } + }, + + marginDense: { + '&.MuiFormHelperText-contained': { + marginLeft: getSpacing(0) + } + } + } + }, + + // Form Control + MuiFormControl: { + styleOverrides: { + root: { + width: '100%' + } + } + }, + + // Select + MuiSelect: { + defaultProps: { + variant: 'outlined', + MenuProps: { + getContentAnchorEl: null, + anchorOrigin: { + vertical: 'bottom', + horizontal: 'left' + } + } + }, + styleOverrides: { + root: { + '&:hover': { + backgroundColor: 'transparent' + } + }, + + select: { + '&:focus': { + backgroundColor: 'transparent' + } + } + } + }, + + // Autocomplete + MuiAutocomplete: { + styleOverrides: { + inputRoot: { + '&[class*="MuiOutlinedInput-root"]': { + padding: getSpacing(3, 1.25, 0.5), + + '& .MuiAutocomplete-input': { + padding: getSpacing(0, 1.25, 0.5) + } + }, + '&.MuiInputBase-marginDense.MuiOutlinedInput-root .MuiInputBase-input.MuiOutlinedInput-inputMarginDense': + { + paddingTop: getSpacing(0.25), + paddingBottom: getSpacing(0.25) + } + } + } + }, + + // Checkbox + MuiCheckbox: { + defaultProps: { + size: 'small', + color: 'primary' + }, + styleOverrides: { + root: { + ...themeTypography.body2, + padding: getSpacing(0.75), + borderRadius: '50%', + + '& + .MuiFormControlLabel-label': { + ...themeTypography.body2, + marginLeft: getSpacing(0.25), + color: commonPalette.text.primary + }, + + '& .MuiSvgIcon-root': { + fontSize: getSpacing(3) + } + } + } + }, + + // Radio Button + MuiRadio: { + defaultProps: { + size: 'small', + color: 'primary' + }, + styleOverrides: { + root: { + ...themeTypography.body2, + padding: getSpacing(0.75), + borderRadius: '50%', + + '& + .MuiFormControlLabel-label': { + ...themeTypography.body2, + marginLeft: getSpacing(0.25), + color: commonPalette.text.primary + }, + + '& .MuiSvgIcon-root': { + fontSize: getSpacing(3) + } + } + } + }, + + // Switch + MuiSwitch: { + defaultProps: { + color: 'primary' + }, + styleOverrides: { + root: { + height: getSpacing(4.5), + width: getSpacing(6), + padding: getSpacing(1), + overflow: 'visible', + + '& + .MuiFormControlLabel-label': { + ...themeTypography.body2, + marginLeft: getSpacing(0.25), + color: commonPalette.text.primary + } + }, + + switchBase: { + padding: getSpacing(1.5), + borderRadius: '50%', + transform: 'translate(1px, 1px)', + color: commonPalette.text.secondary, + + '&.Mui-checked': { + '& input': { + left: getSpacing(-1.5) + }, + + transform: 'translate(13px, 1px)', + color: commonPalette.common.white, + + '& + .MuiSwitch-track': { + opacity: 1 + } + } + }, + + thumb: { + width: getSpacing(1.25), + height: getSpacing(1.25), + boxShadow: 'none' + }, + + input: { + width: getSpacing(6), + left: 0 + }, + + track: { + height: 'auto', + border: `2px solid ${commonPalette.text.secondary}`, + borderRadius: getSpacing(2), + opacity: 1, + backgroundColor: commonPalette.common.white + }, + + colorPrimary: { + '&.Mui-checked': { + color: commonPalette.common.white, + + '& + .MuiSwitch-track': { + backgroundColor: commonPalette.primary.main, + borderColor: 'transparent' + }, + + '&.Mui-disabled': { + color: commonPalette.grey[100], + + '& + .MuiSwitch-track': { + backgroundColor: commonPalette.text.disabled + } + } + }, + + '&.Mui-disabled': { + color: commonPalette.text.disabled, + + '& + .MuiSwitch-track': { + opacity: 1, + backgroundColor: commonPalette.common.white, + borderColor: commonPalette.text.disabled + } + } + }, + + colorSecondary: { + '&.Mui-checked': { + color: commonPalette.common.white, + + '& + .MuiSwitch-track': { + backgroundColor: commonPalette.secondary.main, + borderColor: 'transparent' + }, + + '&.Mui-disabled': { + color: commonPalette.grey[100], + + '& + .MuiSwitch-track': { + backgroundColor: commonPalette.text.disabled + } + } + }, + + '&.Mui-disabled': { + color: commonPalette.text.disabled, + + '& + .MuiSwitch-track': { + opacity: 1, + backgroundColor: commonPalette.common.white, + borderColor: commonPalette.text.disabled + } + } + }, + + sizeSmall: { + height: getSpacing(4.5), + width: getSpacing(6), + padding: getSpacing(1), + + '& .MuiSwitch-switchBase': { + padding: getSpacing(1.5), + transform: 'translate(0, 1px)', + + '&.Mui-checked': { + transform: 'translate(15px, 1px)' + } + }, + '& .MuiSwitch-thumb': { + width: getSpacing(1.25), + height: getSpacing(1.25) + } + } + } + }, + + // Circular Progress + CircularProgress: { + defaultProps: { + size: 40, + thickness: 4 + } + }, + + // Slider + MuiSlider: { + defaultProps: { + color: 'primary', + marks: false + } + } +}; diff --git a/packages/react-ui/src/theme/sections/components/navigation.js b/packages/react-ui/src/theme/sections/components/navigation.js new file mode 100644 index 000000000..75f07a58a --- /dev/null +++ b/packages/react-ui/src/theme/sections/components/navigation.js @@ -0,0 +1,101 @@ +import { getSpacing } from '../../themeUtils'; +import { commonPalette } from '../palette'; +import { themeTypography } from '../typography'; + +export const navigationOverrides = { + // Menu + MuiMenuItem: { + styleOverrides: { + root: { + ...themeTypography.body2 + } + } + }, + + // Tabs + MuiTabs: { + defaultProps: { + indicatorColor: 'primary', + textColor: 'primary', + TabIndicatorProps: { + classes: { + colorPrimary: 'colorPrimary' + } + } + }, + styleOverrides: { + indicator: { + height: 4, + '&.colorPrimary': { + backgroundColor: commonPalette.text.primary + } + }, + + vertical: { + '& .MuiTabs-indicator': { + width: 4 + }, + + '& .MuiTab-root': { + padding: getSpacing(0, 2), + + '& .MuiTab-wrapper': { + alignItems: 'flex-start' + } + } + } + } + }, + + // Tab + MuiTab: { + styleOverrides: { + root: { + padding: getSpacing(0, 1), + marginRight: getSpacing(3), + minWidth: '56px!important', + '&[class*="MuiTab-labelIcon"] .MuiTab-wrapper': { + flexFlow: 'row', + alignItems: 'center' + }, + '&[class*="MuiTab-labelIcon"] .MuiTab-wrapper > .MuiSvgIcon-root': { + marginRight: getSpacing(1), + marginBottom: 0 + } + }, + textColorPrimary: { + color: commonPalette.primary.main, + opacity: 1, + '&.Mui-selected': { + color: commonPalette.text.primary + }, + '&.Mui-disabled': { + color: commonPalette.action.disabled + } + } + } + }, + + // Breadcrumbs + MuiBreadcrumbs: { + styleOverrides: { + li: { + '& .MuiTypography-root': { + ...themeTypography.body2, + display: 'flex', + flexDirection: 'row', + alignItems: 'center' + }, + '& .MuiSvgIcon-root': { + fontSize: `${themeTypography.body2.lineHeight}em`, + marginRight: getSpacing(1) + } + }, + + separator: { + marginLeft: getSpacing(0.5), + marginRight: getSpacing(0.5) + } + } + } +}; diff --git a/packages/react-ui/src/widgets/CategoryWidgetUI.js b/packages/react-ui/src/widgets/CategoryWidgetUI.js index 3f153e18f..0c1d63550 100644 --- a/packages/react-ui/src/widgets/CategoryWidgetUI.js +++ b/packages/react-ui/src/widgets/CategoryWidgetUI.js @@ -38,15 +38,15 @@ const useStyles = makeStyles((theme) => ({ }, element: { - '&$unselected': { + '&.unselected': { color: theme.palette.text.disabled, - '& $progressbar div': { + '& .progressbar div': { backgroundColor: theme.palette.text.disabled } }, - '&$rest $progressbar div': { + '&.rest .progressbar div': { backgroundColor: theme.palette.text.disabled } }, diff --git a/packages/react-ui/src/widgets/FormulaWidgetUI.js b/packages/react-ui/src/widgets/FormulaWidgetUI.js index ac785e511..92c3b5048 100644 --- a/packages/react-ui/src/widgets/FormulaWidgetUI.js +++ b/packages/react-ui/src/widgets/FormulaWidgetUI.js @@ -14,7 +14,7 @@ const useStyles = makeStyles((theme) => ({ color: theme.palette.text.secondary, marginLeft: theme.spacing(0.5), - '&$before': { + '&.before': { marginLeft: 0, marginRight: theme.spacing(0.5) } diff --git a/packages/react-ui/src/widgets/legend/LegendCategories.js b/packages/react-ui/src/widgets/legend/LegendCategories.js index eb32f21ca..f3e602e85 100644 --- a/packages/react-ui/src/widgets/legend/LegendCategories.js +++ b/packages/react-ui/src/widgets/legend/LegendCategories.js @@ -68,10 +68,7 @@ export default LegendCategories; // Aux const useStyles = makeStyles((theme) => ({ legendCategories: { - alignItems: 'center', - '&:hover': { - '& $circle': {} - } + alignItems: 'center' }, marker: { whiteSpace: 'nowrap', diff --git a/packages/react-ui/storybook/.storybook/manager.js b/packages/react-ui/storybook/.storybook/manager.js new file mode 100644 index 000000000..8615b65a2 --- /dev/null +++ b/packages/react-ui/storybook/.storybook/manager.js @@ -0,0 +1,15 @@ +// ./storybook/manager.ts +import { addons } from '@storybook/addons'; +import { create } from '@storybook/theming'; + +const theme = create({ + base: 'light', + + // Brand + brandTitle: 'CARTO Design System', + brandImage: undefined +}); + +addons.setConfig({ + theme +}); diff --git a/packages/react-ui/storybook/stories/common/Tabs.stories.js b/packages/react-ui/storybook/stories/common/Tabs.stories.js index 8a0067c74..f0d3b7844 100644 --- a/packages/react-ui/storybook/stories/common/Tabs.stories.js +++ b/packages/react-ui/storybook/stories/common/Tabs.stories.js @@ -24,6 +24,12 @@ const options = { type: 'select', options: ['default', 'primary', 'secondary'] } + }, + orientation: { + control: { + type: 'select', + options: ['horizontal', 'vertical'] + } } }, parameters: { diff --git a/packages/react-ui/storybook/stories/common/Text-field.stories.js b/packages/react-ui/storybook/stories/common/Text-field.stories.js index a0aabb0a4..b0a60e1ce 100644 --- a/packages/react-ui/storybook/stories/common/Text-field.stories.js +++ b/packages/react-ui/storybook/stories/common/Text-field.stories.js @@ -20,11 +20,25 @@ const options = { } }, required: { + defaultValue: false, control: { type: 'boolean' } }, disabled: { + defaultValue: false, + control: { + type: 'boolean' + } + }, + error: { + defaultValue: false, + control: { + type: 'boolean' + } + }, + multiline: { + defaultValue: false, control: { type: 'boolean' } @@ -33,6 +47,12 @@ const options = { control: { type: 'text' } + }, + margin: { + control: { + type: 'select', + options: ['dense', 'none', 'normal'] + } } }, parameters: {