Skip to content

Commit

Permalink
feact(alert/callout): design update (#4060)
Browse files Browse the repository at this point in the history
* feat(design-tokens): updated tokens to support Alert and Callout updates

* feat(callout): add dismissible funcitonality to the component

* chore(pr): changeset

* chore(alert): updated styling

* chore(callout): updated styling

* chore(pr): changeset

* chore(pr): typedocs

* chore(pr): linting issues

* chore(pr): peer dependencies

* chore(pr): peer dependencies

* feat(website): added new example for dismissible callout

* chore(pr): cleanup changesets

* chore(pr): cleanup changesets

* chore(pr): pr cleanup

* chore(pr): typedocs

* chore(pr): fix stlying issues

* chore(pr): remove icon padding

* fix(callout): correct row gap size

* fix(callout): correct row gap size

* fix(callout): fix double margin

* fix(callout): close and icon on same line

* fix(callout): issue with additonal pixel added

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
krisantrobus and kodiakhq[bot] authored Sep 10, 2024
1 parent 7ae14af commit 31defba
Show file tree
Hide file tree
Showing 18 changed files with 268 additions and 61 deletions.
6 changes: 6 additions & 0 deletions .changeset/mean-houses-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@twilio-paste/design-tokens": minor
"@twilio-paste/core": minor
---

[Design Tokens] added a new design token for color-border-new-weak and made changes to color-text-icon-warning in the Twilio theme to support design updates to Alert and Callout components
6 changes: 6 additions & 0 deletions .changeset/neat-weeks-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@twilio-paste/callout": minor
"@twilio-paste/core": minor
---

[Callout] added dismissible functionality to the component
7 changes: 7 additions & 0 deletions .changeset/plenty-rockets-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@twilio-paste/alert": patch
"@twilio-paste/callout": patch
"@twilio-paste/core": patch
---

[Callout, Alert] updated styling
20 changes: 13 additions & 7 deletions packages/paste-core/components/alert/src/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ export const AlertVariants = {
NEUTRAL: "neutral",
WARNING: "warning",
} as const;
export const AlertBackgroundColors = {
export const AlertBackgroundColors: BoxProps["backgroundColor"] = {
ERROR: "colorBackgroundErrorWeakest",
NEUTRAL: "colorBackgroundNeutralWeakest",
WARNING: "colorBackgroundWarningWeakest",
} as const;
export const AlertTextColors = {
export const AlertBorderColors: BoxProps["borderColor"] = {
ERROR: "colorBorderErrorWeaker",
NEUTRAL: "colorBorderNeutralWeaker",
WARNING: "colorBorderWarningWeaker",
} as const;
export const AlertTextColors: BoxProps["color"] = {
ERROR: "colorTextError",
NEUTRAL: "colorTextNeutral",
WARNING: "colorTextWarningStrong",
Expand Down Expand Up @@ -169,10 +174,11 @@ const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
<Box
{...safelySpreadBoxProps(props)}
backgroundColor={AlertBackgroundColors[variant.toUpperCase() as AlertVariantKeys]}
paddingLeft="space60"
paddingRight="space60"
paddingTop="space60"
paddingBottom="space60"
borderColor={AlertBorderColors[variant.toUpperCase() as AlertVariantKeys]}
borderStyle="solid"
borderWidth="borderWidth10"
borderRadius="borderRadius30"
padding="space50"
element={element}
variant={variant}
ref={ref}
Expand All @@ -188,7 +194,7 @@ const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
{onDismiss && typeof onDismiss === "function" && (
<MediaFigure align="end" spacing="space60">
<Button onClick={onDismiss} variant="secondary_icon" size="reset" element={`${element}_DISMISS_BUTTON`}>
<CloseIcon element={`${element}_DISMISS_ICON`} decorative size="sizeIcon20" />
<CloseIcon element={`${element}_DISMISS_ICON`} color="colorTextIcon" decorative size="sizeIcon20" />
<ScreenReaderOnly>{i18nDismissLabel}</ScreenReaderOnly>
</Button>
</MediaFigure>
Expand Down
46 changes: 45 additions & 1 deletion packages/paste-core/components/callout/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, screen } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import * as React from "react";

import { Callout, CalloutHeading, CalloutList, CalloutListItem, CalloutText } from "../src";
Expand Down Expand Up @@ -179,3 +179,47 @@ describe("i18n", () => {
expect(icon?.textContent).toEqual("(nouveau)");
});
});

describe("Callout dismiss", () => {
it("should not render close button if no dismiss funciton passed", () => {
render(
<Callout variant="neutral" data-testid="callout">
<CalloutText>This is some text.</CalloutText>
</Callout>,
);

const callout = screen.getByTestId("callout");

const text = screen.getByText("This is some text.");
expect(text).toBeTruthy();

const dismissButton = callout.querySelector('[data-paste-element="CALLOUT_DISMISS_BUTTON"]');
expect(dismissButton).toBeNull();
});

it("should render close button if dismiss function passed", async () => {
const closeSpy = jest.fn();

render(
<Callout variant="neutral" data-testid="callout" onDismiss={closeSpy}>
<CalloutText>This is some text.</CalloutText>
</Callout>,
);

const callout = screen.getByTestId("callout");

const text = screen.getByText("This is some text.");
expect(text).toBeTruthy();

const dismissButton = callout.querySelector('[data-paste-element="CALLOUT_DISMISS_BUTTON"]');
expect(dismissButton).toBeTruthy();

expect(closeSpy).not.toHaveBeenCalled();

await waitFor(() => {
fireEvent.click(dismissButton as HTMLElement);
});

expect(closeSpy).toHaveBeenCalled();
});
});
8 changes: 8 additions & 0 deletions packages/paste-core/components/callout/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@
"tsc": "tsc"
},
"peerDependencies": {
"@twilio-paste/anchor": "^12.0.0",
"@twilio-paste/animation-library": "^2.0.0",
"@twilio-paste/box": "^10.0.0",
"@twilio-paste/button": "^14.0.0",
"@twilio-paste/color-contrast-utils": "^5.0.0",
"@twilio-paste/customization": "^8.0.0",
"@twilio-paste/design-tokens": "^10.0.0",
"@twilio-paste/icons": "^12.0.0",
"@twilio-paste/screen-reader-only": "^13.0.0",
"@twilio-paste/spinner": "^14.0.0",
"@twilio-paste/stack": "^8.0.0",
"@twilio-paste/style-props": "^9.0.0",
"@twilio-paste/styling-library": "^3.0.0",
"@twilio-paste/text": "^10.0.0",
Expand All @@ -44,13 +48,17 @@
"react-dom": "^16.8.6 || ^17.0.2 || ^18.0.0"
},
"devDependencies": {
"@twilio-paste/anchor": "^12.0.0",
"@twilio-paste/animation-library": "^2.0.0",
"@twilio-paste/box": "^10.1.0",
"@twilio-paste/button": "^14.1.0",
"@twilio-paste/color-contrast-utils": "^5.0.0",
"@twilio-paste/customization": "^8.1.0",
"@twilio-paste/design-tokens": "^10.2.0",
"@twilio-paste/icons": "^12.2.0",
"@twilio-paste/screen-reader-only": "^13.1.0",
"@twilio-paste/spinner": "^14.0.0",
"@twilio-paste/stack": "^8.0.0",
"@twilio-paste/style-props": "^9.1.0",
"@twilio-paste/styling-library": "^3.0.0",
"@twilio-paste/text": "^10.1.0",
Expand Down
77 changes: 59 additions & 18 deletions packages/paste-core/components/callout/src/Callout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Box, safelySpreadBoxProps } from "@twilio-paste/box";
import type { BoxProps, BoxStyleProps } from "@twilio-paste/box";
import { Button } from "@twilio-paste/button";
import { CloseIcon } from "@twilio-paste/icons/esm/CloseIcon";
import { ErrorIcon } from "@twilio-paste/icons/esm/ErrorIcon";
import { NeutralIcon } from "@twilio-paste/icons/esm/NeutralIcon";
import { NewIcon } from "@twilio-paste/icons/esm/NewIcon";
Expand Down Expand Up @@ -43,33 +45,48 @@ export interface CalloutProps extends HTMLPasteProps<"div"> {
* @memberof CalloutProps
*/
marginY?: BoxStyleProps["marginY"];
/**
* Function to run on dismiss of the Callout. Adds a close button.
*
* @default null
* @memberof CalloutProps
*/
onDismiss?: () => void;
/**
* Title for dismiss label. Only necessary when using onDismiss.
*
* @default 'Dismiss callout'
* @memberof CalloutProps
* @type {string}
*/
i18nDismissLabel?: string;
}

const variantStyles: Record<CalloutVariants, BoxStyleProps> = {
success: {
backgroundColor: "colorBackgroundSuccessWeakest",
backgroundColor: "colorBackgroundWeak",
color: "colorTextSuccess",
borderColor: "colorBorderSuccessWeaker",
borderColor: "colorBorderSuccessWeak",
},
error: {
backgroundColor: "colorBackgroundErrorWeakest",
color: "colorTextError",
borderColor: "colorBorderErrorWeaker",
borderColor: "colorBorderErrorWeak",
},
warning: {
backgroundColor: "colorBackgroundWarningWeakest",
backgroundColor: "colorBackgroundWeak",
color: "colorTextWarningStrong",
borderColor: "colorBorderWarningWeaker",
borderColor: "colorBorderWarningWeak",
},
new: {
backgroundColor: "colorBackgroundNewWeakest",
backgroundColor: "colorBackgroundWeak",
color: "colorTextNew",
borderColor: "colorBorderNewWeaker",
borderColor: "colorBorderNewWeak",
},
neutral: {
backgroundColor: "colorBackgroundNeutralWeakest",
backgroundColor: "colorBackgroundWeak",
color: "colorTextNeutral",
borderColor: "colorBorderNeutralWeaker",
borderColor: "colorBorderNeutralWeak",
},
};

Expand All @@ -90,7 +107,19 @@ const defaultIconLabels: Record<CalloutVariants, string> = {
};

export const Callout = React.forwardRef<HTMLDivElement, CalloutProps>(
({ children, variant, element = "CALLOUT", i18nLabel, marginY, ...props }, ref) => {
(
{
children,
variant,
element = "CALLOUT",
i18nLabel,
marginY,
onDismiss,
i18nDismissLabel = "Dismiss callout",
...props
},
ref,
) => {
const IconComponent = variantIcons[variant];
const iconLabel = i18nLabel ? i18nLabel : defaultIconLabels[variant];

Expand All @@ -100,19 +129,31 @@ export const Callout = React.forwardRef<HTMLDivElement, CalloutProps>(
ref={ref}
element={element}
display="flex"
flexDirection="column"
marginY={marginY}
padding="space60"
borderStyle="solid"
borderWidth="borderWidth10"
borderRadius="borderRadius30"
rowGap="space50"
paddingTop="space70"
paddingLeft="space70"
paddingRight="space70"
paddingBottom="space90"
borderLeftStyle="solid"
borderLeftWidth="borderWidth20"
variant={variant}
{...variantStyles[variant]}
>
<Box marginRight="space60" paddingTop="space10" element={`${element}_ICON`}>
{IconComponent}
<ScreenReaderOnly>{iconLabel}</ScreenReaderOnly>
<Box display="flex" justifyContent="space-between">
<Box element={`${element}_ICON`}>
{IconComponent}
<ScreenReaderOnly>{iconLabel}</ScreenReaderOnly>
</Box>
{onDismiss && typeof onDismiss === "function" && (
<Button onClick={onDismiss} variant="secondary_icon" size="reset" element={`${element}_DISMISS_BUTTON`}>
<CloseIcon element={`${element}_DISMISS_ICON`} decorative size="sizeIcon20" />
<ScreenReaderOnly>{i18nDismissLabel}</ScreenReaderOnly>
</Button>
)}
</Box>
<Box display="flex" flexDirection="column" rowGap="space50" flex="1">
<Box display="flex" flexDirection="column" rowGap="space30" flex="1">
{children}
</Box>
</Box>
Expand Down
43 changes: 43 additions & 0 deletions packages/paste-core/components/callout/stories/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,46 @@ export const CalloutWithMargin: StoryFn = () => (
</Callout>
</>
);

export const DismissibleVsNonDismissibleVariantsWithDetailedBody: StoryFn = () => {
return (
<Box display="flex" flexDirection="column" rowGap="space60">
{/* eslint-disable-next-line no-console */}
<Callout variant="new" onDismiss={() => console.log("dismissed!")}>
<CalloutHeading as="h3">New callout</CalloutHeading>
<CalloutText>Take a look at this list:</CalloutText>
<ExampleList as="ol" />
</Callout>
{/* eslint-disable-next-line no-console */}
<Callout variant="neutral" onDismiss={() => console.log("dismissed!")}>
<CalloutHeading as="h3">Neutral callout</CalloutHeading>
<CalloutText>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas bibendum lectus in congue dignissim. Donec
id blandit justo. Nam dignissim eros sem, sit amet commodo enim dapibus nec. Pellentesque at euismod mi.
Aenean dignissim fringilla ipsum, ac mattis justo vehicula sed. Nulla non vestibulum sapien, ut mollis leo.
Aliquam iaculis urna vel efficitur suscipit. Maecenas id mi vel est elementum varius. Maecenas lobortis, nisi
ut pulvinar accumsan, ante felis consequat neque, id mollis mi eros non eros. Quisque sagittis euismod enim ut
lobortis. Etiam ac diam efficitur, dapibus ligula sit amet, auctor ex. Cras lacinia, lacus id consequat
sollicitudin, augue ex laoreet felis, ac blandit tellus erat vel felis.
</CalloutText>
</Callout>
<Callout variant="new">
<CalloutHeading as="h3">New callout</CalloutHeading>
<CalloutText>Take a look at this list:</CalloutText>
<ExampleList as="ol" />
</Callout>
<Callout variant="neutral">
<CalloutHeading as="h3">Neutral callout</CalloutHeading>
<CalloutText>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas bibendum lectus in congue dignissim. Donec
id blandit justo. Nam dignissim eros sem, sit amet commodo enim dapibus nec. Pellentesque at euismod mi.
Aenean dignissim fringilla ipsum, ac mattis justo vehicula sed. Nulla non vestibulum sapien, ut mollis leo.
Aliquam iaculis urna vel efficitur suscipit. Maecenas id mi vel est elementum varius. Maecenas lobortis, nisi
ut pulvinar accumsan, ante felis consequat neque, id mollis mi eros non eros. Quisque sagittis euismod enim ut
lobortis. Etiam ac diam efficitur, dapibus ligula sit amet, auctor ex. Cras lacinia, lacus id consequat
sollicitudin, augue ex laoreet felis, ac blandit tellus erat vel felis.
</CalloutText>
</Callout>
</Box>
);
};
14 changes: 14 additions & 0 deletions packages/paste-core/components/callout/type-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,13 @@
"required": false,
"externalProp": true
},
"i18nDismissLabel": {
"type": "string",
"defaultValue": "'Dismiss callout'",
"required": false,
"externalProp": false,
"description": "Title for dismiss label. Only necessary when using onDismiss."
},
"i18nLabel": {
"type": "string",
"defaultValue": "'(neutral)' | '(warning)' | '(error)' | '(success)' | '(new)'",
Expand Down Expand Up @@ -724,6 +731,13 @@
"required": false,
"externalProp": true
},
"onDismiss": {
"type": "() => void",
"defaultValue": "null",
"required": false,
"externalProp": false,
"description": "Function to run on dismiss of the Callout. Adds a close button."
},
"onDoubleClick": {
"type": "MouseEventHandler<HTMLDivElement>",
"defaultValue": null,
Expand Down
Loading

0 comments on commit 31defba

Please sign in to comment.