diff --git a/app/src/pages/Button.jsx b/app/src/pages/Button.jsx index 9fdec342e..8574aecff 100644 --- a/app/src/pages/Button.jsx +++ b/app/src/pages/Button.jsx @@ -29,8 +29,8 @@ function App() { return (
-
-

Disabled

+

Disabled

+
{ }; return ( -
+
{ }`; const scope = { - DxcButton + DxcButton, }; export default { code, scope }; diff --git a/docs/src/pages/components/cdk-components/button/examples/withIcon.js b/docs/src/pages/components/cdk-components/button/examples/withIcon.js index b26df54c3..1bfa6ad68 100644 --- a/docs/src/pages/components/cdk-components/button/examples/withIcon.js +++ b/docs/src/pages/components/cdk-components/button/examples/withIcon.js @@ -7,7 +7,7 @@ const code = `() => { }; return ( -
+
( <DxcButton label="Xxlarge margin" margin="xxlarge" /> </ExampleContainer> + <Title title="Inside a flex" theme="light" level={2} /> + <ExampleContainer> + <DxcFlex direction="column" gap="0.75rem"> + <DxcButton label="Button" /> + <DxcButton label="Button" /> + <DxcButton label="Button" /> + </DxcFlex> + </ExampleContainer> </> ); diff --git a/lib/src/button/Button.tsx b/lib/src/button/Button.tsx index a04699989..a4689966c 100644 --- a/lib/src/button/Button.tsx +++ b/lib/src/button/Button.tsx @@ -1,12 +1,25 @@ -// @ts-nocheck import React, { useContext } from "react"; -import Button from "@material-ui/core/Button"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables.js"; import { getMargin } from "../common/utils.js"; import useTheme from "../useTheme"; import BackgroundColorContext from "../BackgroundColorContext"; -import ButtonPropsType from "./types"; +import ButtonPropsType, { Space, Margin, SVG } from "./types"; + +const sizes = { + small: "42px", + medium: "120px", + large: "240px", + fillParent: "100%", + fitContent: "fit-content", +}; + +const calculateWidth = (margin, size) => { + if (size === "fillParent") { + return `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})`; + } + return sizes[size]; +}; const DxcButton = ({ label = "", @@ -31,84 +44,39 @@ const DxcButton = ({ return ( <ThemeProvider theme={colorsTheme.button}> - <DxCButton + <Button type={type} - margin={margin} mode={mode !== "primary" && mode !== "secondary" && mode !== "text" ? "primary" : mode} disabled={disabled} - iconPosition={iconPosition} - size={size} + aria-disabled={disabled} + tabIndex={disabled ? -1 : tabIndex} backgroundType={backgroundType} - icon={icon} + size={size} + margin={margin} + onClick={() => { + onClick(); + }} > - <Button - type={type} - disabled={disabled} - disableRipple - aria-disabled={disabled} - tabIndex={disabled ? -1 : tabIndex} - onClick={() => { - onClick(); - }} - > - {label && iconPosition === "after" && labelComponent} - {icon && ( - <IconContainer label={label} iconPosition={iconPosition}> - {typeof icon === "string" ? <ButtonIcon src={icon} /> : icon} - </IconContainer> - )} - {label && iconPosition === "before" && labelComponent} - </Button> - </DxCButton> + {label && iconPosition === "after" && labelComponent} + {icon && ( + <IconContainer label={label} iconPosition={iconPosition}> + {typeof icon === "string" ? <ButtonIcon src={icon} /> : icon} + </IconContainer> + )} + {label && iconPosition === "before" && labelComponent} + </Button> </ThemeProvider> ); }; -const sizes = { - small: "42px", - medium: "120px", - large: "240px", - fillParent: "100%", - fitContent: "unset", -}; - -const calculateWidth = (margin, size) => { - if (size === "fillParent") { - return `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})`; - } - return sizes[size]; +type ButtonProps = { + mode: "primary" | "secondary" | "text"; + margin?: Space | Margin; + size: "small" | "medium" | "large" | "fillParent" | "fitContent"; + backgroundType: "dark" | "light"; }; -const LabelContainer = styled.span` - line-height: ${(props) => props.theme.labelFontLineHeight}; - font-size: ${(props) => props.theme.fontSize}; - text-overflow: ellipsis; - overflow: hidden; - text-transform: none; - white-space: nowrap; - margin-right: ${(props) => (!props.icon || props.iconPosition === "before" ? "8px" : "0px")}; - margin-left: ${(props) => (!props.icon || props.iconPosition === "after" ? "8px" : "0px")}; -`; - -const IconContainer = styled.div` - max-height: 24px; - max-width: 24px; - margin-left: ${(props) => - !props.label ? "0px" : (props.iconPosition === "after" && props.label !== "" && "8px") || "8px"}; - margin-right: ${(props) => - !props.label ? "0px" : (props.iconPosition === "before" && props.label !== "" && "8px") || "8px"}; - overflow: hidden; - display: flex; - img, - svg { - height: 100%; - width: 100%; - } -`; - -const ButtonIcon = styled.img``; - -const DxCButton = styled.div` +const Button = styled.button<ButtonProps>` margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; margin-top: ${(props) => props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; @@ -118,196 +86,243 @@ const DxCButton = styled.div` props.margin && typeof props.margin === "object" && props.margin.bottom ? spaces[props.margin.bottom] : ""}; margin-left: ${(props) => props.margin && typeof props.margin === "object" && props.margin.left ? spaces[props.margin.left] : ""}; - - display: inline-block; + display: inline-flex; width: ${(props) => calculateWidth(props.margin, props.size)}; - cursor: ${(props) => (props.disabled && "not-allowed") || "pointer"}; - - .MuiButtonBase-root { - padding-left: ${(props) => props.theme.paddingLeft}; - padding-right: ${(props) => props.theme.paddingRight}; - padding-top: ${(props) => props.theme.paddingTop}; - padding-bottom: ${(props) => props.theme.paddingBottom}; - - .MuiButton-label { - display: flex; - align-items: center; - } - - box-shadow: 0 0 0 2px transparent; - font-family: ${(props) => props.theme.fontFamily}; - font-size: ${(props) => props.theme.fontSize}; - font-weight: ${(props) => props.theme.fontWeight}; - letter-spacing: ${(props) => props.theme.labelLetterSpacing}; - min-width: ${(props) => (props.size === "small" && "calc(100% - 22px)") || "unset"}; - width: 100%; - height: 40px; - transition: none !important; - - &:focus { - border-color: transparent; - box-shadow: 0 0 0 2px - ${(props) => - props.backgroundType === "dark" ? props.theme.focusBorderColorOnDark : props.theme.focusBorderColor}; - } - - ${(props) => { - const { mode, backgroundType } = props; - if (mode === "primary") { - return ` - border-radius: ${props.theme.primaryBorderRadius}; - border-width: ${props.theme.primaryBorderThickness}; - border-style: ${props.theme.primaryBorderStyle}; - font-family: ${props.theme.primaryFontFamily}; - font-size: ${props.theme.primaryFontSize}; - font-weight: ${props.theme.primaryFontWeight}; - background-color: ${ - backgroundType === "dark" ? props.theme.primaryBackgroundColorOnDark : props.theme.primaryBackgroundColor + height: 40px; + padding-left: ${(props) => props.theme.paddingLeft}; + padding-right: ${(props) => props.theme.paddingRight}; + padding-top: ${(props) => props.theme.paddingTop}; + padding-bottom: ${(props) => props.theme.paddingBottom}; + align-items: center; + justify-content: center; + box-shadow: 0 0 0 2px transparent; + font-family: ${(props) => props.theme.fontFamily}; + font-size: ${(props) => props.theme.fontSize}; + font-weight: ${(props) => props.theme.fontWeight}; + letter-spacing: ${(props) => props.theme.labelLetterSpacing}; + cursor: pointer; + &:focus { + outline: none; + box-shadow: 0 0 0 2px + ${(props) => + props.backgroundType === "dark" ? props.theme.focusBorderColorOnDark : props.theme.focusBorderColor}; + } + ${(props) => { + const { mode, backgroundType, disabled } = props; + return ` + border-radius: ${ + props.mode === "primary" + ? props.theme.primaryBorderRadius + : props.mode === "secondary" + ? props.theme.secondaryBorderRadius + : props.theme.textBorderRadius + }; + border-width: ${ + props.mode === "primary" + ? props.theme.primaryBorderThickness + : props.mode === "secondary" + ? props.theme.secondaryBorderThickness + : props.theme.textBorderThickness + }; + border-style: ${ + mode === "primary" + ? props.theme.primaryBorderStyle + : mode === "secondary" + ? props.theme.secondaryBorderStyle + : props.theme.textBorderStyle + }; + font-family: ${ + mode === "primary" + ? props.theme.primaryFontFamily + : mode === "secondary" + ? props.theme.secondaryFontFamily + : props.theme.textFontFamily + }; + font-size: ${ + mode === "primary" + ? props.theme.primaryFontSize + : mode === "secondary" + ? props.theme.secondaryFontSize + : props.theme.textFontSize + }; + font-weight: ${ + mode === "primary" + ? props.theme.primaryFontWeight + : mode === "secondary" + ? props.theme.secondaryFontWeight + : props.theme.textFontWeight }; - color: ${ - backgroundType && backgroundType === "dark" - ? props.theme.primaryFontColorOnDark - : props.theme.primaryFontColor - } !important; - - &:hover { - background-color: ${ - backgroundType === "dark" - ? props.theme.primaryHoverBackgroundColorOnDark - : props.theme.primaryHoverBackgroundColor - }; - } - &:active { - background-color: ${ - backgroundType === "dark" - ? props.theme.primaryActiveBackgroundColorOnDark - : props.theme.primaryActiveBackgroundColor - } !important; - border-color: transparent; - box-shadow: 0 0 0 2px ${ - backgroundType === "dark" ? props.theme.focusBorderColorOnDark : props.theme.focusBorderColor - }; - } - &:disabled { - cursor: not-allowed; - background-color: ${ - backgroundType === "dark" - ? props.theme.primaryDisabledBackgroundColorOnDark - : props.theme.primaryDisabledBackgroundColor - }; - color: ${ - backgroundType === "dark" - ? props.theme.primaryDisabledFontColorOnDark - : props.theme.primaryDisabledFontColor - } !important; - } - `; - } else if (mode === "secondary") { - return ` - border-radius: ${props.theme.secondaryBorderRadius}; - border-width: ${props.theme.secondaryBorderThickness}; - border-style: ${props.theme.secondaryBorderStyle}; - font-family: ${props.theme.secondaryFontFamily}; - font-size: ${props.theme.secondaryFontSize}; - font-weight: ${props.theme.secondaryFontWeight}; background-color: ${ - backgroundType === "dark" ? props.theme.secondaryBackgroundColorOnDark : props.theme.secondaryBackgroundColor + mode === "primary" + ? backgroundType === "dark" + ? props.theme.primaryBackgroundColorOnDark + : props.theme.primaryBackgroundColor + : mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryBackgroundColorOnDark + : props.theme.secondaryBackgroundColor + : backgroundType === "dark" + ? props.theme.textBackgroundColorOnDark + : props.theme.textBackgroundColor }; color: ${ - backgroundType === "dark" ? props.theme.secondaryFontColorOnDark : props.theme.secondaryFontColor - } !important; + mode === "primary" + ? backgroundType === "dark" + ? props.theme.primaryFontColorOnDark + : props.theme.primaryFontColor + : mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryFontColorOnDark + : props.theme.secondaryFontColor + : backgroundType === "dark" + ? props.theme.textFontColorOnDark + : props.theme.textFontColor + }; border-color: ${ - backgroundType === "dark" ? props.theme.secondaryBorderColorOnDark : props.theme.secondaryBorderColor + mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryBorderColorOnDark + : props.theme.secondaryBorderColor + : "" }; - &:hover { background-color: ${ - backgroundType === "dark" - ? props.theme.secondaryHoverBackgroundColorOnDark - : props.theme.secondaryHoverBackgroundColor + mode === "primary" + ? backgroundType === "dark" + ? props.theme.primaryHoverBackgroundColorOnDark + : props.theme.primaryHoverBackgroundColor + : mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryHoverBackgroundColorOnDark + : props.theme.secondaryHoverBackgroundColor + : backgroundType === "dark" + ? props.theme.textHoverBackgroundColorOnDark + : props.theme.textHoverBackgroundColor }; color: ${ - backgroundType === "dark" ? props.theme.secondaryHoverFontColorOnDark : props.theme.secondaryHoverFontColor - } !important; - } - &:active { - background-color: ${ - backgroundType === "dark" - ? props.theme.secondaryActiveBackgroundColorOnDark - : props.theme.secondaryActiveBackgroundColor - } !important; - color: ${ - backgroundType === "dark" ? props.theme.secondaryHoverFontColorOnDark : props.theme.secondaryHoverFontColor - } !important; - border-color: transparent; - box-shadow: 0 0 0 2px ${ - backgroundType === "dark" ? props.theme.focusBorderColorOnDark : props.theme.focusBorderColor + mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryHoverFontColorOnDark + : props.theme.secondaryHoverFontColor + : "" }; } - &:disabled { - cursor: not-allowed; - background-color: ${ - backgroundType === "dark" - ? props.theme.secondaryDisabledBackgroundColorOnDark - : props.theme.secondaryDisabledBackgroundColor - } !important; - color: ${ - backgroundType === "dark" - ? props.theme.secondaryDisabledFontColorOnDark - : props.theme.secondaryDisabledFontColor - } !important; - border-color: ${ - backgroundType === "dark" - ? props.theme.secondaryDisabledBorderColorOnDark - : props.theme.secondaryDisabledBorderColor - }; - } - `; - } else if (mode === "text") { - return ` - border-radius: ${props.theme.textBorderRadius}; - border-width: ${props.theme.textBorderThickness}; - border-style: ${props.theme.textBorderStyle}; - font-family: ${props.theme.textFontFamily}; - font-size: ${props.theme.textFontSize}; - font-weight: ${props.theme.textFontWeight}; - background-color: ${ - backgroundType === "dark" ? props.theme.textBackgroundColorOnDark : props.theme.textBackgroundColor - }; - color: ${backgroundType === "dark" ? props.theme.textFontColorOnDark : props.theme.textFontColor} !important; - - &:hover { - background-color: ${ - backgroundType === "dark" - ? props.theme.textHoverBackgroundColorOnDark - : props.theme.textHoverBackgroundColor - }; + &:focus { + border-color: ${mode === "secondary" ? "transparent" : ""}; } &:active { background-color: ${ - backgroundType === "dark" + mode === "primary" + ? backgroundType === "dark" + ? props.theme.primaryActiveBackgroundColorOnDark + : props.theme.primaryActiveBackgroundColor + : mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryActiveBackgroundColorOnDark + : props.theme.secondaryActiveBackgroundColor + : backgroundType === "dark" ? props.theme.textActiveBackgroundColorOnDark : props.theme.textActiveBackgroundColor - } !important; - border-color: transparent; - box-shadow: 0 0 0 2px ${ - backgroundType === "dark" ? props.theme.focusBorderColorOnDark : props.theme.focusBorderColor + }; + color: ${ + mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryHoverFontColorOnDark + : props.theme.secondaryHoverFontColor + : "" + }; + border-color: ${mode === "secondary" ? "transparent" : ""}; + outline: none; + box-shadow: ${ + !disabled + ? `0 0 0 2px ${ + backgroundType === "dark" ? props.theme.focusBorderColorOnDark : props.theme.focusBorderColor + }` + : "" }; } &:disabled { - cursor:not-allowed; - color: ${ - backgroundType === "dark" ? props.theme.textDisabledFontColorOnDark : props.theme.textDisabledFontColor - } !important; + cursor: not-allowed; background-color: ${ - backgroundType === "dark" + mode === "primary" + ? backgroundType === "dark" + ? props.theme.primaryDisabledBackgroundColorOnDark + : props.theme.primaryDisabledBackgroundColor + : mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryDisabledBackgroundColorOnDark + : props.theme.secondaryDisabledBackgroundColor + : backgroundType === "dark" ? props.theme.textDisabledBackgroundColorOnDark : props.theme.textDisabledBackgroundColor }; + color: ${ + mode === "primary" + ? backgroundType === "dark" + ? props.theme.primaryDisabledFontColorOnDark + : props.theme.primaryDisabledFontColor + : mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryDisabledFontColorOnDark + : props.theme.secondaryDisabledFontColor + : backgroundType === "dark" + ? props.theme.textDisabledFontColorOnDark + : props.theme.textDisabledFontColor + }; + border-color: ${ + mode === "secondary" + ? backgroundType === "dark" + ? props.theme.secondaryDisabledBorderColorOnDark + : props.theme.secondaryDisabledBorderColor + : "" + }; } `; - } - }} + }} +`; + +type LabelPropsType = { + iconPosition: "before" | "after"; + icon?: string | SVG; +}; + +const LabelContainer = styled.span<LabelPropsType>` + line-height: ${(props) => props.theme.labelFontLineHeight}; + font-size: ${(props) => props.theme.fontSize}; + text-overflow: ellipsis; + overflow: hidden; + text-transform: none; + white-space: nowrap; + margin-right: ${(props) => (!props.icon || props.iconPosition === "before" ? "8px" : "0px")}; + margin-left: ${(props) => (!props.icon || props.iconPosition === "after" ? "8px" : "0px")}; +`; + +type IconPropsType = { + label?: string; + iconPosition: "before" | "after"; +}; + +const IconContainer = styled.div<IconPropsType>` + max-height: 24px; + max-width: 24px; + margin-left: ${(props) => + !props.label ? "0px" : (props.iconPosition === "after" && props.label !== "" && "8px") || "8px"}; + margin-right: ${(props) => + !props.label ? "0px" : (props.iconPosition === "before" && props.label !== "" && "8px") || "8px"}; + overflow: hidden; + display: flex; + img, + svg { + height: 100%; + width: 100%; + } +`; + +const ButtonIcon = styled.img` + img, + svg { + height: 100%; + width: 100%; } `; diff --git a/lib/src/button/types.ts b/lib/src/button/types.ts index f6434961d..612a2ac24 100644 --- a/lib/src/button/types.ts +++ b/lib/src/button/types.ts @@ -1,19 +1,19 @@ -type Space = "xxsmall" | "xsmall" | "small" | "medium" | "large" | "xlarge" | "xxlarge"; -type Margin = { +export type Space = "xxsmall" | "xsmall" | "small" | "medium" | "large" | "xlarge" | "xxlarge"; +export type Margin = { top?: Space; bottom?: Space; left?: Space; right?: Space; }; -type SVG = React.SVGProps<SVGSVGElement>; +export type SVG = React.SVGProps<SVGSVGElement>; type Props = { /** - * Text to be placed next to the button. + * Text to be placed in the button. */ label?: string; /** - * Uses one of the available button modes. + * The available button modes. */ mode?: "primary" | "secondary" | "text"; /** @@ -25,11 +25,11 @@ type Props = { */ iconPosition?: "before" | "after"; /** - * This prop corresponds to the 'type' prop of the button in html. + * 'type' html prop of the button. */ type?: "button" | "reset" | "submit"; /** - * Element or path used as the icon that will be placed next to the button label. + * Element or path used as the icon that will be placed next to the label. */ icon?: string | SVG; /** diff --git a/website/screens/components/button/code/ButtonCodePage.tsx b/website/screens/components/button/code/ButtonCodePage.tsx index 291d072a1..901e3ad6d 100644 --- a/website/screens/components/button/code/ButtonCodePage.tsx +++ b/website/screens/components/button/code/ButtonCodePage.tsx @@ -26,21 +26,19 @@ const sections = [ <td> <Code>'primary'</Code> </td> - <td>Uses one of the available button modes.</td> + <td>The available button modes.</td> </tr> <tr> <td>type: 'button' | 'reset' | 'submit'</td> <td> <Code>'button'</Code> </td> - <td> - This prop corresponds to the 'type' prop of the button in html. - </td> + <td>'type' html prop of the button.</td> </tr> <tr> <td>label: string</td> <td></td> - <td>Text to be placed next to the button.</td> + <td>Text to be placed in the button.</td> </tr> <tr> <td>icon: node | string</td>