Skip to content

Commit

Permalink
fix(button): added destructive button style
Browse files Browse the repository at this point in the history
  • Loading branch information
masoudmanson committed Apr 27, 2024
1 parent 053fbf9 commit 24475e7
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 229 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CheckCircleOutline } from "@mui/icons-material";
import { Snackbar } from "@mui/material";
import { Snackbar, Button as MUIButton } from "@mui/material";
import { styled } from "@mui/material/styles";
import { Args, Meta } from "@storybook/react";
import React from "react";
Expand All @@ -8,7 +8,7 @@ import { defaultTheme } from "src/core/styles/common/defaultTheme";
import Alert from "../index";
import { BADGE } from "src/common/storybook/storybookBadges";

const DismissButton = styled(Button)`
const DismissButton = styled(MUIButton)`
margin-left: -${defaultTheme.spacing(3)}px;
padding-bottom: 0;
font-size: 12px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ export const BUTTON_SDS_STYLES = ["rounded", "square", "minimal", "icon"];

export const BUTTON_SDS_SIZE = ["small", "medium", "large"];

export const BUTTON_SDS_TYPES = ["primary", "secondary", "tertiary"];
export const BUTTON_SDS_TYPES = [
"primary",
"secondary",
"tertiary",
"destructive",
];

export const BUTTON_TEXT = "Label";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const InvalidIconButtonPropsError = (
</Callout>
);

