Skip to content

Commit

Permalink
refactor(callout): add sdsStyle prop to control expandability or dism…
Browse files Browse the repository at this point in the history
…issability
  • Loading branch information
masoudmanson committed Nov 27, 2024
1 parent 04bbab6 commit b942a54
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 76 deletions.
13 changes: 10 additions & 3 deletions packages/components/src/core/Callout/__storybook__/constants.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { action } from "@storybook/addon-actions";
import CustomSdsIcon from "src/common/storybook/customSdsIcon";
import CustomSvgIcon from "src/common/storybook/customSvgIcon";

export const CALLOUT_EXCLUDED_CONTROLS = [
"autoDismiss",
"expandable",
"icon",
"intent",
"onClose",
"sdsStyle",
"hideTitle",
"hideBody",
"title",
"body",
];

export const CALLOUT_ICON_OPTIONS = [
Expand All @@ -17,4 +20,8 @@ export const CALLOUT_ICON_OPTIONS = [
"CheckCircle",
];

export const CALLOUT_ON_CLOSE_OPTIONS = [action("onClick"), undefined];
export const CALLOUT_SDS_STYLE_OPTIONS = [
"persistent",
"expandable",
"dismissible",
];
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta } from "@storybook/react";
import { BADGE } from "@geometricpanda/storybook-addon-badges";
import { CALLOUT_ICON_OPTIONS, CALLOUT_ON_CLOSE_OPTIONS } from "./constants";
import { CALLOUT_ICON_OPTIONS, CALLOUT_SDS_STYLE_OPTIONS } from "./constants";
import { Callout } from "./stories/default";

export default {
Expand All @@ -17,17 +17,6 @@ export default {
defaultValue: { summary: "-" },
description: "The body text of the callout.",
},
expandable: {
control: { type: "boolean" },
defaultValue: { summary: "false" },
description: "If true, the callout will be expandable.",
},
extraContent: {
control: { type: "boolean" },
defaultValue: { summary: "-" },
description:
"The extra content in the Callout is passed as children to the component, allowing you to add custom content to it. For Storybook purposes, a predefined example of extra content has been added to the Callout component. You can toggle this boolean to test it out. Note that this is not an actual prop on the Callout component.",
},
hideBody: {
control: { type: "boolean" },
defaultValue: { summary: "false" },
Expand Down Expand Up @@ -60,16 +49,16 @@ export default {
description: "The intent of the callout.",
options: ["info", "negative", "positive", "notice"],
},
onClose: {
sdsStyle: {
control: {
labels: ["() => {}", "undefined"],
labels: ["persistent", "expandable", "dismissible"],
type: "select",
},
defaultValue: { summary: "-" },
defaultValue: { summary: "persistent" },
description:
"If set to a function, the callout will have a close button.",
mapping: CALLOUT_ON_CLOSE_OPTIONS,
options: Object.keys(CALLOUT_ON_CLOSE_OPTIONS),
"The style of the Callout determines its behavior: Persistent Callouts are always visible, Expandable Callouts can be toggled to show or hide extra content, and Dismissible Callouts can be closed by the user.",
mapping: CALLOUT_SDS_STYLE_OPTIONS,
options: Object.keys(CALLOUT_SDS_STYLE_OPTIONS),
},
title: {
control: { type: "text" },
Expand All @@ -92,9 +81,7 @@ export default {
export const Default = {
args: {
autoDismiss: false,
body: "Callout text—replace the content here and the height of the component will adjust automatically.",
expandable: false,
onClose: false,
title: "Title",
body: "Callout text — replace the content here and the height of the component will adjust automatically.",
title: "The callout title goes here",
},
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FormControlLabel } from "@mui/material";
import { Box, FormControlLabel } from "@mui/material";
import { Args } from "@storybook/react";
import { useState } from "react";
import RawCallout from "src/core/Callout";
Expand All @@ -7,18 +7,18 @@ import { SHORT_LOREM_IPSUM } from "src/common/storybook/loremIpsum";
import TooltipTableContent from "src/core/TooltipTable";

export const Callout = (props: Args): JSX.Element => {
const { intent, onClose, autoDismiss, extraContent, ...rest } = props;
const { intent, autoDismiss, ...rest } = props;

const [dismissed, setDismissed] = useState(false);

const handleChange = () => {
setDismissed((prev) => !prev);
};

const finalOnclose = !onClose ? undefined : () => setDismissed(true);
const finalOnclose = (_: React.SyntheticEvent) => setDismissed(true);

return (
<>
<Box sx={{ width: 600 }}>
{!autoDismiss && (
<FormControlLabel
control={<InputToggle checked={dismissed} onChange={handleChange} />}
Expand All @@ -38,7 +38,7 @@ export const Callout = (props: Args): JSX.Element => {
onClose={finalOnclose}
{...rest}
>
{extraContent && (
{
<div>
{SHORT_LOREM_IPSUM}
<div style={{ marginTop: 10 }}>
Expand All @@ -55,8 +55,8 @@ export const Callout = (props: Args): JSX.Element => {
/>
</div>
</div>
)}
}
</RawCallout>
</>
</Box>
);
};
72 changes: 39 additions & 33 deletions packages/components/src/core/Callout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const SDS_STAGE_OPEN = "open";
const SDS_STAGE_CLOSED = "closed";

export type CalloutIntentType = "info" | "negative" | "notice" | "positive";
export type CalloutSdsStyleType = "persistent" | "expandable" | "dismissible";
export interface CalloutProps {
autoDismiss?: boolean | number;
dismissed?: boolean;
Expand All @@ -24,30 +25,30 @@ export interface CalloutProps {
body?: string;
hideBody?: boolean;
extraContent?: React.ReactNode;
expandable?: boolean;
sdsStyle?: CalloutSdsStyleType;
}

export type ExposedCalloutProps = Omit<AlertProps, "severity"> & CalloutProps;

/**
* @see https://mui.com/material-ui/react-alert/
*/
const Callout = ({
autoDismiss,
dismissed,
expandable,
onClose,
icon,
sdsIconProps,
intent,
sdsStage,
title,
body,
hideTitle = false,
hideBody = false,
children,
...rest
}: ExposedCalloutProps): JSX.Element => {
const Callout = (props: ExposedCalloutProps): JSX.Element => {
const {
autoDismiss,
dismissed,
icon,
sdsIconProps,
intent,
sdsStage,
title,
body,
hideTitle = false,
hideBody = false,
sdsStyle = "persistent",
children,
...rest
} = props;
const [hide, setHide] = useState(dismissed);
const [stage, setStage] = useState(sdsStage || SDS_STAGE_CLOSED);

Expand All @@ -64,7 +65,7 @@ const Callout = ({

const handleClose = (event: React.SyntheticEvent<Element, Event>) => {
setHide(true);
if (onClose) onClose(event);
if (props?.onClose) props?.onClose(event);
};

const getIcon = () => {
Expand Down Expand Up @@ -93,7 +94,7 @@ const Callout = ({
};

const getAction = (collapsed: boolean) => {
if (expandable) {
if (sdsStyle === "expandable") {
return (
<Button
aria-label={collapsed ? "open" : "close"}
Expand All @@ -106,36 +107,41 @@ const Callout = ({
icon={collapsed ? "ChevronDown" : "ChevronUp"}
/>
);
} else if (sdsStyle === "dismissible") {
return (
<Button
aria-label="Dismiss"
onClick={handleClose}
sdsSize="small"
sdsType="tertiary"
sdsStyle="icon"
size="large"
icon="XMark"
/>
);
}
return onClose ? (
<Button
aria-label="Dismiss"
onClick={handleClose}
sdsSize="small"
sdsType="tertiary"
sdsStyle="icon"
size="large"
icon="XMark"
/>
) : null;

return null;
};

const collapsed = (expandable && stage === SDS_STAGE_CLOSED) || false;
const collapsed =
(sdsStyle === "expandable" && stage === SDS_STAGE_CLOSED) || false;

return (
<>
<Grow in={!hide}>
<StyledCallout
onClose={onClose ? handleClose : undefined}
onClose={handleClose}
action={getAction(collapsed)}
icon={getIcon()}
intent={intent}
collapsed={collapsed || false}
sdsStyle={sdsStyle}
{...rest}
>
{!hideTitle && <CalloutTitle>{title}</CalloutTitle>}
{!hideBody && <CalloutBody hideTitle={hideTitle}>{body}</CalloutBody>}
{!collapsed && (
{sdsStyle === "expandable" && !collapsed && (
<CalloutExtraContent hideTitle={hideTitle} hideBody={hideBody}>
{children}
</CalloutExtraContent>
Expand Down
20 changes: 9 additions & 11 deletions packages/components/src/core/Callout/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,25 @@ import {
getSemanticColors,
getSpaces,
} from "src/core/styles";
import { CalloutIntentType } from "src/core/Callout";
import { CalloutIntentType, CalloutSdsStyleType } from "src/core/Callout";

interface CalloutExtraProps extends CommonThemeProps {
collapsed?: boolean;
intent?: CalloutIntentType;
sdsStyle?: CalloutSdsStyleType;
}

type CalloutProps = Omit<AlertProps, "severity"> & CalloutExtraProps;

const doNotForwardProps = ["calloutTitle", "collapsed", "severity"];
const doNotForwardProps = ["calloutTitle", "collapsed", "severity", "sdsStyle"];

export const StyledCallout = styled(Alert, {
shouldForwardProp: (prop: string) => !doNotForwardProps.includes(prop),
})`
${fontBodyXs}
${(props: CalloutProps) => {
const { intent = "info" } = props;
const { intent = "info", sdsStyle } = props;
const spaces = getSpaces(props);
const corners = getCorners(props);
Expand All @@ -43,8 +45,6 @@ export const StyledCallout = styled(Alert, {
semanticColors?.[intent]?.surfaceSecondary ?? "white";
return `
position: relative;
width: 360px;
margin: ${spaces?.m}px 0;
border-radius: ${corners?.m}px;
color: ${palette?.text?.primary};
Expand All @@ -63,22 +63,20 @@ export const StyledCallout = styled(Alert, {
}
.${alertClasses.message} {
width: 100%;
padding: 0;
margin-right: ${spaces?.m}px;
margin: 0;
.${alertTitleClasses.root} {
margin: 0;
}
}
.${alertClasses.action} {
position: absolute;
right: ${spaces?.m}px;
top: ${spaces?.m}px;
margin-right: 0;
display: ${sdsStyle === "persistent" ? "none" : "block"};
margin: 0 0 0 ${spaces?.s}px;
padding: 0;
align-items: flex-start;
margin-top: ${spaces?.xxs}px;
> button {
padding: 0;
Expand Down

0 comments on commit b942a54

Please sign in to comment.