• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fkhadra / react-toastify / 12001201729

25 Nov 2024 12:54AM UTC coverage: 42.537% (-46.5%) from 88.998%
12001201729

Pull #1178

github

fkhadra
bump coverall action
Pull Request #1178: [WIP] V11

141 of 375 branches covered (37.6%)

Branch coverage included in aggregate %.

3 of 5 new or added lines in 3 files covered. (60.0%)

214 existing lines in 9 files now uncovered.

201 of 429 relevant lines covered (46.85%)

176.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

19.77
/src/core/toast.ts
1
import {
2
  ClearWaitingQueueFunc,
3
  Id,
4
  IdOpts,
5
  NotValidatedToastProps,
6
  OnChangeCallback,
7
  ToastContent,
8
  ToastOptions,
9
  ToastProps,
10
  TypeOptions,
11
  UpdateOptions
12
} from '../types';
13
import { Type, isFn, isNum, isStr } from '../utils';
14
import { genToastId } from './genToastId';
15
import {
16
  clearWaitingQueue,
17
  getToast,
18
  isToastActive,
19
  onChange,
20
  pushToast,
21
  removeToast,
22
  toggleToast
23
} from './store';
24

25
/**
26
 * Generate a toastId or use the one provided
27
 */
28
function getToastId<TData>(options?: ToastOptions<TData>) {
UNCOV
29
  return options && (isStr(options.toastId) || isNum(options.toastId))
×
30
    ? options.toastId
31
    : genToastId();
32
}
33

34
/**
35
 * If the container is not mounted, the toast is enqueued
36
 */
37
function dispatchToast<TData>(
38
  content: ToastContent<TData>,
39
  options: NotValidatedToastProps
40
): Id {
UNCOV
41
  pushToast(content, options);
×
UNCOV
42
  return options.toastId;
×
43
}
44

45
/**
46
 * Merge provided options with the defaults settings and generate the toastId
47
 */
48
function mergeOptions<TData>(type: string, options?: ToastOptions<TData>) {
UNCOV
49
  return {
×
50
    ...options,
51
    type: (options && options.type) || type,
×
52
    toastId: getToastId(options)
53
  } as NotValidatedToastProps;
54
}
55

56
function createToastByType(type: string) {
57
  return <TData = unknown>(
184✔
58
    content: ToastContent<TData>,
59
    options?: ToastOptions<TData>
UNCOV
60
  ) => dispatchToast(content, mergeOptions(type, options));
×
61
}
62

63
function toast<TData = unknown>(
64
  content: ToastContent<TData>,
65
  options?: ToastOptions<TData>
66
) {
UNCOV
67
  return dispatchToast(content, mergeOptions(Type.DEFAULT, options));
×
68
}
69

70
toast.loading = <TData = unknown>(
46✔
71
  content: ToastContent<TData>,
72
  options?: ToastOptions<TData>
73
) =>
UNCOV
74
  dispatchToast(
×
75
    content,
76
    mergeOptions(Type.DEFAULT, {
77
      isLoading: true,
78
      autoClose: false,
79
      closeOnClick: false,
80
      closeButton: false,
81
      draggable: false,
82
      ...options
83
    })
84
  );
85

86
export interface ToastPromiseParams<
87
  TData = unknown,
88
  TError = unknown,
89
  TPending = unknown
90
> {
91
  pending?: string | UpdateOptions<TPending>;
92
  success?: string | UpdateOptions<TData>;
93
  error?: string | UpdateOptions<TError>;
94
}
95

96
function handlePromise<TData = unknown, TError = unknown, TPending = unknown>(
97
  promise: Promise<TData> | (() => Promise<TData>),
98
  { pending, error, success }: ToastPromiseParams<TData, TError, TPending>,
99
  options?: ToastOptions<TData>
100
) {
101
  let id: Id;
102

UNCOV
103
  if (pending) {
×
UNCOV
104
    id = isStr(pending)
×
105
      ? toast.loading(pending, options)
106
      : toast.loading(pending.render, {
107
          ...options,
108
          ...(pending as ToastOptions)
109
        } as ToastOptions<TPending>);
110
  }
111

UNCOV
112
  const resetParams = {
×
113
    isLoading: null,
114
    autoClose: null,
115
    closeOnClick: null,
116
    closeButton: null,
117
    draggable: null
118
  };
119

UNCOV
120
  const resolver = <T>(
×
121
    type: TypeOptions,
122
    input: string | UpdateOptions<T> | undefined,
123
    result: T
124
  ) => {
125
    // Remove the toast if the input has not been provided. This prevents the toast from hanging
126
    // in the pending state if a success/error toast has not been provided.
UNCOV
127
    if (input == null) {
×
128
      toast.dismiss(id);
×
129
      return;
×
130
    }
131

UNCOV
132
    const baseParams = {
×
133
      type,
134
      ...resetParams,
135
      ...options,
136
      data: result
137
    };
UNCOV
138
    const params = isStr(input) ? { render: input } : input;
×
139

140
    // if the id is set we know that it's an update
UNCOV
141
    if (id) {
×
UNCOV
142
      toast.update(id, {
×
143
        ...baseParams,
144
        ...params
145
      } as UpdateOptions);
146
    } else {
147
      // using toast.promise without loading
148
      toast(params!.render, {
×
149
        ...baseParams,
150
        ...params
151
      } as ToastOptions<T>);
152
    }
153

UNCOV
154
    return result;
×
155
  };
156

UNCOV
157
  const p = isFn(promise) ? promise() : promise;
×
158

159
  //call the resolvers only when needed
UNCOV
160
  p.then(result => resolver('success', success, result)).catch(err =>
×
UNCOV
161
    resolver('error', error, err)
×
162
  );
163

UNCOV
164
  return p;
×
165
}
166

167
/**
168
 * Supply a promise or a function that return a promise and the notification will be updated if it resolves or fails.
169
 * When the promise is pending a spinner is displayed by default.
170
 * `toast.promise` returns the provided promise so you can chain it.
171
 *
172
 * Simple example:
173
 *
174
 * ```
175
 * toast.promise(MyPromise,
176
 *  {
177
 *    pending: 'Promise is pending',
178
 *    success: 'Promise resolved 👌',
179
 *    error: 'Promise rejected 🤯'
180
 *  }
181
 * )
182
 *
183
 * ```
184
 *
185
 * Advanced usage:
186
 * ```
187
 * toast.promise<{name: string}, {message: string}, undefined>(
188
 *    resolveWithSomeData,
189
 *    {
190
 *      pending: {
191
 *        render: () => "I'm loading",
192
 *        icon: false,
193
 *      },
194
 *      success: {
195
 *        render: ({data}) => `Hello ${data.name}`,
196
 *        icon: "🟢",
197
 *      },
198
 *      error: {
199
 *        render({data}){
200
 *          // When the promise reject, data will contains the error
201
 *          return <MyErrorComponent message={data.message} />
202
 *        }
203
 *      }
204
 *    }
205
 * )
206
 * ```
207
 */
208
toast.promise = handlePromise;
46✔
209
toast.success = createToastByType(Type.SUCCESS);
46✔
210
toast.info = createToastByType(Type.INFO);
46✔
211
toast.error = createToastByType(Type.ERROR);
46✔
212
toast.warning = createToastByType(Type.WARNING);
46✔
213
toast.warn = toast.warning;
46✔
214
toast.dark = (content: ToastContent, options?: ToastOptions) =>
46✔
UNCOV
215
  dispatchToast(
×
216
    content,
217
    mergeOptions(Type.DEFAULT, {
218
      theme: 'dark',
219
      ...options
220
    })
221
  );
222

223
interface RemoveParams {
224
  id?: Id;
225
  containerId: Id;
226
}
227

228
function dismiss(params: RemoveParams): void;
229
function dismiss(params?: Id): void;
230
function dismiss(params?: Id | RemoveParams) {
UNCOV
231
  removeToast(params);
×
232
}
233

234
/**
235
 * Remove toast programmatically
236
 *
237
 * - Remove all toasts:
238
 * ```
239
 * toast.dismiss()
240
 * ```
241
 *
242
 * - Remove all toasts that belongs to a given container
243
 * ```
244
 * toast.dismiss({ container: "123" })
245
 * ```
246
 *
247
 * - Remove toast that has a given id regardless the container
248
 * ```
249
 * toast.dismiss({ id: "123" })
250
 * ```
251
 *
252
 * - Remove toast that has a given id for a specific container
253
 * ```
254
 * toast.dismiss({ id: "123", containerId: "12" })
255
 * ```
256
 */
257
toast.dismiss = dismiss;
46✔
258

259
/**
260
 * Clear waiting queue when limit is used
261
 */
262
toast.clearWaitingQueue = clearWaitingQueue as ClearWaitingQueueFunc;
46✔
263

264
/**
265
 * Check if a toast is active
266
 *
267
 * - Check regardless the container
268
 * ```
269
 * toast.isActive("123")
270
 * ```
271
 *
272
 * - Check in a specific container
273
 * ```
274
 * toast.isActive("123", "containerId")
275
 * ```
276
 */
