Skip to content

Commit 8f3d52f

Browse files
authored
docs(blade): Toast API decisions (#1990)
1 parent 46a5c16 commit 8f3d52f

File tree

4 files changed

+267
-0
lines changed

4 files changed

+267
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# Toast 🏷️
2+
3+
Toasts are used to provide feedback to the user after an important action has been performed.
4+
Toasts can also be used to provide feedback to the user when a system event occurs, such as when a file is saved.
5+
6+
<img width="426" alt="image" src="./toast-thumbnail.png" />
7+
8+
## Design
9+
10+
- [Toast - Figma Design](https://www.figma.com/file/jubmQL9Z8V7881ayUD95ps/Blade-DSL?type=design&node-id=7665-27414&mode=design&t=UNInCMmP1iFCu9je-0)
11+
12+
## Features
13+
14+
- Stackable
15+
- Auto dismissable / manual dismissable
16+
- `informational` or `promotional` types
17+
18+
## Anatomy
19+
20+
### Informational Toast
21+
22+
<img src="./toast-anatomy.png" width="100%" alt="Toast anatomy" />
23+
24+
### Promotional Toast
25+
26+
<img src="./example-promotional-toast.png" width="380" alt="Promotional toast example" />
27+
28+
## Simple Usage
29+
30+
The blade toasts will use [react-hot-toast](https://react-hot-toast.com/) under the hood with a similar imperative API to show, dismiss and create new toasts without needing for consumer to handle positional or stacking logic.
31+
32+
```jsx
33+
import { BladeProvider, ToastContainer, useToast } from "@razorpay/blade/components"
34+
35+
const HomePage = () => {
36+
const toast = useToast();
37+
38+
return (
39+
<Button
40+
onClick={() => {
41+
toast.show({
42+
type: 'informational',
43+
color: 'success',
44+
content: 'Payment Successful',
45+
leading: <DollarIcon />,
46+
autoDismiss: true,
47+
onDismissButtonClick: () => {
48+
console.log('Toast dismissed');
49+
},
50+
action: {
51+
text: 'View',
52+
onClick: () => {
53+
console.log('Toast action clicked');
54+
}
55+
}
56+
});
57+
}}
58+
>
59+
Show Toast
60+
</Button>
61+
);
62+
};
63+
64+
const App = () => {
65+
return (
66+
<BladeProvider>
67+
<ToastContainer position="bottom-left" />
68+
<HomePage />
69+
</BladeProvider>
70+
)
71+
}
72+
```
73+
74+
## Props
75+
76+
```ts
77+
type ToastProps = {
78+
/**
79+
* @default `informational`
80+
*/
81+
type?: 'informational' | 'promotional';
82+
83+
/**
84+
* @default `neutral`
85+
*/
86+
color?: 'neutral' | 'positive' | 'negative' | 'warning' | 'information'
87+
88+
/**
89+
* Content of the toast
90+
*/
91+
content: React.ReactNode;
92+
93+
/**
94+
* Can be used to render an icon or asset
95+
*/
96+
leading?: React.ReactElement;
97+
98+
/**
99+
* If true, the toast will be dismissed after few seconds
100+
*
101+
* @default true - for informational toast
102+
* @default false - for promotional toast
103+
*/
104+
autoDismiss?: boolean;
105+
106+
/**
107+
* Duration after which the toast will be dismissed (in ms)
108+
*
109+
* Duration for promotional toast is 8s
110+
* Duration for informational toast is 4s
111+
*/
112+
duration?: 8000 | 4000;
113+
114+
/**
115+
* Called when the toast is dismissed or duration runs out
116+
*/
117+
onDismissButtonClick?: () => void;
118+
119+
/**
120+
* Primary action of toast
121+
*/
122+
action?: {
123+
text: string;
124+
onClick?: (e: Event) => void;
125+
isLoading? boolean;
126+
}
127+
128+
/**
129+
* Forwarded to react-hot-toast
130+
*
131+
* This can be used to programatically update toasts by providing an id
132+
*/
133+
toastId?: string;
134+
}
135+
```
136+
137+
### useToast
138+
139+
The useToast hook will return few modified methods from [react-hot-toast](https://react-hot-toast.com/docs/toast):
140+
141+
```ts
142+
type useToastReturnType = {
143+
/**
144+
* @returns id of the toast
145+
*/
146+
show: (toast: Toast) => string;
147+
148+
/**
149+
* id of the toast to be dismissed
150+
*
151+
* if id is not provided, all the toasts will be dismissed
152+
*/
153+
dismiss: (toastId?: string) => void;
154+
}
155+
```
156+
157+
## Examples
158+
159+
#### Removing a toast
160+
161+
react-hot-toast provides this functionality, for more info see [react-hot-toast docs](https://react-hot-toast.com/docs/toast#dismiss-toast-programmatically)
162+
163+
```jsx
164+
import { BladeProvider, ToastContainer, useToast } from "@razorpay/blade/components"
165+
166+
const Example = () => {
167+
const toastId = React.useRef(null);
168+
const toast = useToast();
169+
170+
return (
171+
<Box>
172+
<Button
173+
onClick={() => {
174+
toastId.current = toast.show({
175+
color: 'success',
176+
content: 'Payment Successful',
177+
});
178+
}}
179+
>
180+
Show Toast
181+
</Button>
182+
<Button onClick={() => toast.dismiss(toastId.current)}>Dismiss Toast</Button>
183+
</Box>
184+
);
185+
};
186+
```
187+
188+
### Promotional Toast
189+
190+
```jsx
191+
import { BladeProvider, ToastContainer, useToast } from "@razorpay/blade/components"
192+
193+
const Example = () => {
194+
const toast = useToast();
195+
196+
return (
197+
<Button
198+
onClick={() => {
199+
toast.show({
200+
type: 'promotional',
201+
content: (
202+
<Box>
203+
<Heading>Payment Successful</Heading>
204+
<Text>Amount:100</Text>
205+
</Box>
206+
),
207+
leading: <DollarIcon />,
208+
action: {
209+
text: 'Okay'
210+
}
211+
});
212+
}}
213+
>
214+
Do payment
215+
</Button>
216+
);
217+
};
218+
```
219+
220+
<img src="./example-promotional-toast.png" width="380" alt="Promotional toast example" />
221+
222+
223+
## Motion
224+
225+
You can checkout the toast motion [here](https://www.figma.com/proto/jubmQL9Z8V7881ayUD95ps/Blade-DSL?type=design&node-id=75848-1063056&t=QZkki0ZrlcG4sKzw-0&scaling=min-zoom&page-id=7665%3A27414).
226+
227+
## Accessibility
228+
229+
- Toast components will follow the WAI-ARIA guidelines for [alert](https://www.w3.org/WAI/ARIA/apg/patterns/alert/examples/alert/). For error toast we will use the `alert` role and for informational toast we will use the `status` role.
230+
231+
232+
## References
233+
234+
- https://react-hot-toast.com/docs
235+
- https://chakra-ui.com/docs/components/toast/usage
236+
237+
## Open Questions
238+
239+
- Q. What should be the default duration for auto dismissable toasts?
240+
- A. 4s for informational toasts and 8s for promotional toasts
241+
242+
- Q. Should we call it `onDismissButtonClick` or `onDismiss`?
243+
244+
- Q. Should the dismiss handler be called even when the toast is auto dismissed? Or should we have different handlers for auto dismiss and manual dismiss? (eg: `onAutoDismiss` `onDismissButtonClick`)
245+
- A. We will call it `onDismissButtonClick` and it will only be called when the user clicks on the dismiss button.
246+
247+
- Q. In the `useToast` hook should we call the returned functions `showToast`/`dismissToast` or `show`/`dismiss`?
248+
249+
If we call them `show` & `dismiss` consumer can do this and might look more cleaner:
250+
251+
```jsx
252+
const App = () => {
253+
const toast = useToast();
254+
255+
toast.show(); // <-- they will also get auto complete when writing `toast.`
256+
toast.dismiss();
257+
}
258+
```
259+
- A. We will call them `show` & `dismiss`
260+
261+
- Q. In design we are restricting the Toast position to be bottom-left. In that case should we also do the same or should we allow all the positions & set the default to bottom-left? (If we allow all the positions, we will have to add some additional logic to handle stacking/animation of toasts coming from top instead of bottom)
262+
- A. We will only allow bottom-left position for now.
263+
264+
- Q. Should we keep the ToastContainer inside BladeProvider?
265+
266+
The problem is if we keep it inside BladeProvider, given our new light/dark mode setup where consumers will need to nest BladeProviders it would cause ToastContainer to render [multiple times](https://github.com/razorpay/blade/pull/1990#discussion_r1470796627).
267+
- A. We will expose ToastContainer, so that users can render it at the root level of their app regardless of where BladeProvider is.
Loading
Loading
Loading

0 commit comments

Comments
 (0)