From cb3c84a1257ebc0ceb6df73a76329c78623f6543 Mon Sep 17 00:00:00 2001 From: Adam Alston Date: Sun, 1 Dec 2024 13:23:13 -0500 Subject: [PATCH] feat: add typescript-eslint (#85) --- .commitlintrc.cjs => .commitlintrc.mjs | 2 +- .eslintrc.cjs | 7 - .lintstagedrc.cjs => .lintstagedrc.mjs | 2 +- .prettierrc.cjs => .prettierrc.mjs | 2 +- .stylelintrc.cjs => .stylelintrc.mjs | 2 +- eslint.config.mjs | 20 ++ package-lock.json | 285 ++++++++++++++++++++++++- package.json | 4 +- src/App/App.tsx | 6 +- src/App/AppContext.tsx | 27 +-- src/Index.test.tsx | 35 ++- src/components/Toggle.tsx | 18 +- src/types/theme.interface.ts | 4 +- 13 files changed, 357 insertions(+), 57 deletions(-) rename .commitlintrc.cjs => .commitlintrc.mjs (95%) delete mode 100644 .eslintrc.cjs rename .lintstagedrc.cjs => .lintstagedrc.mjs (93%) rename .prettierrc.cjs => .prettierrc.mjs (77%) rename .stylelintrc.cjs => .stylelintrc.mjs (81%) create mode 100644 eslint.config.mjs diff --git a/.commitlintrc.cjs b/.commitlintrc.mjs similarity index 95% rename from .commitlintrc.cjs rename to .commitlintrc.mjs index 9fd30566..8c3bd6ce 100644 --- a/.commitlintrc.cjs +++ b/.commitlintrc.mjs @@ -1,4 +1,4 @@ -module.exports = { +export default { extends: ['@commitlint/config-conventional'], rules: { // Sets the maximum length for commit messages. It accounts for a 3-digit PR diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index c41786b7..00000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - extends: 'react-app', - rules: { - eqeqeq: 'error', - }, - ignorePatterns: ['!*.cjs'], -}; diff --git a/.lintstagedrc.cjs b/.lintstagedrc.mjs similarity index 93% rename from .lintstagedrc.cjs rename to .lintstagedrc.mjs index 441c2226..8fe090cc 100644 --- a/.lintstagedrc.cjs +++ b/.lintstagedrc.mjs @@ -1,4 +1,4 @@ -module.exports = { +export default { '*.{cjs,js,jsx,ts,tsx}': [ 'eslint --cache --fix --max-warnings 0', 'prettier --cache --write', diff --git a/.prettierrc.cjs b/.prettierrc.mjs similarity index 77% rename from .prettierrc.cjs rename to .prettierrc.mjs index 19c09f23..9e1fe047 100644 --- a/.prettierrc.cjs +++ b/.prettierrc.mjs @@ -1,4 +1,4 @@ /** @type {import("prettier").Config} */ -module.exports = { +export default { singleQuote: true, }; diff --git a/.stylelintrc.cjs b/.stylelintrc.mjs similarity index 81% rename from .stylelintrc.cjs rename to .stylelintrc.mjs index 6b1a95ce..1bfa40e3 100644 --- a/.stylelintrc.cjs +++ b/.stylelintrc.mjs @@ -1,3 +1,3 @@ -module.exports = { +export default { extends: ['stylelint-config-standard-scss', 'stylelint-config-prettier-scss'], }; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..247b5e5c --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,20 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + tseslint.configs.strict, + tseslint.configs.stylistic, + { ignores: ['build', 'deprecated'] }, + { + rules: { + eqeqeq: 'error', + '@typescript-eslint/no-empty-function': [ + 'error', + { allow: ['arrowFunctions'] }, + ], + }, + }, +); diff --git a/package-lock.json b/package-lock.json index 814f538b..b89d3cfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "devDependencies": { "@commitlint/cli": "^19.6.0", "@commitlint/config-conventional": "^19.6.0", + "@eslint/js": "^9.15.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", "@testing-library/user-event": "^14.5.2", @@ -30,7 +31,8 @@ "stylelint": "^16.11.0", "stylelint-config-prettier-scss": "^1.0.0", "stylelint-config-standard-scss": "^13.1.0", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "typescript-eslint": "^8.16.0" } }, "node_modules/@adobe/css-tools": { @@ -3063,12 +3065,12 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", + "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@humanwhocodes/config-array": { @@ -8473,6 +8475,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/eslint/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -18661,6 +18672,18 @@ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", "dev": true }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -18819,6 +18842,258 @@ "node": ">=4.2.0" } }, + "node_modules/typescript-eslint": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.16.0.tgz", + "integrity": "sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.16.0", + "@typescript-eslint/parser": "8.16.0", + "@typescript-eslint/utils": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz", + "integrity": "sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/type-utils": "8.16.0", + "@typescript-eslint/utils": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.16.0.tgz", + "integrity": "sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz", + "integrity": "sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz", + "integrity": "sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.16.0", + "@typescript-eslint/utils": "8.16.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.16.0.tgz", + "integrity": "sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz", + "integrity": "sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/visitor-keys": "8.16.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.16.0.tgz", + "integrity": "sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.16.0", + "@typescript-eslint/types": "8.16.0", + "@typescript-eslint/typescript-estree": "8.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz", + "integrity": "sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.16.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index 328357b5..23e9a031 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "devDependencies": { "@commitlint/cli": "^19.6.0", "@commitlint/config-conventional": "^19.6.0", + "@eslint/js": "^9.15.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", "@testing-library/user-event": "^14.5.2", @@ -37,7 +38,8 @@ "stylelint": "^16.11.0", "stylelint-config-prettier-scss": "^1.0.0", "stylelint-config-standard-scss": "^13.1.0", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "typescript-eslint": "^8.16.0" }, "jest": { "collectCoverageFrom": [ diff --git a/src/App/App.tsx b/src/App/App.tsx index f574f682..0d6c1143 100644 --- a/src/App/App.tsx +++ b/src/App/App.tsx @@ -6,8 +6,8 @@ import { Buttons, Content, Footer, Particles, Toggle } from 'components'; import { config } from './config'; export const App = () => { - const [isReady, setIsReady]: [boolean, Function] = useState(false); - const [isMobile, setIsMobile]: [boolean, Function] = useState(false); + const [isReady, setIsReady] = useState(false); + const [isMobile, setIsMobile] = useState(false); const init = () => { if ( @@ -21,7 +21,7 @@ export const App = () => { // before the state refactoring, 'theme' had a boolean-ish ('true', 'false') // value in localStorage, now 'theme' has a theme value ('dark', 'light'), // to prevent the site from breaking, older 'theme' entries should be updated - const localStorageTheme: string | null = localStorage.getItem('theme'); + const localStorageTheme = localStorage.getItem('theme'); if (localStorageTheme === 'true') { localStorage.setItem('theme', 'dark'); } else if (localStorageTheme === 'false') { diff --git a/src/App/AppContext.tsx b/src/App/AppContext.tsx index 1b7cd948..523986dc 100644 --- a/src/App/AppContext.tsx +++ b/src/App/AppContext.tsx @@ -13,21 +13,26 @@ interface AppContextInterface extends AppProviderInterface { setTheme: Dispatch; } -const initialState: AppContextInterface = { +const actions = { SET_THEME: 'SET_THEME' } as const; + +interface AppAction { + type: 'SET_THEME'; + value: string; +} + +type AppState = AppContextInterface; + +const initialState: AppState = { config: {} as Config, isMobile: false, theme: themes.dark, setTheme: () => {}, }; -const actions = { SET_THEME: 'SET_THEME' }; - -export const reducer = (state: any, action: any) => { +export const reducer = (state: AppState, action: AppAction): AppState => { switch (action.type) { case actions.SET_THEME: return { ...state, theme: themes[action.value] }; - default: - return state; } }; @@ -41,21 +46,19 @@ export const AppProvider = ({ initialState.config = config; initialState.isMobile = isMobile; - const supportedThemes: string[] = Object.keys(themes); - const localStorageTheme: string | null = localStorage.getItem('theme'); + const supportedThemes = Object.keys(themes); + const localStorageTheme = localStorage.getItem('theme'); if (localStorageTheme && supportedThemes.includes(localStorageTheme)) { initialState.theme = themes[localStorageTheme]; } const [state, dispatch] = useReducer(reducer, initialState); - const value = { + const value: AppContextInterface = { config: state.config, isMobile: state.isMobile, theme: state.theme, - setTheme: (value: string) => { - dispatch({ type: actions.SET_THEME, value }); - }, + setTheme: (value) => dispatch({ type: actions.SET_THEME, value }), }; return {children}; diff --git a/src/Index.test.tsx b/src/Index.test.tsx index 6f66bf4b..232be741 100644 --- a/src/Index.test.tsx +++ b/src/Index.test.tsx @@ -9,6 +9,25 @@ import { themes } from 'appearance'; configure({ testIdAttribute: 'data-v2' }); +const mockState = { + config: { + name: { display: 'Default Name' }, + title: { display: 'Default Title' }, + buttons: [ + { + name: 'Default Button', + display: 'Default Display', + ariaLabel: 'Default Aria Label', + icon: <>, + href: '#', + }, + ], + }, + isMobile: false, + theme: themes.dark, + setTheme: () => {}, +}; + describe('application tests', () => { beforeEach(async () => { await act(async () => render()); @@ -150,7 +169,7 @@ describe('app context tests', () => { await act(async () => render( } />, @@ -165,22 +184,16 @@ describe('app context tests', () => { }); describe('reducer tests', () => { - it('should return the initial state', () => { - const state = reducer(undefined, {}); - - expect(state).toEqual(undefined); - }); - it('should return the dark theme', () => { - const state = reducer(undefined, { type: 'SET_THEME', value: 'dark' }); + const state = reducer(mockState, { type: 'SET_THEME', value: 'dark' }); - expect(state).toEqual({ theme: themes.dark }); + expect(state).toEqual({ ...mockState, theme: themes.dark }); }); it('should return the light theme', () => { - const state = reducer(undefined, { type: 'SET_THEME', value: 'light' }); + const state = reducer(mockState, { type: 'SET_THEME', value: 'light' }); - expect(state).toEqual({ theme: themes.light }); + expect(state).toEqual({ ...mockState, theme: themes.light }); }); }); }); diff --git a/src/components/Toggle.tsx b/src/components/Toggle.tsx index 0e9dce96..e09ec26f 100644 --- a/src/components/Toggle.tsx +++ b/src/components/Toggle.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useContext } from 'react'; +import { useContext } from 'react'; import styled from 'styled-components'; import { AppContext } from 'App/AppContext'; @@ -61,19 +61,12 @@ const T = { export const Toggle = () => { const { theme, setTheme } = useContext(AppContext); - const isDark: boolean = theme.key === 'dark'; + const isDark = theme.key === 'dark'; const ariaLabel = `Currently in ${ isDark ? 'dark' : 'light' } mode, switch to ${!isDark ? 'dark' : 'light'} mode`; const toggleDescriptionId = 'toggle-description'; - const handleToggle = (checked: boolean) => { - const key: string = checked ? 'dark' : 'light'; - - localStorage.setItem('theme', key); - setTheme(key); - }; - return ( @@ -87,8 +80,11 @@ export const Toggle = () => { checked={isDark} aria-label={ariaLabel} aria-describedby={toggleDescriptionId} - onChange={({ target }: ChangeEvent) => { - handleToggle(target.checked); + onChange={({ target: { checked } }) => { + const key = checked ? 'dark' : 'light'; + + localStorage.setItem('theme', key); + setTheme(key); }} /> diff --git a/src/types/theme.interface.ts b/src/types/theme.interface.ts index 694a4d47..419db96c 100644 --- a/src/types/theme.interface.ts +++ b/src/types/theme.interface.ts @@ -7,6 +7,4 @@ export interface Theme { shadowColor: string; } -export interface Themes { - [key: string]: Theme; -} +export type Themes = Record;