diff --git a/.eslintrc.json b/.eslintrc.json index bffb357a..c8c1a9d0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,44 @@ { - "extends": "next/core-web-vitals" + "extends": [ + "next/core-web-vitals", + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended", + "plugin:jsx-a11y/recommended" + ], + "plugins": [ + "@typescript-eslint", + "react", + "jsx-a11y" + ], + "rules": { + "react/react-in-jsx-scope": "off", + "react/prop-types": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "@typescript-eslint/no-explicit-any": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }], + "prefer-const": "error", + "jsx-a11y/anchor-is-valid": ["error", { + "components": ["Link"], + "specialLink": ["hrefLeft", "hrefRight"], + "aspects": ["invalidHref", "preferButton"] + }], + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", + "jsx-a11y/label-has-associated-control": ["error", { + "labelComponents": [], + "labelAttributes": [], + "controlComponents": [], + "assert": "either", + "depth": 25 + }] + }, + "settings": { + "react": { + "version": "detect" + } + }, + "ignorePatterns": ["node_modules/", "build/", ".next/", "coverage/"] } diff --git a/package.json b/package.json index a32206e7..6406216e 100644 --- a/package.json +++ b/package.json @@ -53,11 +53,16 @@ "@types/node": "^22.10.1", "@types/react": "^18.3.13", "@types/react-dom": "^18.3.1", + "@typescript-eslint/eslint-plugin": "^8.17.0", + "@typescript-eslint/parser": "^8.17.0", "axe-core": "^4.10.2", "cypress": "13.16.1", "cypress-axe": "^1.5.0", "eslint": "9.16.0", "eslint-config-next": "15.0.4", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.1.0", "jest-environment-jsdom": "^29.7.0", "jsdom-testing-mocks": "^1.13.1", "postcss": "^8.4.49", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12a51813..8b5ffe84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -102,6 +102,12 @@ importers: '@types/react-dom': specifier: ^18.3.1 version: 18.3.1 + '@typescript-eslint/eslint-plugin': + specifier: ^8.17.0 + version: 8.17.0(@typescript-eslint/parser@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/parser': + specifier: ^8.17.0 + version: 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) axe-core: specifier: ^4.10.2 version: 4.10.2 @@ -117,6 +123,15 @@ importers: eslint-config-next: specifier: 15.0.4 version: 15.0.4(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + eslint-plugin-jsx-a11y: + specifier: ^6.10.2 + version: 6.10.2(eslint@9.16.0(jiti@1.21.6)) + eslint-plugin-react: + specifier: ^7.37.2 + version: 7.37.2(eslint@9.16.0(jiti@1.21.6)) + eslint-plugin-react-hooks: + specifier: ^5.1.0 + version: 5.1.0(eslint@9.16.0(jiti@1.21.6)) jest-environment-jsdom: specifier: ^29.7.0 version: 29.7.0 @@ -3564,8 +3579,8 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - eslint-plugin-react-hooks@5.0.0: - resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==} + eslint-plugin-react-hooks@5.1.0: + resolution: {integrity: sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 @@ -11149,7 +11164,7 @@ snapshots: eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint-import-resolver-typescript@3.6.3)(eslint@9.16.0(jiti@1.21.6)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.16.0(jiti@1.21.6)) eslint-plugin-react: 7.37.2(eslint@9.16.0(jiti@1.21.6)) - eslint-plugin-react-hooks: 5.0.0(eslint@9.16.0(jiti@1.21.6)) + eslint-plugin-react-hooks: 5.1.0(eslint@9.16.0(jiti@1.21.6)) optionalDependencies: typescript: 5.7.2 transitivePeerDependencies: @@ -11243,7 +11258,7 @@ snapshots: safe-regex-test: 1.0.3 string.prototype.includes: 2.0.1 - eslint-plugin-react-hooks@5.0.0(eslint@9.16.0(jiti@1.21.6)): + eslint-plugin-react-hooks@5.1.0(eslint@9.16.0(jiti@1.21.6)): dependencies: eslint: 9.16.0(jiti@1.21.6) diff --git a/src/components/ErrorBoundary/ErrorBoundary.tsx b/src/components/ErrorBoundary/ErrorBoundary.tsx index 2140dfb2..9c7a6ad3 100644 --- a/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -9,11 +9,6 @@ interface ErrorBoundaryProps { compact?: boolean; } -// Define the fallback component outside of ErrorBoundary -function ErrorFallback(props: FallbackProps) { - return ; -} - /** * ErrorBoundary component that catches JavaScript errors anywhere in the child component tree. * It logs the error and displays a fallback UI instead of the component tree that crashed. @@ -28,6 +23,10 @@ const ErrorBoundary: React.FC = ({ children, compact = false console.error("Uventet feil i Matrix:", error, info); }; + const ErrorFallback = (props: FallbackProps) => { + return ; + }; + return ( { emailjs.init(EMAIL_API_KEY); await emailjs.send(SERVICE_KEY, TEMPLATE_KEY, data); setServerResponse("Takk for din beskjed"); - } catch (error) { + } catch { setServerResponse("Feil under sending av skjema"); } }; diff --git a/src/components/Layout/MobileMenu.component.tsx b/src/components/Layout/MobileMenu.component.tsx index bc520ad2..282c7bc6 100644 --- a/src/components/Layout/MobileMenu.component.tsx +++ b/src/components/Layout/MobileMenu.component.tsx @@ -118,7 +118,7 @@ const MobileMenu = ({ links }: IMobileMenuProps) => { }} > {links.map( - ({ title, name, hash, href, externalLink }, index) => ( + ({ title, name, href, externalLink }, index) => ( | // Example async submit handlers const mockSubmit = async (data: FormData) => { await new Promise((resolve) => setTimeout(resolve, 1000)); + // eslint-disable-next-line no-console console.log("Form submitted:", data); }; diff --git a/src/stories/components/InputField.stories.tsx b/src/stories/components/InputField.stories.tsx index 918c93d5..bf2f7b9a 100644 --- a/src/stories/components/InputField.stories.tsx +++ b/src/stories/components/InputField.stories.tsx @@ -8,13 +8,22 @@ export default { component: InputField, } as Meta; +// Define a type for the form values +interface StoryFormValues extends FieldValues { + defaultInput?: string; + requiredInput?: string; + emailInput?: string; + textArea?: string; + errorInput?: string; +} + // Wrapper component to provide form context const InputFieldWrapper = (props: Omit, "register">) => { const { register } = useForm(); return ; }; -const Template: Story, "register">> = (args) => ( +const Template: Story, "register">> = (args) => ( ); diff --git a/src/stories/components/Matrix.stories.tsx b/src/stories/components/Matrix.stories.tsx index 2d35299e..625e3998 100644 --- a/src/stories/components/Matrix.stories.tsx +++ b/src/stories/components/Matrix.stories.tsx @@ -1,13 +1,22 @@ import React, { useEffect, useState } from "react"; import { Meta } from "@ladle/react"; +interface ReactMatrixAnimationProps { + tileSize?: number; + fadeFactor?: number; + backgroundColor?: string; + fontColor?: string; + glowColor?: string; + tileSet?: string[] | null; +} + export default { title: "Animations/Matrix", } as Meta; // Client-side only wrapper component -const ClientOnlyMatrix = (props: any) => { - const [Matrix, setMatrix] = useState(null); +const ClientOnlyMatrix = (props: ReactMatrixAnimationProps) => { + const [Matrix, setMatrix] = useState | null>(null); useEffect(() => { import("../../components/Animations/Matrix.component").then((mod) => { diff --git a/src/stories/components/MatrixCursor.stories.tsx b/src/stories/components/MatrixCursor.stories.tsx index 51151350..6bbc94c0 100644 --- a/src/stories/components/MatrixCursor.stories.tsx +++ b/src/stories/components/MatrixCursor.stories.tsx @@ -58,13 +58,9 @@ export const WithInteractiveElements = () => ( - e.preventDefault()} - > - Link Example - +

diff --git a/src/stories/components/Tabs.stories.tsx b/src/stories/components/Tabs.stories.tsx index 29f4c668..06ea76a6 100644 --- a/src/stories/components/Tabs.stories.tsx +++ b/src/stories/components/Tabs.stories.tsx @@ -33,29 +33,38 @@ const ListContent = () => (

); -const FormContent = () => ( -
-

Form Content

-
-
- - -
-
- - -
-
-
-); +const FormContent = () => { + const formId = React.useId(); + return ( +
+

Form Content

+
+
+ + +
+
+ + +
+
+
+ ); +}; export const VerticalTabs = Template.bind({}); VerticalTabs.args = {