diff --git a/.changeset/unlucky-schools-attend.md b/.changeset/unlucky-schools-attend.md
new file mode 100644
index 00000000000..c8155a73d7b
--- /dev/null
+++ b/.changeset/unlucky-schools-attend.md
@@ -0,0 +1,5 @@
+---
+'@shopify/polaris': minor
+---
+
+Added `tone`, `icon`, and `onClick` props to `Toast`
diff --git a/polaris-react/src/components/Frame/components/Toast/Toast.module.scss b/polaris-react/src/components/Frame/components/Toast/Toast.module.scss
index 69f6a8c32c6..fdacd980b83 100644
--- a/polaris-react/src/components/Frame/components/Toast/Toast.module.scss
+++ b/polaris-react/src/components/Frame/components/Toast/Toast.module.scss
@@ -66,3 +66,35 @@ $Backdrop-opacity: 0.88;
color: var(--p-color-text-inverse);
}
}
+
+.toneMagic {
+ background-color: var(--p-color-bg-fill-magic-secondary);
+ color: var(--p-color-text-magic);
+
+ .CloseButton {
+ color: var(--p-color-text-magic);
+ }
+
+ .Action {
+ color: var(--p-color-text-magic);
+ }
+}
+
+.WithActionOnComponent {
+ border: none;
+ cursor: pointer;
+ line-height: var(--p-font-line-height-500);
+ font-size: var(--p-font-size-325);
+ padding-right: var(--p-space-500);
+}
+
+.WithActionOnComponent.toneMagic {
+ &:focus,
+ &:hover {
+ background-color: var(--p-color-bg-fill-magic-secondary-hover);
+ }
+
+ &:active {
+ background-color: var(--p-color-bg-fill-magic-secondary-active);
+ }
+}
diff --git a/polaris-react/src/components/Frame/components/Toast/Toast.tsx b/polaris-react/src/components/Frame/components/Toast/Toast.tsx
index a1eb9e2eab9..32c0a1806ef 100644
--- a/polaris-react/src/components/Frame/components/Toast/Toast.tsx
+++ b/polaris-react/src/components/Frame/components/Toast/Toast.tsx
@@ -1,7 +1,7 @@
import React, {useEffect} from 'react';
import {AlertCircleIcon, XSmallIcon} from '@shopify/polaris-icons';
-import {classNames} from '../../../../utilities/css';
+import {classNames, variationName} from '../../../../utilities/css';
import {Key} from '../../../../types';
import {Button} from '../../../Button';
import {Icon} from '../../../Icon';
@@ -24,6 +24,9 @@ export function Toast({
duration,
error,
action,
+ tone,
+ onClick,
+ icon,
}: ToastProps) {
useEffect(() => {
let timeoutDuration = duration || DEFAULT_TOAST_DURATION;
@@ -67,20 +70,61 @@ export function Toast({
) : null;
- const leadingIconMarkup = error ? (
-
-
-
- ) : null;
+ let leadingIconMarkup = null;
+
+ if (error) {
+ leadingIconMarkup = (
+
+
+
+ );
+ } else if (icon) {
+ leadingIconMarkup = (
+
+
+
+ );
+ }
- const className = classNames(styles.Toast, error && styles.error);
+ const className = classNames(
+ styles.Toast,
+ error && styles.error,
+ tone && styles[variationName('tone', tone)],
+ );
+
+ if (!action && onClick) {
+ return (
+
+
+ {leadingIconMarkup}
+
+
+ {content}
+
+
+
+ );
+ }
return (
{leadingIconMarkup}
-
+
{content}
diff --git a/polaris-react/src/components/Frame/components/Toast/tests/Toast.test.tsx b/polaris-react/src/components/Frame/components/Toast/tests/Toast.test.tsx
index e6ed350d4eb..b7186f8ff0b 100644
--- a/polaris-react/src/components/Frame/components/Toast/tests/Toast.test.tsx
+++ b/polaris-react/src/components/Frame/components/Toast/tests/Toast.test.tsx
@@ -1,11 +1,13 @@
import React from 'react';
import {timer} from '@shopify/jest-dom-mocks';
import {mountWithApp} from 'tests/utilities';
+import {CheckIcon} from '@shopify/polaris-icons';
import {Button} from '../../../../Button';
import {Toast} from '../Toast';
import type {ToastProps} from '../Toast';
import {Key} from '../../../../../types';
+import {Icon} from '../../../../Icon';
interface HandlerMap {
[eventName: string]: any;
@@ -38,6 +40,13 @@ describe(' ', () => {
});
});
+ it('renders a Toast with the magic tone when tone is "magic"', () => {
+ const message = mountWithApp( );
+ expect(message).toContainReactComponent('div', {
+ className: 'Toast toneMagic',
+ });
+ });
+
describe('dismiss button', () => {
it('renders by default', () => {
const message = mountWithApp( );
@@ -45,6 +54,11 @@ describe(' ', () => {
});
});
+ it('renders a leading icon if an icon is provided', () => {
+ const message = mountWithApp( );
+ expect(message).toContainReactComponent(Icon, {source: CheckIcon});
+ });
+
describe('action', () => {
const mockAction = {
content: 'Do something',
@@ -193,6 +207,24 @@ describe(' ', () => {
expect(spy).not.toHaveBeenCalled();
});
});
+
+ describe('onClick', () => {
+ it('wraps the toast in a button when provided', () => {
+ const spy = jest.fn();
+ const message = mountWithApp( );
+
+ expect(message.find('button')).toContainReactText(mockProps.content);
+ });
+
+ it('fires the callback when the toast is clicked', () => {
+ const spy = jest.fn();
+ const message = mountWithApp( );
+
+ message.find('button')?.trigger('onClick');
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+ });
});
function noop() {}
diff --git a/polaris-react/src/components/Toast/Toast.stories.tsx b/polaris-react/src/components/Toast/Toast.stories.tsx
index dcd61516fc4..929e09f10ca 100644
--- a/polaris-react/src/components/Toast/Toast.stories.tsx
+++ b/polaris-react/src/components/Toast/Toast.stories.tsx
@@ -11,6 +11,7 @@ import {
BlockStack,
TextContainer,
} from '@shopify/polaris';
+import {MagicIcon} from '@shopify/polaris-icons';
export default {
component: Toast,
@@ -217,3 +218,77 @@ export function InsideModal() {
);
}
+
+export function Magic() {
+ const [active, setActive] = useState(false);
+
+ const toggleActive = useCallback(() => setActive((active) => !active), []);
+
+ const toastMarkup = active ? (
+
+ ) : null;
+
+ return (
+
+
+
+ Show Magic Toast
+ {toastMarkup}
+
+
+
+ );
+}
+
+export function WithOnClick() {
+ const [active, setActive] = useState(false);
+
+ const toggleActive = useCallback(() => setActive((active) => !active), []);
+
+ const toastMarkup = active ? (
+
+ ) : null;
+
+ return (
+
+
+
+ Show Magic Toast
+ {toastMarkup}
+
+
+
+ );
+}
+
+export function MagicWithOnClick() {
+ const [active, setActive] = useState(false);
+
+ const toggleActive = useCallback(() => setActive((active) => !active), []);
+
+ const toastMarkup = active ? (
+
+ ) : null;
+
+ return (
+
+
+
+ Show Magic Toast
+ {toastMarkup}
+
+
+
+ );
+}
diff --git a/polaris-react/src/utilities/frame/types.ts b/polaris-react/src/utilities/frame/types.ts
index e4dbb5ae5f7..290a512be95 100644
--- a/polaris-react/src/utilities/frame/types.ts
+++ b/polaris-react/src/utilities/frame/types.ts
@@ -1,4 +1,4 @@
-import type {Action} from '../../types';
+import type {Action, IconSource} from '../../types';
export interface Logo {
/** Provides a path for a logo used on a dark background */
@@ -53,7 +53,7 @@ export interface ContextualSaveBarProps {
// Toast
-export interface ToastProps {
+interface BaseToastProps {
/** The content that should appear in the toast message */
content: string;
/**
@@ -63,12 +63,26 @@ export interface ToastProps {
duration?: number;
/** Display an error toast. */
error?: boolean;
- /** Callback when the dismiss icon is clicked */
- onDismiss(): void;
+ /** Indicates the tone of the toast */
+ tone?: 'magic';
+ /** Icon prefix for the toast content */
+ icon?: IconSource;
+}
+
+interface ClickableToast {
+ /** Callback fired when the toast is clicked or keypressed */
+ onClick?(): void;
+}
+
+interface DismissableToast {
/** Adds an action next to the message */
action?: Action;
+ /** Callback when the dismiss icon is clicked */
+ onDismiss(): void;
}
+export type ToastProps = BaseToastProps & ClickableToast & DismissableToast;
+
export interface ToastID {
id: string;
}