From 31ef4ceebf0a77df9c31224305f00c20efc1995e Mon Sep 17 00:00:00 2001 From: Borja Gonzalez Date: Wed, 7 Apr 2021 02:37:20 +0200 Subject: [PATCH] Eslint validation and jest installation --- .eslintrc | 1 + package.json | 2 +- src/components/Bars.jsx | 7 +++++++ src/components/Button.jsx | 14 ++++++++++++-- src/components/Calendar.jsx | 8 ++++++++ src/components/Footer.jsx | 12 +++++++++++- src/components/Header.jsx | 5 +++++ src/components/Loading.jsx | 4 ++++ src/components/Modal.jsx | 7 +++++++ src/components/Numbers.jsx | 7 +++++++ src/components/Result.jsx | 4 ++++ src/components/icons/ChartIcon.jsx | 9 ++++++++- src/components/icons/CloseIcon.jsx | 8 +++++++- src/components/icons/LeftIcon.jsx | 7 ++++++- src/components/icons/LogInIcon.jsx | 11 ++++++++++- src/components/icons/LogOutIcon.jsx | 9 ++++++++- src/components/icons/RestartIcon.jsx | 10 +++++++++- src/components/icons/RightIcon.jsx | 7 ++++++- src/components/icons/TickIcon.jsx | 7 ++++++- src/components/icons/UndoIcon.jsx | 9 ++++++++- src/components/icons/index.js | 2 +- src/constants/routes.js | 2 +- src/contexts/AttemptsContext.jsx | 27 ++++++++++++++++++--------- src/contexts/AuthContext.jsx | 7 ++++++- src/contexts/ModalContext.jsx | 9 +++++++-- src/hooks/useCalc.js | 2 +- src/hooks/useCalendar.js | 2 +- src/services/storage.js | 4 ++-- src/utils/firebase.js | 4 ++-- src/utils/randomNumbers.js | 6 +++++- vite.config.js | 14 +++++++------- 31 files changed, 186 insertions(+), 41 deletions(-) diff --git a/.eslintrc b/.eslintrc index 493f7ab..c693932 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,6 +4,7 @@ "es2021": true, "jest": true }, + "ignorePatterns": ["src/styles/**"], "extends": [ "plugin:react/recommended", "standard" diff --git a/package.json b/package.json index 47c98fd..9639e33 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "dev": "vite", "build": "vite build", "serve": "vite preview", - "test": "eslint src && jest -i" + "test": "eslint 'src/**' && jest -i" }, "dependencies": { "firebase": "8.3.2", diff --git a/src/components/Bars.jsx b/src/components/Bars.jsx index 75ce339..53df6f1 100644 --- a/src/components/Bars.jsx +++ b/src/components/Bars.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + import styles from '@styles/components/Bars.module.css' const Bars = ({ data, days }) => { @@ -28,4 +30,9 @@ const Bars = ({ data, days }) => { ) } +Bars.propTypes = { + data: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.number)).isRequired, + days: PropTypes.arrayOf(PropTypes.string).isRequired, +} + export default Bars diff --git a/src/components/Button.jsx b/src/components/Button.jsx index dc2979e..8cbeb9a 100644 --- a/src/components/Button.jsx +++ b/src/components/Button.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + import styles from '@styles/components/Button.module.css' const Button = ({ @@ -6,8 +8,7 @@ const Button = ({ thin = false, primary = false, secondary = false, - to = null, - onClick = () => {} + onClick = () => {}, }) => { const className = styles.button + (grow ? ` ${styles.grow}` : '') + @@ -18,4 +19,13 @@ const Button = ({ return } +Button.propTypes = { + children: PropTypes.node.isRequired, + grow: PropTypes.bool, + thin: PropTypes.bool, + primary: PropTypes.bool, + secondary: PropTypes.bool, + onClick: PropTypes.func, +} + export default Button diff --git a/src/components/Calendar.jsx b/src/components/Calendar.jsx index 9ad106b..f0bc3cf 100644 --- a/src/components/Calendar.jsx +++ b/src/components/Calendar.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + import styles from '@styles/components/Calendar.module.css' const weekDays = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'] @@ -23,4 +25,10 @@ const Stats = ({ data, days, firstDayOfWeek }) => ( ) +Stats.propTypes = { + data: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.number)).isRequired, + days: PropTypes.arrayOf(PropTypes.string).isRequired, + firstDayOfWeek: PropTypes.number.isRequired, +} + export default Stats diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 420df9f..88a3b65 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -1,4 +1,5 @@ import { useContext } from 'react' +import { PropTypes } from 'prop-types' import { DrawContext } from 'react-drawarea' import Button from '@components/Button' @@ -13,7 +14,7 @@ const Footer = ({ isSolved, handleRestart, handleSolve, - handleSuccess + handleSuccess, }) => { const { undo, reset } = useContext(DrawContext) @@ -57,4 +58,13 @@ const Footer = ({ ) } +Footer.propTypes = { + isCompleted: PropTypes.bool.isRequired, + isLoading: PropTypes.bool.isRequired, + isSolved: PropTypes.bool.isRequired, + handleRestart: PropTypes.func.isRequired, + handleSolve: PropTypes.func.isRequired, + handleSuccess: PropTypes.func.isRequired, +} + export default Footer diff --git a/src/components/Header.jsx b/src/components/Header.jsx index a4294e8..e85d97c 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,4 +1,5 @@ import { useLocation } from 'wouter' +import { PropTypes } from 'prop-types' import Button from '@components/Button' import { ChartIcon, LogInIcon, LogOutIcon } from '@components/icons' @@ -50,4 +51,8 @@ const Header = ({ isCompleted }) => { ) } +Header.propTypes = { + isCompleted: PropTypes.bool.isRequired, +} + export default Header diff --git a/src/components/Loading.jsx b/src/components/Loading.jsx index ff00162..a3a69f5 100644 --- a/src/components/Loading.jsx +++ b/src/components/Loading.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + import styles from '@styles/components/Loading.module.css' const Loading = ({ small = false }) => ( @@ -6,4 +8,6 @@ const Loading = ({ small = false }) => ( ) +Loading.propTypes = { small: PropTypes.bool } + export default Loading diff --git a/src/components/Modal.jsx b/src/components/Modal.jsx index e1bb049..28a2fc3 100644 --- a/src/components/Modal.jsx +++ b/src/components/Modal.jsx @@ -1,3 +1,4 @@ +import { PropTypes } from 'prop-types' import Button from '@components/Button' import { CloseIcon, TickIcon } from '@components/icons' @@ -14,4 +15,10 @@ const Modal = ({ message, onClose, onConfirm }) => ( ) +Modal.propTypes = { + message: PropTypes.string.isRequired, + onClose: PropTypes.func.isRequired, + onConfirm: PropTypes.func.isRequired, +} + export default Modal diff --git a/src/components/Numbers.jsx b/src/components/Numbers.jsx index 4b79bb5..1d47a87 100644 --- a/src/components/Numbers.jsx +++ b/src/components/Numbers.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + import styles from '@styles/components/Numbers.module.css' const Numbers = ({ numbers, isSolved }) => ( @@ -10,4 +12,9 @@ const Numbers = ({ numbers, isSolved }) => ( ) +Numbers.propTypes = { + numbers: PropTypes.arrayOf(PropTypes.number).isRequired, + isSolved: PropTypes.bool.isRequired, +} + export default Numbers diff --git a/src/components/Result.jsx b/src/components/Result.jsx index a478eac..15f6452 100644 --- a/src/components/Result.jsx +++ b/src/components/Result.jsx @@ -1,5 +1,9 @@ +import { PropTypes } from 'prop-types' + import styles from '@styles/components/Result.module.css' const Result = ({ value }) =>
{value}
+Result.propTypes = { value: PropTypes.number.isRequired } + export default Result diff --git a/src/components/icons/ChartIcon.jsx b/src/components/icons/ChartIcon.jsx index fd23649..e323e1b 100644 --- a/src/components/icons/ChartIcon.jsx +++ b/src/components/icons/ChartIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const ChartIcon = ({ color = '#ffffff' }) => ( ( > ) +ChartIcon.propTypes = { color: PropTypes.string } + export default ChartIcon diff --git a/src/components/icons/CloseIcon.jsx b/src/components/icons/CloseIcon.jsx index d2ffa11..9319df8 100644 --- a/src/components/icons/CloseIcon.jsx +++ b/src/components/icons/CloseIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const CloseIcon = ({ color = '#ffffff' }) => ( ( height={48} > ) +CloseIcon.propTypes = { color: PropTypes.string } + export default CloseIcon diff --git a/src/components/icons/LeftIcon.jsx b/src/components/icons/LeftIcon.jsx index 75095ea..5afc858 100644 --- a/src/components/icons/LeftIcon.jsx +++ b/src/components/icons/LeftIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const LeftIcon = ({ color = '#ffffff' }) => ( ( > ) +LeftIcon.propTypes = { color: PropTypes.string } + export default LeftIcon diff --git a/src/components/icons/LogInIcon.jsx b/src/components/icons/LogInIcon.jsx index 676ddde..557f06f 100644 --- a/src/components/icons/LogInIcon.jsx +++ b/src/components/icons/LogInIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const LogInIcon = ({ color = '#ffffff' }) => ( ( > ) +LogInIcon.propTypes = { color: PropTypes.string } + export default LogInIcon diff --git a/src/components/icons/LogOutIcon.jsx b/src/components/icons/LogOutIcon.jsx index 18e99ae..7acddfe 100644 --- a/src/components/icons/LogOutIcon.jsx +++ b/src/components/icons/LogOutIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const LogOutIcon = ({ color = '#ffffff' }) => ( ( > ) +LogOutIcon.propTypes = { color: PropTypes.string } + export default LogOutIcon diff --git a/src/components/icons/RestartIcon.jsx b/src/components/icons/RestartIcon.jsx index e02e311..fe21f38 100644 --- a/src/components/icons/RestartIcon.jsx +++ b/src/components/icons/RestartIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const RestartIcon = ({ color = '#ffffff' }) => ( ( height={48} > ) +RestartIcon.propTypes = { color: PropTypes.string } + export default RestartIcon diff --git a/src/components/icons/RightIcon.jsx b/src/components/icons/RightIcon.jsx index 71ae383..0fc64d2 100644 --- a/src/components/icons/RightIcon.jsx +++ b/src/components/icons/RightIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const RightIcon = ({ color = '#ffffff' }) => ( ( > ) +RightIcon.propTypes = { color: PropTypes.string } + export default RightIcon diff --git a/src/components/icons/TickIcon.jsx b/src/components/icons/TickIcon.jsx index 8dba080..ed4ac2d 100644 --- a/src/components/icons/TickIcon.jsx +++ b/src/components/icons/TickIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const TickIcon = ({ color = '#ffffff' }) => ( ( height={48} > ) +TickIcon.propTypes = { color: PropTypes.string } + export default TickIcon diff --git a/src/components/icons/UndoIcon.jsx b/src/components/icons/UndoIcon.jsx index 4181383..f05213f 100644 --- a/src/components/icons/UndoIcon.jsx +++ b/src/components/icons/UndoIcon.jsx @@ -1,3 +1,5 @@ +import { PropTypes } from 'prop-types' + const UndoIcon = ({ color = '#ffffff' }) => ( ( height={48} > ) +UndoIcon.propTypes = { color: PropTypes.string } + export default UndoIcon diff --git a/src/components/icons/index.js b/src/components/icons/index.js index 80e28b2..aeacca1 100644 --- a/src/components/icons/index.js +++ b/src/components/icons/index.js @@ -17,5 +17,5 @@ export { RestartIcon, RightIcon, TickIcon, - UndoIcon + UndoIcon, } diff --git a/src/constants/routes.js b/src/constants/routes.js index 4517dc3..6abba02 100644 --- a/src/constants/routes.js +++ b/src/constants/routes.js @@ -3,5 +3,5 @@ export const ROUTE_STATS = '/stats' export const ROUTES = { MAIN: ROUTE_MAIN, - STATS: ROUTE_STATS + STATS: ROUTE_STATS, } diff --git a/src/contexts/AttemptsContext.jsx b/src/contexts/AttemptsContext.jsx index 05585b7..fad1a3b 100644 --- a/src/contexts/AttemptsContext.jsx +++ b/src/contexts/AttemptsContext.jsx @@ -1,6 +1,8 @@ import { createContext, useCallback, useContext, useState } from 'react' +import { PropTypes } from 'prop-types' import { useAuth } from '@contexts/AuthContext' +import { useModal } from '@contexts/ModalContext' import { fetchAttempts, storeAttempt } from '@services/storage' const AttemptsContext = createContext(null) @@ -14,6 +16,7 @@ export const useAttempts = () => { export const AttemptsProvider = ({ children }) => { const { userId } = useAuth() + const { renderModal } = useModal() const [isStored, setIsStored] = useState(false) const [isStoring, setIsStoring] = useState(false) const [startTime, setStartTime] = useState(null) @@ -28,7 +31,7 @@ export const AttemptsProvider = ({ children }) => { setIsStored(false) }, []) - const store = useCallback((numbers, success) => { + const store = useCallback(async (numbers, success) => { if (!userId || isStoring) return const attempt = { @@ -36,16 +39,18 @@ export const AttemptsProvider = ({ children }) => { start: startTime, end: new Date(), numbers, - success - } - const callback = () => { - setIsStored(true) - setIsStoring(false) + success, } setIsStoring(true) - storeAttempt(attempt, callback) - }, [isStoring, startTime, userId]) + try { + await storeAttempt(attempt) + setIsStored(true) + } catch { + renderModal('Oops! Something went wrong. Please try again') + } + setIsStoring(false) + }, [isStoring, startTime, userId, renderModal]) return ( { isStoring, fetch, start, - store + store, }} > {children} ) } + +AttemptsProvider.propTypes = { + children: PropTypes.node.isRequired, +} diff --git a/src/contexts/AuthContext.jsx b/src/contexts/AuthContext.jsx index 82e3e0d..0ce919c 100644 --- a/src/contexts/AuthContext.jsx +++ b/src/contexts/AuthContext.jsx @@ -1,4 +1,5 @@ import { createContext, useContext, useEffect, useState } from 'react' +import { PropTypes } from 'prop-types' import { onAuthChange, signIn, signOut } from '@services/auth' @@ -33,10 +34,14 @@ export const AuthProvider = ({ children }) => { isLoading, userId, signIn, - signOut + signOut, }} > {children} ) } + +AuthProvider.propTypes = { + children: PropTypes.node.isRequired, +} diff --git a/src/contexts/ModalContext.jsx b/src/contexts/ModalContext.jsx index cf17ed9..54a5241 100644 --- a/src/contexts/ModalContext.jsx +++ b/src/contexts/ModalContext.jsx @@ -1,4 +1,5 @@ import { createContext, useContext, useState } from 'react' +import { PropTypes } from 'prop-types' import Modal from '@components/Modal' @@ -7,7 +8,7 @@ const ModalContext = createContext() const initialModalData = { visible: false, message: '', - onConfirm: () => {} + onConfirm: () => {}, } export const useModal = () => { @@ -31,7 +32,7 @@ export const ModalProvider = ({ children }) => { setModalData({ visible: true, message, - onConfirm + onConfirm, }) } @@ -43,3 +44,7 @@ export const ModalProvider = ({ children }) => { ) } + +ModalProvider.propTypes = { + children: PropTypes.node.isRequired, +} diff --git a/src/hooks/useCalc.js b/src/hooks/useCalc.js index 0a60ab9..e8c53ef 100644 --- a/src/hooks/useCalc.js +++ b/src/hooks/useCalc.js @@ -33,7 +33,7 @@ const useCalc = () => { result, complete, restart, - solve + solve, } } diff --git a/src/hooks/useCalendar.js b/src/hooks/useCalendar.js index 5d2637f..eef1a00 100644 --- a/src/hooks/useCalendar.js +++ b/src/hooks/useCalendar.js @@ -52,7 +52,7 @@ const useCalendar = () => { firstDayOfWeek, monthTitle, nextMonth, - prevMonth + prevMonth, } } diff --git a/src/services/storage.js b/src/services/storage.js index b3090ac..22fc226 100644 --- a/src/services/storage.js +++ b/src/services/storage.js @@ -37,5 +37,5 @@ export const fetchAttempts = async (uid, initDate, finishDate) => { return normalizeAttempts(snapshot) } -export const storeAttempt = (attempt, callback) => - attemptsRef.add(attempt).then(callback) +export const storeAttempt = (attempt) => + attemptsRef.add(attempt) diff --git a/src/utils/firebase.js b/src/utils/firebase.js index 6ee9833..f9ef297 100644 --- a/src/utils/firebase.js +++ b/src/utils/firebase.js @@ -8,7 +8,7 @@ firebase.initializeApp({ projectId: import.meta.env.VITE_FB_PROJECT_ID, storageBucket: import.meta.env.VITE_FB_STORAGE_BUCKET, messagingSenderId: import.meta.env.VITE_FB_SENDER_ID, - appId: import.meta.env.VITE_FB_APP_ID + appId: import.meta.env.VITE_FB_APP_ID, }) const auth = firebase.auth() @@ -17,5 +17,5 @@ const firestore = firebase.firestore() export { auth, firebase, - firestore + firestore, } diff --git a/src/utils/randomNumbers.js b/src/utils/randomNumbers.js index 43ac4a6..5e19627 100644 --- a/src/utils/randomNumbers.js +++ b/src/utils/randomNumbers.js @@ -6,7 +6,11 @@ const runOneOf = (callbacks) => { return callbacks[index]() } -export const getRandomNumbersFactory = (minQuantity, maxQuantity, bounds) => { +export const getRandomNumbersFactory = ( + minQuantity = 1, + maxQuantity = 1, + bounds = [[0, 1000]] +) => { const callbacks = bounds.map(([min, max]) => () => getRandomNumber(min, max)) return () => { diff --git a/vite.config.js b/vite.config.js index e503d9e..2bbeeb6 100644 --- a/vite.config.js +++ b/vite.config.js @@ -12,14 +12,14 @@ export default defineConfig({ const hash = Math.random().toString(36).slice(2, 8) return `${file}__${name}--${hash}` - } - } + }, + }, }, esbuild: { - jsxInject: "import React from 'react'" + jsxInject: "import * as React from 'react'", }, plugins: [ - reactRefresh() + reactRefresh(), ], resolve: { alias: { @@ -29,7 +29,7 @@ export default defineConfig({ '@hooks': path.resolve(__dirname, 'src/hooks'), '@services': path.resolve(__dirname, 'src/services'), '@styles': path.resolve(__dirname, 'src/styles'), - '@utils': path.resolve(__dirname, 'src/utils') - } - } + '@utils': path.resolve(__dirname, 'src/utils'), + }, + }, })