277
toast.isActive = isToastActive;
46✔
278

279
/**
280
 * Update a toast, see https://fkhadra.github.io/react-toastify/update-toast/ for more
281
 *
282
 * Example:
283
 * ```
284
 * // With a string
285
 * toast.update(toastId, {
286
 *    render: "New content",
287
 *    type: "info",
288
 * });
289
 *
290
 * // Or with a component
291
 * toast.update(toastId, {
292
 *    render: MyComponent
293
 * });
294
 *
295
 * // Or a function
296
 * toast.update(toastId, {
297
 *    render: () => <div>New content</div>
298
 * });
299
 *
300
 * // Apply a transition
301
 * toast.update(toastId, {
302
 *   render: "New Content",
303
 *   type: toast.TYPE.INFO,
304
 *   transition: Rotate
305
 * })
306
 * ```
307
 */
308
toast.update = <TData = unknown>(
46✔
309
  toastId: Id,
310
  options: UpdateOptions<TData> = {}
×
311
) => {
UNCOV
312
  const toast = getToast(toastId, options as ToastOptions);
×
313

UNCOV
314
  if (toast) {
×
UNCOV
315
    const { props: oldOptions, content: oldContent } = toast;
×
316

UNCOV
317
    const nextOptions = {
×
318
      delay: 100,
319
      ...oldOptions,
320
      ...options,
321
      toastId: options.toastId || toastId,
×
322
      updateId: genToastId()
323
    } as ToastProps & UpdateOptions;
324

UNCOV
325
    if (nextOptions.toastId !== toastId) nextOptions.staleId = toastId;
×
326

UNCOV
327
    const content = nextOptions.render || oldContent;
×
UNCOV
328
    delete nextOptions.render;
×
329

UNCOV
330
    dispatchToast(content, nextOptions);
×
331
  }
332
};
333

334
/**
335
 * Used for controlled progress bar. It will automatically close the notification.
336
 *
337
 * If you don't want your notification to be clsoed when the timer is done you should use `toast.update` instead as follow instead:
338
 *
339
 * ```
340
 * toast.update(id, {
341
 *    progress: null, // remove controlled progress bar
342
 *    render: "ok",
343
 *    type: "success",
344
 *    autoClose: 5000 // set autoClose to the desired value
345
 *   });
346
 * ```
347
 */
348
toast.done = (id: Id) => {
46✔
UNCOV
349
  toast.update(id, {
×
350
    progress: 1
351
  });
352
};
353

354
/**
355
 * Subscribe to change when a toast is added, removed and updated
356
 *
357
 * Usage:
358
 * ```
359
 * const unsubscribe = toast.onChange((payload) => {
360
 *   switch (payload.status) {
361
 *   case "added":
362
 *     // new toast added
363
 *     break;
364
 *   case "updated":
365
 *     // toast updated
366
 *     break;
367
 *   case "removed":
368
 *     // toast has been removed
369
 *     break;
370
 *   }
371
 * })
372
 * ```
373
 */
374
toast.onChange = onChange as (cb: OnChangeCallback) => () => void;
46✔
375

376
/**
377
 * Play a toast(s) timer progammatically
378
 *
379
 * Usage:
380
 *
381
 * - Play all toasts
382
 * ```
383
 * toast.play()
384
 * ```
385
 *
386
 * - Play all toasts for a given container
387
 * ```
388
 * toast.play({ containerId: "123" })
389
 * ```
390
 *
391
 * - Play toast that has a given id regardless the container
392
 * ```
393
 * toast.play({ id: "123" })
394
 * ```
395
 *
396
 * - Play toast that has a given id for a specific container
397
 * ```
398
 * toast.play({ id: "123", containerId: "12" })
399
 * ```
400
 */
401
toast.play = (opts?: IdOpts) => toggleToast(true, opts);
46✔
402

403
/**
404
 * Pause a toast(s) timer progammatically
405
 *
406
 * Usage:
407
 *
408
 * - Pause all toasts
409
 * ```
410
 * toast.pause()
411
 * ```
412
 *
413
 * - Pause all toasts for a given container
414
 * ```
415
 * toast.pause({ containerId: "123" })
416
 * ```
417
 *
418
 * - Pause toast that has a given id regardless the container
419
 * ```
420
 * toast.pause({ id: "123" })
421
 * ```
422
 *
423
 * - Pause toast that has a given id for a specific container
424
 * ```
425
 * toast.pause({ id: "123", containerId: "12" })
426
 * ```
427
 */
428
toast.pause = (opts?: IdOpts) => toggleToast(false, opts);
46✔
429

430
export { toast };
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc