From 1a3a683d6e3812fb921778e0ec327bf2f2587545 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Sun, 25 Oct 2020 23:43:15 -0300 Subject: [PATCH 1/4] Adding info for Login In --- components/auth/LoginView.tsx | 8 +++--- components/auth/SignUpView.tsx | 43 ++++++++++++++++++++----------- components/core/Layout/Layout.tsx | 36 +++++++++++++------------- components/icon/Info.tsx | 22 ++++++++++++++++ components/icon/index.ts | 1 + components/ui/Input/Input.tsx | 12 ++++++++- yarn.lock | 5 ++++ 7 files changed, 89 insertions(+), 38 deletions(-) create mode 100644 components/icon/Info.tsx diff --git a/components/auth/LoginView.tsx b/components/auth/LoginView.tsx index e597c9d26..6ef767ce1 100644 --- a/components/auth/LoginView.tsx +++ b/components/auth/LoginView.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useState } from 'react' +import { FC, useEffect, useState, useCallback } from 'react' import { Logo, Modal, Button, Input } from '@components/ui' import useLogin from '@lib/bigcommerce/use-login' import { useUI } from '@components/ui/context' @@ -39,7 +39,7 @@ const LoginView: FC = () => { } } - const handleValidation = () => { + const handleValidation = useCallback(() => { // Test for Alphanumeric password const validPassword = /^(?=.*[a-zA-Z])(?=.*[0-9])/.test(password) @@ -47,11 +47,11 @@ const LoginView: FC = () => { if (dirty) { setDisabled(!validate(email) || password.length < 7 || !validPassword) } - } + }, [email, password, dirty]) useEffect(() => { handleValidation() - }, [email, password, dirty]) + }, [handleValidation]) return (
diff --git a/components/auth/SignUpView.tsx b/components/auth/SignUpView.tsx index f46184bd0..a574cc963 100644 --- a/components/auth/SignUpView.tsx +++ b/components/auth/SignUpView.tsx @@ -1,8 +1,9 @@ -import { FC, useEffect, useState } from 'react' +import { FC, useEffect, useState, useCallback } from 'react' +import { validate } from 'email-validator' +import { Info } from '@components/icon' +import { useUI } from '@components/ui/context' import { Logo, Button, Input } from '@components/ui' import useSignup from '@lib/bigcommerce/use-signup' -import { useUI } from '@components/ui/context' -import { validate } from 'email-validator' interface Props {} @@ -43,7 +44,7 @@ const SignUpView: FC = () => { } } - const handleValidation = () => { + const handleValidation = useCallback(() => { // Test for Alphanumeric password const validPassword = /^(?=.*[a-zA-Z])(?=.*[0-9])/.test(password) @@ -51,18 +52,18 @@ const SignUpView: FC = () => { if (dirty) { setDisabled(!validate(email) || password.length < 7 || !validPassword) } - } + }, [email, password, dirty]) useEffect(() => { handleValidation() - }, [email, password, dirty]) + }, [handleValidation]) return (
-
+
{message && (
{message}
)} @@ -70,14 +71,26 @@ const SignUpView: FC = () => { - + + + Info: Password must be longer than 7 chars and + include numbers. + +
+ +
+ Do you have an account? {` `} diff --git a/components/core/Layout/Layout.tsx b/components/core/Layout/Layout.tsx index c96d434bd..a97cdb831 100644 --- a/components/core/Layout/Layout.tsx +++ b/components/core/Layout/Layout.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useState } from 'react' +import { FC, useCallback, useEffect, useState } from 'react' import cn from 'classnames' import { useRouter } from 'next/router' import type { Page } from '@lib/bigcommerce/api/operations/get-all-pages' @@ -10,7 +10,7 @@ import { LoginView, SignUpView } from '@components/auth' import { useUI } from '@components/ui/context' import { usePreventScroll } from '@react-aria/overlays' import s from './Layout.module.css' - +import debounce from 'lodash.debounce' interface Props { pageProps: { pages?: Page[] @@ -29,26 +29,26 @@ const Layout: FC = ({ children, pageProps }) => { const [hasScrolled, setHasScrolled] = useState(false) const { locale = 'en-US' } = useRouter() - // TODO: Update code, add throttle and more. - // TODO: Make sure to not do any unnecessary updates as it's doing right now - useEffect(() => { - const offset = 0 - function handleScroll() { - const { scrollTop } = document.documentElement - if (scrollTop > offset) setHasScrolled(true) - else setHasScrolled(false) - } - document.addEventListener('scroll', handleScroll) - - return () => { - document.removeEventListener('scroll', handleScroll) - } - }, []) - usePreventScroll({ isDisabled: !(displaySidebar || displayModal), }) + const handleScroll = useCallback(() => { + debounce(() => { + const offset = 0 + const { scrollTop } = document.documentElement + if (scrollTop > offset) setHasScrolled(true) + else setHasScrolled(false) + }, 1) + }, []) + + useEffect(() => { + document.addEventListener('scroll', handleScroll) + return () => { + document.removeEventListener('scroll', handleScroll) + } + }, [handleScroll]) + return (
diff --git a/components/icon/Info.tsx b/components/icon/Info.tsx new file mode 100644 index 000000000..705a6c6a6 --- /dev/null +++ b/components/icon/Info.tsx @@ -0,0 +1,22 @@ +const Info = ({ ...props }) => { + return ( + + + + + + ) +} + +export default Info diff --git a/components/icon/index.ts b/components/icon/index.ts index 0a8a191c3..1ce388914 100644 --- a/components/icon/index.ts +++ b/components/icon/index.ts @@ -11,3 +11,4 @@ export { default as Moon } from './Moon' export { default as Github } from './Github' export { default as DoubleChevron } from './DoubleChevron' export { default as RightArrow } from './RightArrow' +export { default as Info } from './Info' diff --git a/components/ui/Input/Input.tsx b/components/ui/Input/Input.tsx index 86ae34fc9..404729479 100644 --- a/components/ui/Input/Input.tsx +++ b/components/ui/Input/Input.tsx @@ -19,7 +19,17 @@ const Input: React.FC = (props) => { return null } - return + return ( + + ) } export default Input diff --git a/yarn.lock b/yarn.lock index fb90e1970..77e61f7c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5435,6 +5435,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + lodash.toarray@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" From a59d07291a27a0901c282d0dbb2accaeedc424c9 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 26 Oct 2020 00:12:21 -0300 Subject: [PATCH 2/4] Toast Component Wip --- components/core/Layout/Layout.tsx | 9 +++- components/ui/Toast/Toast.module.css | 9 ++++ components/ui/Toast/Toast.tsx | 73 ++++++++++++++++++++++++++++ components/ui/Toast/index.ts | 1 + components/ui/context.tsx | 42 ++++++++++++++++ components/ui/index.ts | 1 + 6 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 components/ui/Toast/Toast.module.css create mode 100644 components/ui/Toast/Toast.tsx create mode 100644 components/ui/Toast/index.ts diff --git a/components/core/Layout/Layout.tsx b/components/core/Layout/Layout.tsx index a97cdb831..0c65b7b29 100644 --- a/components/core/Layout/Layout.tsx +++ b/components/core/Layout/Layout.tsx @@ -4,7 +4,7 @@ import { useRouter } from 'next/router' import type { Page } from '@lib/bigcommerce/api/operations/get-all-pages' import { CommerceProvider } from '@lib/bigcommerce' import { CartSidebarView } from '@components/cart' -import { Container, Sidebar, Button, Modal } from '@components/ui' +import { Container, Sidebar, Button, Modal, Toast } from '@components/ui' import { Navbar, Featurebar, Footer } from '@components/core' import { LoginView, SignUpView } from '@components/auth' import { useUI } from '@components/ui/context' @@ -24,6 +24,9 @@ const Layout: FC = ({ children, pageProps }) => { closeSidebar, closeModal, modalView, + toastText, + closeToast, + displayToast, } = useUI() const [acceptedCookies, setAcceptedCookies] = useState(false) const [hasScrolled, setHasScrolled] = useState(false) @@ -67,6 +70,7 @@ const Layout: FC = ({ children, pageProps }) => { + {modalView === 'LOGIN_VIEW' && } {modalView === 'SIGNUP_VIEW' && } @@ -81,6 +85,9 @@ const Layout: FC = ({ children, pageProps }) => { } /> + {/* + {toastText} + */}
) diff --git a/components/ui/Toast/Toast.module.css b/components/ui/Toast/Toast.module.css new file mode 100644 index 000000000..41e77b622 --- /dev/null +++ b/components/ui/Toast/Toast.module.css @@ -0,0 +1,9 @@ +.root { +} + +.toast { + @apply absolute bg-primary text-primary flex items-center border border-accents-1 + rounded-md z-50 shadow-2xl top-0 right-0 p-6 my-6 mx-3; + width: 420px; + z-index: 20000; +} diff --git a/components/ui/Toast/Toast.tsx b/components/ui/Toast/Toast.tsx new file mode 100644 index 000000000..33baf3353 --- /dev/null +++ b/components/ui/Toast/Toast.tsx @@ -0,0 +1,73 @@ +import cn from 'classnames' +import { FC, useRef, useEffect, useCallback } from 'react' +import s from './Toast.module.css' +import { useDialog } from '@react-aria/dialog' +import { FocusScope } from '@react-aria/focus' +import { Transition } from '@headlessui/react' +import { useOverlay, useModal, OverlayContainer } from '@react-aria/overlays' + +interface Props { + className?: string + children?: any + open?: boolean + onClose: () => void +} + +const Toast: FC = ({ + className, + children, + open = false, + onClose, + ...props +}) => { + const rootClassName = cn(s.root, className) + let ref = useRef() as React.MutableRefObject + let { modalProps } = useModal() + let { dialogProps } = useDialog({}, ref) + let { overlayProps } = useOverlay( + { + isOpen: open, + isDismissable: true, + onClose: onClose, + ...props, + }, + ref + ) + + // useEffect(() => { + // setTimeout(() => { + // useCallback(onClose, []) + // }, 400) + // }) + + return ( + + + +
+ +
+ {children} +
+
+
+
+
+
+ ) +} + +export default Toast diff --git a/components/ui/Toast/index.ts b/components/ui/Toast/index.ts new file mode 100644 index 000000000..0e86a3392 --- /dev/null +++ b/components/ui/Toast/index.ts @@ -0,0 +1 @@ +export { default } from './Toast' diff --git a/components/ui/context.tsx b/components/ui/context.tsx index 44343fcb4..29f68041c 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -6,7 +6,9 @@ export interface State { displaySidebar: boolean displayDropdown: boolean displayModal: boolean + displayToast: boolean modalView: string + toastText: string } const initialState = { @@ -14,6 +16,8 @@ const initialState = { displayDropdown: false, displayModal: false, modalView: 'LOGIN_VIEW', + displayToast: false, + toastText: 'HOLAAAA', } type Action = @@ -23,6 +27,16 @@ type Action = | { type: 'CLOSE_SIDEBAR' } + | { + type: 'OPEN_TOAST' + } + | { + type: 'CLOSE_TOAST' + } + | { + type: 'SET_TOAST_TEXT' + text: ToastText + } | { type: 'OPEN_DROPDOWN' } @@ -45,6 +59,7 @@ type Action = } type MODAL_VIEWS = 'SIGNUP_VIEW' | 'LOGIN_VIEW' +type ToastText = string export const UIContext = React.createContext(initialState) @@ -88,12 +103,30 @@ function uiReducer(state: State, action: Action) { displayModal: false, } } + case 'OPEN_TOAST': { + return { + ...state, + displayToast: true, + } + } + case 'CLOSE_TOAST': { + return { + ...state, + displayToast: false, + } + } case 'SET_MODAL_VIEW': { return { ...state, modalView: action.view, } } + case 'SET_TOAST_TEXT': { + return { + ...state, + toastText: action.text, + } + } } } @@ -109,6 +142,9 @@ export const UIProvider: FC = (props) => { const openModal = () => dispatch({ type: 'OPEN_MODAL' }) const closeModal = () => dispatch({ type: 'CLOSE_MODAL' }) + const openToast = () => dispatch({ type: 'OPEN_TOAST' }) + const closeToast = () => dispatch({ type: 'CLOSE_TOAST' }) + const setModalView = (view: MODAL_VIEWS) => dispatch({ type: 'SET_MODAL_VIEW', view }) @@ -121,8 +157,14 @@ export const UIProvider: FC = (props) => { openModal, closeModal, setModalView, + openToast, + closeToast, } + setTimeout(() => { + openToast() + }, 200) + return } diff --git a/components/ui/index.ts b/components/ui/index.ts index 581c12d53..f1796b650 100644 --- a/components/ui/index.ts +++ b/components/ui/index.ts @@ -10,3 +10,4 @@ export { default as Skeleton } from './Skeleton' export { default as Modal } from './Modal' export { default as Text } from './Text' export { default as Input } from './Input' +export { default as Toast } from './Toast' From 4314c5270b8cc2bf936ae777c287878b8d693184 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 26 Oct 2020 00:12:51 -0300 Subject: [PATCH 3/4] Toast Component Wip --- components/ui/context.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ui/context.tsx b/components/ui/context.tsx index 29f68041c..8110bf1e0 100644 --- a/components/ui/context.tsx +++ b/components/ui/context.tsx @@ -17,7 +17,7 @@ const initialState = { displayModal: false, modalView: 'LOGIN_VIEW', displayToast: false, - toastText: 'HOLAAAA', + toastText: '', } type Action = From 35355e4b2b3bdbccc362bfa21ce5327e02208e5b Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Mon, 26 Oct 2020 00:16:51 -0300 Subject: [PATCH 4/4] WIP Forgot Password --- components/auth/ForgotPassword.tsx | 89 ++++++++++++++++++++++++++++++ pages/forgot-password.tsx | 33 ----------- 2 files changed, 89 insertions(+), 33 deletions(-) create mode 100644 components/auth/ForgotPassword.tsx delete mode 100644 pages/forgot-password.tsx diff --git a/components/auth/ForgotPassword.tsx b/components/auth/ForgotPassword.tsx new file mode 100644 index 000000000..c7d507701 --- /dev/null +++ b/components/auth/ForgotPassword.tsx @@ -0,0 +1,89 @@ +import { FC, useEffect, useState, useCallback } from 'react' +import { validate } from 'email-validator' +import { Info } from '@components/icon' +import { useUI } from '@components/ui/context' +import { Logo, Button, Input } from '@components/ui' +import useSignup from '@lib/bigcommerce/use-signup' + +interface Props {} + +const ForgotPassword: FC = () => { + // Form State + const [email, setEmail] = useState('') + const [loading, setLoading] = useState(false) + const [message, setMessage] = useState('') + const [dirty, setDirty] = useState(false) + const [disabled, setDisabled] = useState(false) + + const signup = useSignup() + const { setModalView, closeModal } = useUI() + + const handleSignup = async () => { + if (!dirty && !disabled) { + setDirty(true) + handleValidation() + } + + // try { + // setLoading(true) + // setMessage('') + // await signup({ + // email, + // }) + // setLoading(false) + // closeModal() + // } catch ({ errors }) { + // setMessage(errors[0].message) + // setLoading(false) + // } + } + + const handleValidation = useCallback(() => { + // Unable to send form unless fields are valid. + if (dirty) { + setDisabled(!validate(email)) + } + }, [email, dirty]) + + useEffect(() => { + handleValidation() + }, [handleValidation]) + + return ( +
+
+ +
+
+ {message && ( +
{message}
+ )} + + +
+ +
+ + + Do you have an account? + {` `} + setModalView('LOGIN_VIEW')} + > + Log In + + +
+
+ ) +} + +export default ForgotPassword diff --git a/pages/forgot-password.tsx b/pages/forgot-password.tsx deleted file mode 100644 index 5ff128cc6..000000000 --- a/pages/forgot-password.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { Layout } from '@components/core' -import { Logo, Modal, Button } from '@components/ui' -export default function ForgotPassword() { - return ( -
- {}}> -
-
- -
-
-
- -
- - - Don't have an account? - {` `} - - Sign Up - - -
-
-
-
- ) -} - -ForgotPassword.Layout = Layout