Created
June 22, 2019 04:29
-
-
Save jsmanifest/04127b5a3b191b232f3c40512645714b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { isValidElement } from 'react' | |
import isString from 'lodash/isString' | |
import isFunction from 'lodash/isFunction' | |
import { GoCheck, GoAlert } from 'react-icons/go' | |
import { FaInfoCircle } from 'react-icons/fa' | |
import { MdPriorityHigh } from 'react-icons/md' | |
import { toast } from 'react-toastify' | |
/* | |
Calling these toasts most likely happens in the UI 100% of the time. | |
So it is safe to render components/elements as toasts. | |
*/ | |
// Keeping all the toast ids used throughout the app here so we can easily manage/update over time | |
// This used to show only one toast at a time so the user doesn't get spammed with toast popups | |
export const toastIds = { | |
// APP | |
internetOnline: 'internet-online', | |
internetOffline: 'internet-offline', | |
retryInternet: 'internet-retry', | |
} | |
// Note: this toast && is a conditional escape hatch for unit testing to avoid an error. | |
const getDefaultOptions = (options) => ({ | |
position: toast && toast.POSITION.BOTTOM_RIGHT, | |
...options, | |
}) | |
const Toast = ({ children, success, error, info, warning }) => { | |
let componentChildren | |
// Sometimes we are having an "object is not valid as a react child" error and children magically becomes an API error response, so we must use this fallback string | |
if (!isValidElement(children) && !isString(children)) { | |
componentChildren = 'An error occurred' | |
} else { | |
componentChildren = children | |
} | |
let Icon = GoAlert | |
if (success) Icon = GoCheck | |
if (error) Icon = GoAlert | |
if (info) Icon = FaInfoCircle | |
if (warning) Icon = MdPriorityHigh | |
return ( | |
<div style={{ paddingLeft: 10, display: 'flex', alignItems: 'center' }}> | |
<div style={{ width: 30, height: 30 }}> | |
<Icon style={{ color: '#fff', width: 30, height: 30 }} /> | |
</div> | |
<div style={{ padding: 8, display: 'flex', alignItems: 'center' }}> | |
| |
<span style={{ color: '#fff' }}>{componentChildren}</span> | |
</div> | |
</div> | |
) | |
} | |
const toaster = (function() { | |
// Attempt to remove a duplicate toast if it is on the screen | |
const ensurePreviousToastIsRemoved = (toastId) => { | |
if (toastId) { | |
if (toast.isActive(toastId)) { | |
toast.dismiss(toastId) | |
} | |
} | |
} | |
// Try to get the toast id if provided from options | |
const attemptGetToastId = (msg, opts) => { | |
let toastId | |
if (opts && isString(opts.toastId)) { | |
toastId = opts.toastId | |
} else if (isString(msg)) { | |
// We'll just make the string the id by default if its a string | |
toastId = msg | |
} | |
return toastId | |
} | |
const handleToast = (type) => (msg, opts) => { | |
const toastFn = toast[type] | |
if (isFunction(toastFn)) { | |
const toastProps = {} | |
let className = '' | |
const additionalOptions = {} | |
const toastId = attemptGetToastId(msg, opts) | |
if (toastId) additionalOptions.toastId = toastId | |
// Makes sure that the previous toast is removed by using the id, if its still on the screen | |
ensurePreviousToastIsRemoved(toastId) | |
// Apply the type of toast and its props | |
switch (type) { | |
case 'success': | |
toastProps.success = true | |
className = 'toast-success' | |
break | |
case 'error': | |
toastProps.error = true | |
className = 'toast-error' | |
break | |
case 'info': | |
toastProps.info = true | |
className = 'toast-info' | |
break | |
case 'warn': | |
toastProps.warning = true | |
className - 'toast-warn' | |
break | |
case 'neutral': | |
toastProps.warning = true | |
className - 'toast-default' | |
break | |
default: | |
className = 'toast-default' | |
break | |
} | |
toastFn(<Toast {...toastProps}>{msg}</Toast>, { | |
className, | |
...getDefaultOptions(), | |
...opts, | |
...additionalOptions, | |
}) | |
} | |
} | |
return { | |
success: handleToast('success'), | |
error: handleToast('error'), | |
info: handleToast('info'), | |
warn: handleToast('warn'), | |
neutral: handleToast('neutral'), | |
} | |
})() | |
export const success = toaster.success | |
export const error = toaster.error | |
export const info = toaster.info | |
export const warn = toaster.warn | |
export const neutral = toaster.neutral |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment