Skip to content

Commit

Permalink
fix(Toast): icon on Promisse Toast was not rendered (#3805)
Browse files Browse the repository at this point in the history
The icon was not rendered when the Toast was triggered by a promise.
There was also an infinite render problem, solved in this commit.

BREAKING CHANGE: The createToastPromise function now receives three arguments. Check documentation for further details.
  • Loading branch information
DSil authored and mainframev committed Apr 17, 2023
1 parent 342bebc commit e50b66f
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 56 deletions.
2 changes: 1 addition & 1 deletion packages/orbit-components/package.json
Expand Up @@ -81,7 +81,7 @@
"dependencies": {
"@kiwicom/orbit-design-tokens": "^3.2.2",
"@popperjs/core": "^2.9.2",
"react-hot-toast": "^2.2.0",
"react-hot-toast": "^2.4.0",
"react-popper": "^2.2.5",
"react-uid": "^2.3.2"
},
Expand Down
23 changes: 15 additions & 8 deletions packages/orbit-components/src/Toast/README.md
Expand Up @@ -6,7 +6,7 @@ The Toast component consists of `ToastRoot` and `createToast`/`createToastPromis
import { ToastRoot, createToast } from "@kiwicom/orbit-components/lib/Toast";
```

It's better to use ToastRoot once at the root of your application with your other context providers and you can use `createToast` from anywhere after
It's better to use ToastRoot once at the root of your application with your other context providers and you can use `createToast` from anywhere after. The `createToast` function accepts two arguments. The first is required and is the message to be displayed on the toast. The second is an object with an `icon` key that receives the icon to be rendered on the toast.

```jsx
import React from "react";
Expand All @@ -25,16 +25,23 @@ const App = () => {
};
```

You can also use `createToastPromise` function, notification will be updated automatically, when promise will be resolved or rejected
You can also use the `createToastPromise` function. The notification toast will be updated automatically, when the promise is resolved or rejected. The function receives two mandatory arguments and one optional. The first is the promise, the second is the message on the different statuses and the third one controls the icon displayed on each status (or all).

```jsx
const notify = () =>
createPromiseToast(promise, {
icon: <Notification />,
loading: "...Loading",
success: "Got the data",
error: "Error when fetching",
});
createPromiseToast(
promise,
{
loading: "...Loading",
success: "Got the data",
error: "Error when fetching",
},
{
success: {
icon: <Notification />,
},
},
);
```

## Props
Expand Down
20 changes: 13 additions & 7 deletions packages/orbit-components/src/Toast/Toast.stories.tsx
Expand Up @@ -54,13 +54,19 @@ export const WithPromise = () => {
setTimeout(Math.random() > 0.5 ? res : rej, 3000);
});

createToastPromise(promise, {
/* @ts-expect-error ignore icon as it currently has no effect */
icon: Notification,
loading: "...Loading",
success: "Freddy Krueger has nightmares about Chuck Norris!",
error: "Chuck did not come",
});
createToastPromise(
promise,
{
loading: "...Loading",
success: "Freddy Krueger has nightmares about Chuck Norris!",
error: "Chuck did not come",
},
{
success: {
icon: <Notification />,
},
},
);
};

return (
Expand Down
13 changes: 4 additions & 9 deletions packages/orbit-components/src/Toast/ToastMessage.tsx
Expand Up @@ -5,10 +5,9 @@ import mq from "../utils/mediaQuery";
import Stack from "../Stack";
import defaultTheme from "../defaultTheme";
import Text from "../Text";
import { fadeIn, fadeOut, lightAnimation, getPositionStyle, createRectRef } from "./helpers";
import { fadeIn, fadeOut, lightAnimation, getPositionStyle } from "./helpers";
import useTheme from "../hooks/useTheme";
import useSwipe from "./hooks/useSwipe";
import mergeRefs from "../utils/mergeRefs";
import type { Toast as Props } from "./types";

const StyledWrapper = styled(({ className, children, ariaLive }) => (
Expand Down Expand Up @@ -79,8 +78,6 @@ StyledInnerWrapper.defaultProps = {
};

const ToastMessage = ({
id,
onUpdateHeight,
onMouseEnter,
onMouseLeave,
visible,
Expand All @@ -93,12 +90,10 @@ const ToastMessage = ({
ariaLive,
}: Props) => {
const theme = useTheme();
const measurerRef = createRectRef(({ height }) => onUpdateHeight(id, height));
const innerRef = React.useRef(null);
const mergedRef = mergeRefs<HTMLDivElement>([measurerRef, innerRef]);
const ref = React.useRef(null);
const [isPaused, setPaused] = React.useState(false);
const { swipeOffset, swipeOpacity } = useSwipe(
innerRef,
ref,
onDismiss,
50,
placement.match(/right|center/) ? "right" : "left",
Expand All @@ -115,7 +110,7 @@ const ToastMessage = ({
>
<StyledInnerWrapper
visible={visible}
ref={mergedRef}
ref={ref}
isPaused={isPaused}
duration={dismissTimeout}
onMouseEnter={() => {
Expand Down
5 changes: 1 addition & 4 deletions packages/orbit-components/src/Toast/ToastRoot.tsx
Expand Up @@ -38,9 +38,7 @@ const ToastRoot = ({
duration: dismissTimeout,
});

const { startPause, endPause, calculateOffset, updateHeight } = handlers;
// eslint-disable-next-line react-hooks/exhaustive-deps
const handleUpdateHeight = React.useCallback(updateHeight, []);
const { startPause, endPause, calculateOffset } = handlers;

return (
<StyledWrapper
Expand All @@ -66,7 +64,6 @@ const ToastRoot = ({
visible={visible}
icon={icon}
offset={offset}
onUpdateHeight={handleUpdateHeight}
onMouseEnter={startPause}
onMouseLeave={endPause}
placement={placement}
Expand Down
Expand Up @@ -34,7 +34,6 @@ describe("Toast", () => {
<Toast
id="1"
icon={<Airplane dataTest="airplane" />}
onUpdateHeight={() => {}}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
ariaLive="polite"
Expand Down
16 changes: 10 additions & 6 deletions packages/orbit-components/src/Toast/index.js.flow
Expand Up @@ -20,7 +20,6 @@ export type Toast = {|
+visible?: boolean,
+children: React.Node,
+dismissTimeout?: number,
+onUpdateHeight: (id: string, height: number) => void,
+onMouseEnter: () => void,
+onMouseLeave: () => void,
+onDismiss: () => void,
Expand All @@ -47,11 +46,16 @@ type Options = {|
error?: string | ((err: any) => string),
|};

export type createToastType = (
message: React.Node,
options?: {| icon?: React.Element<any> |},
) => void;
export type createToastPromiseType = (promise: Promise<void>, options?: Options) => Promise<void>;
type IconType = {| icon?: React.Element<any> |};
type ToastType = "error" | "success" | "loading" | "blank" | "custom";

export type createToastType = (message: React.Node, options?: IconType) => void;

export type createToastPromiseType = (
promise: Promise<void>,
messages: Options,
options?: IconType & $Partial<{ [k: ToastType]: IconType }>,
) => Promise<void>;

declare export var ToastRoot: React.ComponentType<Props>;
declare export var createToast: createToastType;
Expand Down
4 changes: 2 additions & 2 deletions packages/orbit-components/src/Toast/index.tsx
Expand Up @@ -10,7 +10,7 @@ const createToast: createToastType = (content, options) => {
toast(content, options);
};

const createToastPromise: createToastPromiseType = (content, options) =>
toast.promise(content, options);
const createToastPromise: createToastPromiseType = (content, messages, options?) =>
toast.promise(content, messages, options);

export { ToastRoot, createToast, createToastPromise };
22 changes: 13 additions & 9 deletions packages/orbit-components/src/Toast/types.d.ts
Expand Up @@ -4,7 +4,8 @@ import type * as React from "react";
import type {
ValueOrFunction,
Renderable,
Toast as ToastType,
Toast,
ToastType,
DefaultToastOptions,
} from "react-hot-toast";

Expand All @@ -28,13 +29,12 @@ export interface Props extends Common.Globals {
readonly placement?: Placement;
}

export interface Toast {
interface ToastProps {
readonly id: string;
readonly icon?: Renderable;
readonly visible?: boolean;
readonly children: React.ReactNode;
readonly dismissTimeout?: number;
readonly onUpdateHeight: (id: string, height: number) => void;
readonly onMouseEnter: () => void;
readonly onMouseLeave: () => void;
readonly onDismiss: () => void;
Expand All @@ -44,15 +44,19 @@ export interface Toast {
}

export interface Options<T> {
readonly icon: Pick<DefaultToastOptions, "icon">;
readonly loading: Renderable;
readonly success: ValueOrFunction<Renderable, T>;
readonly error: ValueOrFunction<Renderable, any>;
}

export type createToast = (
message: ValueOrFunction<Renderable, ToastType>,
options?: Pick<DefaultToastOptions, "icon">,
) => void;
export type IconType = Pick<DefaultToastOptions, "icon">;

export type createToastPromise = <T>(promise: Promise<T>, options: Options<T>) => Promise<T>;
export type createToast = (message: ValueOrFunction<Renderable, Toast>, options?: IconType) => void;

export type createToastPromise = <T>(
promise: Promise<T>,
messages: Options<T>,
options?: IconType & Partial<Record<ToastType, IconType>>,
) => Promise<T>;

export { ToastProps as Toast };
18 changes: 9 additions & 9 deletions yarn.lock
Expand Up @@ -19224,10 +19224,10 @@ gm@^1.23.1:
cross-spawn "^4.0.0"
debug "^3.1.0"

goober@^2.1.1:
version "2.1.9"
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.9.tgz#0faee08fab1a5d55b23e9ec043bb5a1b46fa025a"
integrity sha512-PAtnJbrWtHbfpJUIveG5PJIB6Mc9Kd0gimu9wZwPyA+wQUSeOeA4x4Ug16lyaaUUKZ/G6QEH1xunKOuXP1F4Vw==
goober@^2.1.10:
version "2.1.12"
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.12.tgz#6c1645314ac9a68fe76408e1f502c63df8a39042"
integrity sha512-yXHAvO08FU1JgTXX6Zn6sYCUFfB/OJSX8HHjDSgerZHZmFKAb08cykp5LBw5QnmyMcZyPRMqkdyHUSSzge788Q==

good-listener@^1.2.2:
version "1.2.2"
Expand Down Expand Up @@ -27763,12 +27763,12 @@ react-hot-loader@^4.13.0:
shallowequal "^1.1.0"
source-map "^0.7.3"

react-hot-toast@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.2.0.tgz#ab6f4caed4214b9534f94bb8cfaaf21b051e62b9"
integrity sha512-248rXw13uhf/6TNDVzagX+y7R8J183rp7MwUMNkcrBRyHj/jWOggfXTGlM8zAOuh701WyVW+eUaWG2LeSufX9g==
react-hot-toast@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.0.tgz#b91e7a4c1b6e3068fc599d3d83b4fb48668ae51d"
integrity sha512-qnnVbXropKuwUpriVVosgo8QrB+IaPJCpL8oBI6Ov84uvHZ5QQcTp2qg6ku2wNfgJl6rlQXJIQU5q+5lmPOutA==
dependencies:
goober "^2.1.1"
goober "^2.1.10"

react-input-autosize@^3.0.0:
version "3.0.0"
Expand Down

0 comments on commit e50b66f

Please sign in to comment.