const InvalidSdsTypeError = (
const InvalidSdsTypeTertiaryError = (
<Callout intent="negative">
<CalloutTitle>Invalid Props!</CalloutTitle>
<p>
Expand All @@ -31,6 +31,17 @@ const InvalidSdsTypeError = (
</Callout>
);

const InvalidSdsTypeDestructiveError = (
<Callout intent="negative">
<CalloutTitle>Invalid Props!</CalloutTitle>
<p>
Buttons with the &apos;icon&apos; style cannot have the
&apos;destructive&apos; type. Please choose another type, such as
&apos;square&apos;, &apos;rounded&apos;, or &apos;minimal&apos;.
</p>
</Callout>
);

const FinalIconSize = (sdsStyle: string, sdsSize: string) => {
return sdsStyle === "minimal"
? "s"
Expand All @@ -41,6 +52,7 @@ const FinalIconSize = (sdsStyle: string, sdsSize: string) => {
: "l";
};

// eslint-disable-next-line sonarjs/cognitive-complexity
export const Button = (props: Args): JSX.Element => {
const { sdsType, sdsStyle, sdsSize, text, startIcon, endIcon, icon } = props;

Expand Down Expand Up @@ -86,7 +98,11 @@ export const Button = (props: Args): JSX.Element => {
}

if (sdsStyle !== "icon" && sdsType === "tertiary") {
return InvalidSdsTypeError;
return InvalidSdsTypeTertiaryError;
}

if (sdsStyle === "icon" && sdsType === "destructive") {
return InvalidSdsTypeDestructiveError;
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`<button /> Default story renders snapshot 1`] = `
<button
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium css-n7hga1-MuiButtonBase-root-MuiButton-root"
class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium css-14nu2qe-MuiButtonBase-root-MuiButton-root"
tabindex="0"
text="Label"
type="button"
Expand Down
145 changes: 54 additions & 91 deletions packages/components/src/core/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,37 @@ import {
SDSWarningTypes,
showWarningIfFirstOccurence,
} from "src/common/warnings";
import {
PrimaryMinimalButton,
RoundedButton,
SecondaryMinimalButton,
SquareButton,
StyledButton,
} from "./style";
import { StyledButton, StyledButtonLegacy, StyledMinimalButton } from "./style";
import ButtonIcon from "../ButtonIcon";
import { IconNameToSizes, IconProps } from "../Icon";
import { filterProps } from "src/common/utils";

/**
* (masoudmanson): When sdsStyle="icon", sdsType can be any ButtonType.
* For other sdsStyle values, sdsType is restricted to "primary" or "secondary".
* To enforce this, we utilized a union type for the sdsStyle and sdsType props.
*/

type ButtonStyle = "rounded" | "square" | "minimal" | "icon";
type ButtonType = "primary" | "secondary" | "tertiary";
type ButtonType = "primary" | "secondary" | "tertiary" | "destructive";
type ButtonSize = "small" | "medium" | "large";

type SdsProps =
| {
sdsStyle?: Exclude<ButtonStyle, "icon">;
sdsType?: Exclude<ButtonType, "tertiary">;
isAllCaps?: boolean;
isRounded?: boolean;
sdsSize?: ButtonSize;
icon?: keyof IconNameToSizes | React.ReactElement<CustomSVGProps>;
sdsIconProps?: Partial<IconProps<keyof IconNameToSizes>>;
}
| {
sdsStyle?: "icon";
sdsType?: ButtonType;
isAllCaps?: boolean;
isRounded?: boolean;
sdsSize?: ButtonSize;
icon?: keyof IconNameToSizes | React.ReactElement<CustomSVGProps>;
sdsIconProps?: Partial<IconProps<keyof IconNameToSizes>>;
};
type SdsProps = {
sdsStyle?: ButtonStyle;
sdsType?: ButtonType;
isAllCaps?: boolean;
isRounded?: boolean;
sdsSize?: ButtonSize;
icon?: keyof IconNameToSizes | React.ReactElement<CustomSVGProps>;
sdsIconProps?: Partial<IconProps<keyof IconNameToSizes>>;
};

export type ButtonProps = RawButtonProps & SdsProps;

/**
* (masoudmanson): The MUI variant prop is determined by the sdsType prop.
*/
const MUI_VARIANT_MAP: { [key in ButtonType]: ButtonProps["variant"] } = {
destructive: "contained",
primary: "contained",
secondary: "outlined",
tertiary: "text",
};

/**
* @see https://mui.com/material-ui/react-button/
*/
Expand All @@ -73,61 +61,7 @@ const Button = React.forwardRef(
const propsWithDefault: PropsWithDefaultsType = { ...props, isAllCaps };

switch (true) {
case sdsStyle === "rounded" && sdsType === "primary":
return (
<RoundedButton
color="primary"
ref={ref}
variant="contained"
{...propsWithDefault}
/>
);
case sdsStyle === "rounded" && sdsType === "secondary":
return (
<RoundedButton
color="primary"
ref={ref}
variant="outlined"
{...propsWithDefault}
/>
);
case sdsStyle === "square" && sdsType === "primary":
return (
<SquareButton
color="primary"
ref={ref}
variant="contained"
{...propsWithDefault}
/>
);
case sdsStyle === "square" && sdsType === "secondary":
return (
<SquareButton
color="primary"
ref={ref}
variant="outlined"
{...propsWithDefault}
/>
);
case sdsStyle === "minimal" && sdsType === "primary":
return (
<PrimaryMinimalButton
color="primary"
ref={ref}
variant="text"
{...propsWithDefault}
/>
);
case sdsStyle === "minimal" && sdsType === "secondary":
return (
<SecondaryMinimalButton
color="primary"
ref={ref}
variant="text"
{...propsWithDefault}
/>
);
case sdsStyle === "icon": {
case sdsStyle === "icon":
if (icon !== undefined) {
// (masoudmanson): We need to remove the props that are not supported by
// the ButtonIcon component.
Expand All @@ -136,19 +70,48 @@ const Button = React.forwardRef(
"endIcon",
"sdsStyle",
"isAllCaps",
"sdsType",
];
const finalProps = filterProps(propsWithDefault, excludedProps);

return <ButtonIcon icon={icon} {...finalProps} ref={ref} />;
return (
<ButtonIcon
icon={icon}
{...finalProps}
sdsType={sdsType as Exclude<ButtonType, "destructive">}
ref={ref}
/>
);
} else {
showWarningIfFirstOccurence(
SDSWarningTypes.ButtonIconMissingIconProp
);
return null;
}
}

case sdsStyle === "minimal":
return (
<StyledMinimalButton
color="primary"
variant="text"
{...propsWithDefault}
ref={ref}
/>
);

case sdsStyle === "rounded":
case sdsStyle === "square":
return (
<StyledButton
color="primary"
variant={sdsType ? MUI_VARIANT_MAP[sdsType] : "text"}
{...propsWithDefault}
ref={ref}
/>
);

default:
return <StyledButton {...propsWithDefault} ref={ref} />;
return <StyledButtonLegacy {...propsWithDefault} ref={ref} />;
}
}
);
Expand Down
Loading

0 comments on commit 24475e7

Please sign in to comment.