diff --git a/package-lock.json b/package-lock.json
index 20c9a8b2b..8f001bf90 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,7 +18,8 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-feather": "2.0.10",
- "rimraf": "^5.0.1"
+ "rimraf": "^5.0.1",
+ "uuid": "^10.0.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -4832,9 +4833,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001523",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001523.tgz",
- "integrity": "sha512-I5q5cisATTPZ1mc588Z//pj/Ox80ERYDfR71YnvY7raS/NOk8xXlZcB0sF7JdqaV//kOaa6aus7lRfpdnt1eBA==",
+ "version": "1.0.30001667",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz",
+ "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==",
"funding": [
{
"type": "opencollective",
@@ -8726,6 +8727,18 @@
"node": ">= 4"
}
},
+ "node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/weak-lru-cache": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
@@ -12013,9 +12026,9 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"caniuse-lite": {
- "version": "1.0.30001523",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001523.tgz",
- "integrity": "sha512-I5q5cisATTPZ1mc588Z//pj/Ox80ERYDfR71YnvY7raS/NOk8xXlZcB0sF7JdqaV//kOaa6aus7lRfpdnt1eBA=="
+ "version": "1.0.30001667",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz",
+ "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw=="
},
"chalk": {
"version": "4.1.2",
@@ -14681,6 +14694,11 @@
"resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz",
"integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw=="
},
+ "uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="
+ },
"weak-lru-cache": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
diff --git a/package.json b/package.json
index 0f1de3265..6dba38a7c 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,8 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-feather": "2.0.10",
- "rimraf": "^5.0.1"
+ "rimraf": "^5.0.1",
+ "uuid": "^10.0.0"
},
"scripts": {
"predev": "rimraf .parcel-cache dist",
diff --git a/src/components/App/App.js b/src/components/App/App.js
index 785b62a67..c81678aea 100644
--- a/src/components/App/App.js
+++ b/src/components/App/App.js
@@ -2,11 +2,14 @@ import React from 'react';
import ToastPlayground from '../ToastPlayground';
import Footer from '../Footer';
+import ToastProvider from '../ToastProvider/ToastProvider';
function App() {
return (
<>
-
+
+
+
>
);
diff --git a/src/components/Toast/Toast.js b/src/components/Toast/Toast.js
index 96af6012c..f9b9308b4 100644
--- a/src/components/Toast/Toast.js
+++ b/src/components/Toast/Toast.js
@@ -10,6 +10,7 @@ import {
import VisuallyHidden from '../VisuallyHidden';
import styles from './Toast.module.css';
+import { ToastContext } from '../ToastProvider/ToastProvider.js';
const ICONS_BY_VARIANT = {
notice: Info,
@@ -18,18 +19,26 @@ const ICONS_BY_VARIANT = {
error: AlertOctagon,
};
-function Toast() {
+function Toast({ children, variant, id }) {
+ const { dismissToast } = React.useContext(ToastContext)
+ const className = `${styles.toast} ${styles[variant]}`
+ const Icon = ICONS_BY_VARIANT[variant]
+
return (
-
+
-
+
- 16 photos have been uploaded
+ {variant} -
+ {children}
-
);
diff --git a/src/components/ToastPlayground/ToastPlayground.js b/src/components/ToastPlayground/ToastPlayground.js
index e1c6c6eb2..b4c45bcef 100644
--- a/src/components/ToastPlayground/ToastPlayground.js
+++ b/src/components/ToastPlayground/ToastPlayground.js
@@ -1,20 +1,36 @@
import React from 'react';
-
import Button from '../Button';
-
import styles from './ToastPlayground.module.css';
+import ToastShelf from '../ToastShelf/ToastShelf'
+import { ToastContext } from '../ToastProvider/ToastProvider';
const VARIANT_OPTIONS = ['notice', 'warning', 'success', 'error'];
function ToastPlayground() {
+ const [message, setMessage] = React.useState("")
+ const [variant, setVariant] = React.useState(VARIANT_OPTIONS[0])
+ const { createToast } = React.useContext(ToastContext)
+
+ function handleSubmit(e) {
+ e.preventDefault();
+
+ if (message !== '' && variant !== '') {
+ createToast(message, variant)
+ }
+ setMessage('')
+ setVariant(VARIANT_OPTIONS[0])
+ }
+
return (
Toast Playground
-
-
@@ -52,10 +73,10 @@ function ToastPlayground() {
- Pop Toast!
+ Pop Toast!
-
+
);
}
diff --git a/src/components/ToastPlayground/ToastPlayground.module.css b/src/components/ToastPlayground/ToastPlayground.module.css
index 0b9d27683..7d650efea 100644
--- a/src/components/ToastPlayground/ToastPlayground.module.css
+++ b/src/components/ToastPlayground/ToastPlayground.module.css
@@ -106,3 +106,7 @@
width: 100%;
height: 4rem;
}
+
+.hidden {
+ display: none;
+}
\ No newline at end of file
diff --git a/src/components/ToastProvider/ToastProvider.js b/src/components/ToastProvider/ToastProvider.js
new file mode 100644
index 000000000..b5cec4eed
--- /dev/null
+++ b/src/components/ToastProvider/ToastProvider.js
@@ -0,0 +1,49 @@
+import React, { Children, useCallback } from 'react';
+import useEscapeKey from '../../hooks/useEscapeKey.js'
+export const ToastContext = React.createContext()
+
+function ToastProvider({ children }) {
+ const [toasts, setToasts] = React.useState([
+ {
+ id: crypto.randomUUID(),
+ message: 'Please enter the information',
+ variant: 'error'
+ },
+ {
+ id: crypto.randomUUID(),
+ message: 'Please enter the information',
+ variant: 'notice'
+ }
+ ])
+
+ const handleEscape = useCallback(() => {
+ setToasts([])
+ }, [])
+
+ useEscapeKey(handleEscape)
+
+ const createToast = (message, variant) => {
+ const nextToasts = [
+ ...toasts,
+ {
+ id: crypto.randomUUID(),
+ message,
+ variant,
+ }
+ ]
+ setToasts(nextToasts)
+ }
+
+ const dismissToast = (id) => {
+ const nextToasts = toasts.filter((toast) => { return toast.id !== id })
+ setToasts(nextToasts)
+ }
+
+ return (
+
+ {children}
+
+ );
+}
+
+export default ToastProvider;
diff --git a/src/components/ToastProvider/index.js b/src/components/ToastProvider/index.js
new file mode 100644
index 000000000..e6c22f60b
--- /dev/null
+++ b/src/components/ToastProvider/index.js
@@ -0,0 +1,2 @@
+export * from './ToastProvider';
+export { default } from './ToastProvider';
diff --git a/src/components/ToastShelf/ToastShelf.js b/src/components/ToastShelf/ToastShelf.js
index 1c5e8089d..adb094180 100644
--- a/src/components/ToastShelf/ToastShelf.js
+++ b/src/components/ToastShelf/ToastShelf.js
@@ -2,16 +2,24 @@ import React from 'react';
import Toast from '../Toast';
import styles from './ToastShelf.module.css';
+import { ToastContext } from '../ToastProvider/ToastProvider';
function ToastShelf() {
+ const { toasts } = React.useContext(ToastContext)
return (
-
- -
- Example notice toast
-
- -
- Example error toast
-
+
+ {
+ toasts.map(({ id, variant, message }) => (
+ -
+ {message}
+
+ ))
+ }
);
}
diff --git a/src/hooks/useEscapeKey.js b/src/hooks/useEscapeKey.js
new file mode 100644
index 000000000..1e027c975
--- /dev/null
+++ b/src/hooks/useEscapeKey.js
@@ -0,0 +1,17 @@
+import React from 'react';
+
+export default function useEscapeKey(callback) {
+ React.useEffect(() => {
+ function handleKeyDown(event) {
+ if (event.code === 'Escape') {
+ callback(event)
+ }
+ }
+
+ window.addEventListener('keydown', handleKeyDown)
+
+ return () => {
+ window.removeEventListener('keydown', handleKeyDown)
+ }
+ }, [callback])
+}
\ No newline at end of file
diff --git a/src/utils/useToggle.jsx b/src/utils/useToggle.jsx
new file mode 100644
index 000000000..44e697c30
--- /dev/null
+++ b/src/utils/useToggle.jsx
@@ -0,0 +1,17 @@
+import React from 'react'
+
+export default function useToggle(initialValue = false) {
+ if (typeof initialValue !== 'boolean') {
+ console.warn('Invalid type for useToggle');
+ }
+
+ const [value, setValue] = React.useState(
+ initialValue
+ );
+
+ const toggleValue = React.useCallback(() => {
+ setValue((currentValue) => !currentValue);
+ }, []);
+
+ return [value, toggleValue];
+ }
\ No newline at end of file