diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 07a7bac..87ff843 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,38 +1,38 @@ /** @type {import("eslint").Linter.Config} */ const config = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: true - }, - plugins: ['@typescript-eslint'], - extends: [ - 'plugin:@next/next/recommended', - 'plugin:@typescript-eslint/recommended-type-checked', - 'plugin:@typescript-eslint/stylistic-type-checked', - 'plugin:storybook/recommended' - ], - rules: { - // These opinionated rules are enabled in stylistic-type-checked above. - // Feel free to reconfigure them to your own preference. - '@typescript-eslint/array-type': 'off', - '@typescript-eslint/consistent-type-definitions': 'off', + parser: '@typescript-eslint/parser', + parserOptions: { + project: true + }, + plugins: ['@typescript-eslint'], + extends: [ + 'plugin:@next/next/recommended', + 'plugin:@typescript-eslint/recommended-type-checked', + 'plugin:@typescript-eslint/stylistic-type-checked', + 'plugin:storybook/recommended' + ], + rules: { + // These opinionated rules are enabled in stylistic-type-checked above. + // Feel free to reconfigure them to your own preference. + '@typescript-eslint/array-type': 'off', + '@typescript-eslint/consistent-type-definitions': 'off', - '@typescript-eslint/consistent-type-imports': [ - 'warn', - { - prefer: 'type-imports', - fixStyle: 'inline-type-imports' - } - ], - '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], - '@typescript-eslint/require-await': 'off', - '@typescript-eslint/no-misused-promises': [ - 'error', - { - checksVoidReturn: { attributes: false } - } - ] - } + '@typescript-eslint/consistent-type-imports': [ + 'warn', + { + prefer: 'type-imports', + fixStyle: 'inline-type-imports' + } + ], + '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/no-misused-promises': [ + 'error', + { + checksVoidReturn: { attributes: false } + } + ] + } }; module.exports = config; diff --git a/.storybook/constants.ts b/.storybook/constants.ts index 884977f..f86c510 100644 --- a/.storybook/constants.ts +++ b/.storybook/constants.ts @@ -4,39 +4,39 @@ * @see https://tailwindcss.com/docs/responsive-design */ export const TAILWIND_VIEWPORTS = { - sm: { - name: 'Small (sm)', - styles: { - width: '640px', - height: '100%' - } - }, - md: { - name: 'Medium (md)', - styles: { - width: '768px', - height: '100%' - } - }, - lg: { - name: 'Large (lg)', - styles: { - width: '1024px', - height: '100%' - } - }, - xl: { - name: 'Extra Large (xl)', - styles: { - width: '1280px', - height: '100%' - } - }, - '2xl': { - name: '2X Large (2xl)', - styles: { - width: '1536px', - height: '100%' - } - } + sm: { + name: 'Small (sm)', + styles: { + width: '640px', + height: '100%' + } + }, + md: { + name: 'Medium (md)', + styles: { + width: '768px', + height: '100%' + } + }, + lg: { + name: 'Large (lg)', + styles: { + width: '1024px', + height: '100%' + } + }, + xl: { + name: 'Extra Large (xl)', + styles: { + width: '1280px', + height: '100%' + } + }, + '2xl': { + name: '2X Large (2xl)', + styles: { + width: '1536px', + height: '100%' + } + } } as const; diff --git a/.storybook/main.ts b/.storybook/main.ts index 1bd34e0..11451d8 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,23 +1,23 @@ import type { StorybookConfig } from '@storybook/nextjs'; const config: StorybookConfig = { - stories: ['../src/components/**/*.stories.@(ts|tsx)'], - addons: [ - '@storybook/addon-onboarding', - '@storybook/addon-links', - '@storybook/addon-essentials', - '@chromatic-com/storybook', - '@storybook/addon-interactions', - 'storybook-addon-pseudo-states' - ], - framework: { - name: '@storybook/nextjs', - options: {} - }, - docs: { - autodocs: 'tag', - defaultName: 'Documentation' - }, - staticDirs: ['../public'] + stories: ['../src/components/**/*.stories.@(ts|tsx)'], + addons: [ + '@storybook/addon-onboarding', + '@storybook/addon-links', + '@storybook/addon-essentials', + '@chromatic-com/storybook', + '@storybook/addon-interactions', + 'storybook-addon-pseudo-states' + ], + framework: { + name: '@storybook/nextjs', + options: {} + }, + docs: { + autodocs: 'tag', + defaultName: 'Documentation' + }, + staticDirs: ['../public'] }; export default config; diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index 4df2249..836654d 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -4,9 +4,9 @@ diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 346f977..4c6a64a 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -4,20 +4,20 @@ import '~/styles/tailwind.css'; import { TAILWIND_VIEWPORTS } from './constants'; const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i - } - }, - docs: { - toc: true - }, - viewport: { - viewports: TAILWIND_VIEWPORTS - } - } + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i + } + }, + docs: { + toc: true + }, + viewport: { + viewports: TAILWIND_VIEWPORTS + } + } }; export default preview; diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b14aae1..f6ec3c4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,9 @@ { - "recommendations": [ - "ms-playwright.playwright", - "bradlc.vscode-tailwindcss", - "yoavbls.pretty-ts-errors", - "esbenp.prettier-vscode", - "dbaeumer.vscode-eslint" - ] + "recommendations": [ + "ms-playwright.playwright", + "bradlc.vscode-tailwindcss", + "yoavbls.pretty-ts-errors", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 7361d0d..9ae7548 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,4 +2,4 @@ "editor.detectIndentation": false, "editor.insertSpaces": true, "editor.tabSize": 4 -} \ No newline at end of file +} diff --git a/commitlint.config.js b/commitlint.config.js index 852d001..465b85a 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,7 +1,7 @@ /** @type {import('@commitlint/types').UserConfig} */ const config = { - // @commitlint/config-conventional enforces Conventional Commits (https://www.conventionalcommits.org/en/v1.0.0/). - extends: ['@commitlint/config-conventional'] + // @commitlint/config-conventional enforces Conventional Commits (https://www.conventionalcommits.org/en/v1.0.0/). + extends: ['@commitlint/config-conventional'] }; export default config; diff --git a/jest.config.mjs b/jest.config.mjs index fc30168..bc7c4f2 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -6,192 +6,192 @@ import nextJest from 'next/jest.js'; const createJestConfig = nextJest({ - // Provide the path to your Next.js app to load next.config.js and .env files in your test environment - dir: './' + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './' }); /** @type {import('jest').Config} */ const config = { - // All imported modules in your tests should be mocked automatically - // automock: false, + // All imported modules in your tests should be mocked automatically + // automock: false, - // Stop running tests after `n` failures - // bail: 0, + // Stop running tests after `n` failures + // bail: 0, - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/lr/7407yp7162j102fjlp8xj9940000gn/T/jest_dx", + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/private/var/folders/lr/7407yp7162j102fjlp8xj9940000gn/T/jest_dx", - // Automatically clear mock calls, instances, contexts and results before every test - // clearMocks: true, + // Automatically clear mock calls, instances, contexts and results before every test + // clearMocks: true, - // Indicates whether the coverage information should be collected while executing the test - // collectCoverage: true, + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: true, - // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: undefined, + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, - // The directory where Jest should output its coverage files - // coverageDirectory: 'coverage', + // The directory where Jest should output its coverage files + // coverageDirectory: 'coverage', - // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], - // Indicates which provider should be used to instrument code for coverage - // coverageProvider: 'v8', + // Indicates which provider should be used to instrument code for coverage + // coverageProvider: 'v8', - // A list of reporter names that Jest uses when writing coverage reports - coverageReporters: ['json', 'json-summary'], + // A list of reporter names that Jest uses when writing coverage reports + coverageReporters: ['json', 'json-summary'], - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: undefined, + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, - // A path to a custom dependency extractor - // dependencyExtractor: undefined, + // A path to a custom dependency extractor + // dependencyExtractor: undefined, - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, - // The default configuration for fake timers - // fakeTimers: { - // "enableGlobally": false - // }, + // The default configuration for fake timers + // fakeTimers: { + // "enableGlobally": false + // }, - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: undefined, + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: undefined, + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, - // A set of global variables that need to be available in all test environments - // globals: {}, + // A set of global variables that need to be available in all test environments + // globals: {}, - // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. - // maxWorkers: "50%", + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", - // An array of directory names to be searched recursively up from the requiring module's location - moduleDirectories: ['node_modules', '/'], + // An array of directory names to be searched recursively up from the requiring module's location + moduleDirectories: ['node_modules', '/'], - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "mjs", - // "cjs", - // "jsx", - // "ts", - // "tsx", - // "json", - // "node" - // ], + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "mjs", + // "cjs", + // "jsx", + // "ts", + // "tsx", + // "json", + // "node" + // ], - // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module - moduleNameMapper: { - '^~/(.*)$': '/$1' - }, + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + moduleNameMapper: { + '^~/(.*)$': '/$1' + }, - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], - // Activates notifications for test results - // notify: false, + // Activates notifications for test results + // notify: false, - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", - // A preset that is used as a base for Jest's configuration - // preset: undefined, + // A preset that is used as a base for Jest's configuration + // preset: undefined, - // Run tests from one or more projects - // projects: undefined, + // Run tests from one or more projects + // projects: undefined, - // Use this configuration option to add custom reporters to Jest - reporters: ['default', 'jest-junit'], + // Use this configuration option to add custom reporters to Jest + reporters: ['default', 'jest-junit'], - // Automatically reset mock state before every test - // resetMocks: false, + // Automatically reset mock state before every test + // resetMocks: false, - // Reset the module registry before running each individual test - // resetModules: false, + // Reset the module registry before running each individual test + // resetModules: false, - // A path to a custom resolver - // resolver: undefined, + // A path to a custom resolver + // resolver: undefined, - // Automatically restore mock state and implementation before every test - // restoreMocks: false, + // Automatically restore mock state and implementation before every test + // restoreMocks: false, - // The root directory that Jest should scan for tests and modules within - // rootDir: undefined, + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, - // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: [], + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], - // The number of seconds after which a test is considered as slow and reported as such in the results. - // slowTestThreshold: 5, + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], - // The test environment that will be used for testing - // testEnvironment: "jest-environment-node", + // The test environment that will be used for testing + // testEnvironment: "jest-environment-node", - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, - // Adds a location field to test results - // testLocationInResults: false, + // Adds a location field to test results + // testLocationInResults: false, - // The glob patterns Jest uses to detect test files - testMatch: ['**/__tests__/*.test.mjs'] + // The glob patterns Jest uses to detect test files + testMatch: ['**/__tests__/*.test.mjs'] - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], - // This option allows the use of a custom results processor - // testResultsProcessor: undefined, + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, - // This option allows use of a custom test runner - // testRunner: "jest-circus/runner", + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", - // A map from regular expressions to paths to transformers - // transform: undefined, + // A map from regular expressions to paths to transformers + // transform: undefined, - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/", - // "\\.pnp\\.[^\\/]+$" - // ], + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, - // Indicates whether each individual test should be reported during the run - // verbose: undefined, + // Indicates whether each individual test should be reported during the run + // verbose: undefined, - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], - // Whether to use watchman for file crawling - // watchman: true, + // Whether to use watchman for file crawling + // watchman: true, }; export default createJestConfig(config); diff --git a/lint-staged.config.js b/lint-staged.config.js index c451d4c..46b71bf 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -2,24 +2,24 @@ import path from 'path'; /** @type {import("lint-staged").ConfigFn} */ function lint(filenames) { - // If there are more than 10 staged files, run the linter over the whole project - if (filenames.length > 10) return 'next lint --fix'; + // If there are more than 10 staged files, run the linter over the whole project + if (filenames.length > 10) return 'next lint --fix'; - // Otherwise, run the linter over each file individually - return `next lint --fix --file ${filenames.map((f) => path.relative(process.cwd(), f)).join(' --file ')}`; + // Otherwise, run the linter over each file individually + return `next lint --fix --file ${filenames.map((f) => path.relative(process.cwd(), f)).join(' --file ')}`; } /** @type {import("lint-staged").ConfigFn} */ function prettier(filenames) { - // If there are more than 10 staged files, run prettier over the whole project - if (filenames.length > 10) return 'prettier --write .'; + // If there are more than 10 staged files, run prettier over the whole project + if (filenames.length > 10) return 'prettier --write .'; - // Otherwise, run prettier over each file individually - return `prettier --write ${filenames.map((f) => path.relative(process.cwd(), f)).join(' ')}`; + // Otherwise, run prettier over each file individually + return `prettier --write ${filenames.map((f) => path.relative(process.cwd(), f)).join(' ')}`; } const config = { - '*.{ts,tsx,cjs,js,mjs}': [lint, prettier] + '*.{ts,tsx,cjs,js,mjs}': [lint, prettier] }; export default config; diff --git a/next.config.mjs b/next.config.mjs index ae9743e..bee3233 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -6,11 +6,11 @@ await import('./src/env.js'); /** @type {import('next').NextConfig} */ const config = { - logging: { - fetches: { - fullUrl: true - } - } + logging: { + fetches: { + fullUrl: true + } + } }; export default config; diff --git a/package.json b/package.json index 3c728be..368159b 100644 --- a/package.json +++ b/package.json @@ -1,91 +1,91 @@ { - "name": "nextjs-starter", - "version": "0.0.0", - "private": true, - "type": "module", - "scripts": { - "build": "next build", - "dev": "next dev --turbo", - "format": "prettier --write .", - "jest:test": "SKIP_ENV_VALIDATION=1 jest", - "jest:watch": "jest --watch", - "lint": "next lint", - "playwright:test": "playwright test", - "playwright:test:codegen": "playwright codegen", - "playwright:test:debug": "playwright test --debug", - "playwright:test:interactive": "playwright test --ui", - "prepare": "husky", - "start": "next start", - "storybook": "storybook dev -p 6006", - "storybook:build": "SKIP_ENV_VALIDATION=1 storybook build", - "storybook:test": "test-storybook --browsers chromium firefox webkit", - "plop": "plop" - }, - "jest-junit": { - "ancestorSeparator": " › ", - "classNameTemplate": "{classname}", - "outputDirectory": "reports", - "outputName": "jest-junit.xml", - "suiteNameTemplate": "{filepath}", - "titleTemplate": "{title}", - "uniqueOutputName": "false" - }, - "dependencies": { - "@t3-oss/env-nextjs": "~0.9", - "class-variance-authority": "~0.7", - "clsx": "~2.1", - "next": "~14.2", - "react": "~18.2", - "react-dom": "~18.2", - "tailwind-merge": "~2.2", - "zod": "~3.22" - }, - "devDependencies": { - "@chromatic-com/storybook": "~1.3", - "@commitlint/cli": "~19.2", - "@commitlint/config-conventional": "~19.1", - "@commitlint/types": "~19.0", - "@playwright/test": "~1.43", - "@storybook/addon-essentials": "~8.0", - "@storybook/addon-interactions": "~8.0", - "@storybook/addon-links": "~8.0", - "@storybook/addon-onboarding": "~8.0", - "@storybook/blocks": "~8.0", - "@storybook/nextjs": "~8.0", - "@storybook/react": "~8.0", - "@storybook/test": "~8.0", - "@storybook/test-runner": "~0.17", - "@types/eslint": "~8.56", - "@types/jest": "~29.5", - "@types/lint-staged": "~13.3", - "@types/node": "~20.12", - "@types/react": "~18.2", - "@types/react-dom": "~18.2", - "@typescript-eslint/eslint-plugin": "~7.6", - "@typescript-eslint/parser": "~7.6", - "autoprefixer": "~10.4", - "eslint": "~8.57", - "eslint-config-next": "~14.2", - "eslint-plugin-storybook": "~0.8", - "husky": "~9.0", - "jest": "~29.7", - "jest-junit": "^16.0.0", - "lint-staged": "~15.2", - "plop": "^4.0.1", - "postcss": "~8.4", - "prettier": "~3.2", - "prettier-plugin-organize-imports": "~3.2", - "prettier-plugin-tailwindcss": "~0.5", - "storybook": "~8.0", - "storybook-addon-pseudo-states": "~3.0", - "tailwindcss": "~3.4", - "typescript": "~5.4" - }, - "engines": { - "bun": "Please use pnpm", - "node": "20.*.*", - "npm": "Please use pnpm", - "pnpm": "9.*.*", - "yarn": "Please use pnpm" - } + "name": "nextjs-starter", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "build": "next build", + "dev": "next dev --turbo", + "format": "prettier --write .", + "jest:test": "SKIP_ENV_VALIDATION=1 jest", + "jest:watch": "jest --watch", + "lint": "next lint", + "playwright:test": "playwright test", + "playwright:test:codegen": "playwright codegen", + "playwright:test:debug": "playwright test --debug", + "playwright:test:interactive": "playwright test --ui", + "prepare": "husky", + "start": "next start", + "storybook": "storybook dev -p 6006", + "storybook:build": "SKIP_ENV_VALIDATION=1 storybook build", + "storybook:test": "test-storybook --browsers chromium firefox webkit", + "plop": "plop" + }, + "jest-junit": { + "ancestorSeparator": " › ", + "classNameTemplate": "{classname}", + "outputDirectory": "reports", + "outputName": "jest-junit.xml", + "suiteNameTemplate": "{filepath}", + "titleTemplate": "{title}", + "uniqueOutputName": "false" + }, + "dependencies": { + "@t3-oss/env-nextjs": "~0.9", + "class-variance-authority": "~0.7", + "clsx": "~2.1", + "next": "~14.2", + "react": "~18.2", + "react-dom": "~18.2", + "tailwind-merge": "~2.2", + "zod": "~3.22" + }, + "devDependencies": { + "@chromatic-com/storybook": "~1.3", + "@commitlint/cli": "~19.2", + "@commitlint/config-conventional": "~19.1", + "@commitlint/types": "~19.0", + "@playwright/test": "~1.43", + "@storybook/addon-essentials": "~8.0", + "@storybook/addon-interactions": "~8.0", + "@storybook/addon-links": "~8.0", + "@storybook/addon-onboarding": "~8.0", + "@storybook/blocks": "~8.0", + "@storybook/nextjs": "~8.0", + "@storybook/react": "~8.0", + "@storybook/test": "~8.0", + "@storybook/test-runner": "~0.17", + "@types/eslint": "~8.56", + "@types/jest": "~29.5", + "@types/lint-staged": "~13.3", + "@types/node": "~20.12", + "@types/react": "~18.2", + "@types/react-dom": "~18.2", + "@typescript-eslint/eslint-plugin": "~7.6", + "@typescript-eslint/parser": "~7.6", + "autoprefixer": "~10.4", + "eslint": "~8.57", + "eslint-config-next": "~14.2", + "eslint-plugin-storybook": "~0.8", + "husky": "~9.0", + "jest": "~29.7", + "jest-junit": "^16.0.0", + "lint-staged": "~15.2", + "plop": "^4.0.1", + "postcss": "~8.4", + "prettier": "~3.2", + "prettier-plugin-organize-imports": "~3.2", + "prettier-plugin-tailwindcss": "~0.5", + "storybook": "~8.0", + "storybook-addon-pseudo-states": "~3.0", + "tailwindcss": "~3.4", + "typescript": "~5.4" + }, + "engines": { + "bun": "Please use pnpm", + "node": "20.*.*", + "npm": "Please use pnpm", + "pnpm": "9.*.*", + "yarn": "Please use pnpm" + } } diff --git a/playwright.config.ts b/playwright.config.ts index 96ee812..e69ac2c 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -10,68 +10,68 @@ import { defineConfig, devices } from '@playwright/test'; * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ - testMatch: '**/*.test.ts', - /* Run tests in files in parallel */ - fullyParallel: true, - /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, - /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, - /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', - /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ - use: { - /* Base URL to use in actions like `await page.goto('/')`. */ - // baseURL: 'http://127.0.0.1:3000', + testMatch: '**/*.test.ts', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry' - }, + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry' + }, - /* Configure projects for major browsers */ - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] } - }, + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + }, - { - name: 'firefox', - use: { ...devices['Desktop Firefox'] } - }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] } + }, - { - name: 'webkit', - use: { ...devices['Desktop Safari'] } - } + { + name: 'webkit', + use: { ...devices['Desktop Safari'] } + } - /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { ...devices['Pixel 5'] }, - // }, - // { - // name: 'Mobile Safari', - // use: { ...devices['iPhone 12'] }, - // }, + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, - /* Test against branded browsers. */ - // { - // name: 'Microsoft Edge', - // use: { ...devices['Desktop Edge'], channel: 'msedge' }, - // }, - // { - // name: 'Google Chrome', - // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, - // }, - ] + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ] - /* Run your local dev server before starting the tests */ - // webServer: { - // command: 'npm run start', - // url: 'http://127.0.0.1:3000', - // reuseExistingServer: !process.env.CI, - // }, + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, }); diff --git a/plopfile.mjs b/plopfile.mjs index 9eb1fb4..73ea4d0 100644 --- a/plopfile.mjs +++ b/plopfile.mjs @@ -2,58 +2,58 @@ * @param {import('plop').NodePlopAPI} plop */ export default function (plop) { - plop.setGenerator('component', { - description: 'Create a new component according to the Atomic Design methodology', - prompts: [ - { - type: 'input', - name: 'name', - message: 'What is your component name?' - }, - { - type: 'list', - name: 'type', - message: 'What is your component type? ', - choices: ['atoms', 'molecules', 'organisms'] - }, - { - type: 'input', - name: 'subFolder', - message: (answers) => { - switch (answers.type) { - case 'atoms': - return 'Name your subfolder (e.g. interactive, typography, etc.)'; - case 'molecules': - return 'Name your subfolder (e.g. forms, cards, etc.)'; - case 'organisms': - return 'Name your subfolder (e.g. headers, footers, sections etc.)'; - default: - return 'Name your subfolder'; - } - } - }, - { - type: 'confirm', - name: 'confirmation', - message: (answers) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call - const pascalCaseName = answers.name - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - .replace(/(^\w|-\w|\s\w|_\w)/g, (/** @type {string} */ s) => s.toUpperCase()) - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - .replace(/[-_\s]+/g, ''); + plop.setGenerator('component', { + description: 'Create a new component according to the Atomic Design methodology', + prompts: [ + { + type: 'input', + name: 'name', + message: 'What is your component name?' + }, + { + type: 'list', + name: 'type', + message: 'What is your component type? ', + choices: ['atoms', 'molecules', 'organisms'] + }, + { + type: 'input', + name: 'subFolder', + message: (answers) => { + switch (answers.type) { + case 'atoms': + return 'Name your subfolder (e.g. interactive, typography, etc.)'; + case 'molecules': + return 'Name your subfolder (e.g. forms, cards, etc.)'; + case 'organisms': + return 'Name your subfolder (e.g. headers, footers, sections etc.)'; + default: + return 'Name your subfolder'; + } + } + }, + { + type: 'confirm', + name: 'confirmation', + message: (answers) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call + const pascalCaseName = answers.name + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .replace(/(^\w|-\w|\s\w|_\w)/g, (/** @type {string} */ s) => s.toUpperCase()) + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .replace(/[-_\s]+/g, ''); - return `Are you sure you want to create '~/components/${answers.type}/${answers.subFolder}/${pascalCaseName}'?`; - } - } - ], - actions: [ - { - type: 'addMany', - destination: 'src/components/{{type}}/{{subFolder}}/{{pascalCase name}}', - base: '.plop-templates/component', - templateFiles: '.plop-templates/component/*.hbs' - } - ] - }); + return `Are you sure you want to create '~/components/${answers.type}/${answers.subFolder}/${pascalCaseName}'?`; + } + } + ], + actions: [ + { + type: 'addMany', + destination: 'src/components/{{type}}/{{subFolder}}/{{pascalCase name}}', + base: '.plop-templates/component', + templateFiles: '.plop-templates/component/*.hbs' + } + ] + }); } diff --git a/postcss.config.cjs b/postcss.config.cjs index 056c26b..7d86165 100644 --- a/postcss.config.cjs +++ b/postcss.config.cjs @@ -1,8 +1,8 @@ const config = { - plugins: { - tailwindcss: {}, - autoprefixer: {} - } + plugins: { + tailwindcss: {}, + autoprefixer: {} + } }; module.exports = config; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 1923057..27351ca 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -9,24 +9,24 @@ import '~/styles/tailwind.css'; const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app' + title: 'Create Next App', + description: 'Generated by create next app' }; export default function RootLayout({ - children + children }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - return ( - - - {children} + return ( + + + {children} - {/* In development mode, render a fixed component displaying the current viewport + {/* In development mode, render a fixed component displaying the current viewport width and corresponding Tailwind breakpoint, as a helper for responsive design. */} - {IS_DEVELOPMENT && } - - - ); + {IS_DEVELOPMENT && } + + + ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index f5eb808..d4ef025 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -3,13 +3,13 @@ import { LogoSection } from '~/components/organisms/sections/LogoSection'; import { ResourceSection } from '~/components/organisms/sections/ResourceSection'; export default function Home() { - return ( -
- + return ( +
+ - + - -
- ); + +
+ ); } diff --git a/src/components/atoms/icons/ArrowIcon/index.stories.tsx b/src/components/atoms/icons/ArrowIcon/index.stories.tsx index 242628f..8eefafa 100644 --- a/src/components/atoms/icons/ArrowIcon/index.stories.tsx +++ b/src/components/atoms/icons/ArrowIcon/index.stories.tsx @@ -14,9 +14,9 @@ type ArrowIconMeta = Meta; * Arrow icon that can point to the left or right. */ const meta = { - component: ArrowIcon, - tags: ['autodocs'], - title: 'Atoms/Icons/ArrowIcon' + component: ArrowIcon, + tags: ['autodocs'], + title: 'Atoms/Icons/ArrowIcon' } satisfies ArrowIconMeta; export default meta; @@ -34,16 +34,16 @@ type ArrowIconStory = StoryObj; * Arrow icon that points to the left. */ export const LeftDirection = { - args: { - direction: 'left' - } + args: { + direction: 'left' + } } satisfies ArrowIconStory; /** * Arrow icon that points to the right. */ export const RightDirection = { - args: { - direction: 'right' - } + args: { + direction: 'right' + } } satisfies ArrowIconStory; diff --git a/src/components/atoms/icons/ArrowIcon/index.tsx b/src/components/atoms/icons/ArrowIcon/index.tsx index 18c997e..3fc30f6 100644 --- a/src/components/atoms/icons/ArrowIcon/index.tsx +++ b/src/components/atoms/icons/ArrowIcon/index.tsx @@ -6,20 +6,20 @@ type Direction = 'left' | 'right'; * Props for the ArrowIcon component. */ type ArrowIconProps = React.HTMLAttributes & { - /** Direction of which the arrow should point to. */ - direction: Direction; + /** Direction of which the arrow should point to. */ + direction: Direction; }; export function ArrowIcon({ direction, className, ...props }: ArrowIconProps) { - /* Constants */ + /* Constants */ - const arrow = direction === 'left' ? '<-' : '->'; + const arrow = direction === 'left' ? '<-' : '->'; - /* JSX */ + /* JSX */ - return ( - - {arrow} - - ); + return ( + + {arrow} + + ); } diff --git a/src/components/atoms/logos/NextJSLogo/index.stories.tsx b/src/components/atoms/logos/NextJSLogo/index.stories.tsx index 0331e43..ad7218b 100644 --- a/src/components/atoms/logos/NextJSLogo/index.stories.tsx +++ b/src/components/atoms/logos/NextJSLogo/index.stories.tsx @@ -14,9 +14,9 @@ type NextJSLogoMeta = Meta; * The Next.js logo */ const meta = { - component: NextJSLogo, - tags: ['autodocs'], - title: 'Atoms/Logos/NextJSLogo' + component: NextJSLogo, + tags: ['autodocs'], + title: 'Atoms/Logos/NextJSLogo' } satisfies NextJSLogoMeta; export default meta; @@ -32,8 +32,8 @@ type NextJSLogoStory = StoryObj; * The default Next.js logo. */ export const Default = { - args: { - width: 180, - height: 37 - } + args: { + width: 180, + height: 37 + } } satisfies NextJSLogoStory; diff --git a/src/components/atoms/logos/NextJSLogo/index.tsx b/src/components/atoms/logos/NextJSLogo/index.tsx index c62778e..aaff383 100644 --- a/src/components/atoms/logos/NextJSLogo/index.tsx +++ b/src/components/atoms/logos/NextJSLogo/index.tsx @@ -13,12 +13,12 @@ import { cn } from '~/lib/utils'; type NextJSLogoProps = Omit; export function NextJSLogo({ className, ...props }: NextJSLogoProps) { - return ( - Next.js Logo - ); + return ( + Next.js Logo + ); } diff --git a/src/components/atoms/logos/VercelLogo/index.stories.tsx b/src/components/atoms/logos/VercelLogo/index.stories.tsx index 2a6161f..ea02dae 100644 --- a/src/components/atoms/logos/VercelLogo/index.stories.tsx +++ b/src/components/atoms/logos/VercelLogo/index.stories.tsx @@ -14,9 +14,9 @@ type VercelLogoMeta = Meta; * The Vercel logo */ const meta = { - component: VercelLogo, - tags: ['autodocs'], - title: 'Atoms/Logos/VercelLogo' + component: VercelLogo, + tags: ['autodocs'], + title: 'Atoms/Logos/VercelLogo' } satisfies VercelLogoMeta; export default meta; @@ -34,8 +34,8 @@ type VercelLogoStory = StoryObj; * The default Vercel logo. */ export const Default = { - args: { - width: 100, - height: 24 - } + args: { + width: 100, + height: 24 + } } satisfies VercelLogoStory; diff --git a/src/components/atoms/logos/VercelLogo/index.tsx b/src/components/atoms/logos/VercelLogo/index.tsx index 50065b2..9631b37 100644 --- a/src/components/atoms/logos/VercelLogo/index.tsx +++ b/src/components/atoms/logos/VercelLogo/index.tsx @@ -13,7 +13,7 @@ import { cn } from '~/lib/utils'; type VercelLogoProps = Omit; export function VercelLogo({ className, ...props }: VercelLogoProps) { - /* JSX */ + /* JSX */ - return Vercel Logo; + return Vercel Logo; } diff --git a/src/components/atoms/typography/Code/index.stories.tsx b/src/components/atoms/typography/Code/index.stories.tsx index 37cd0da..e92b173 100644 --- a/src/components/atoms/typography/Code/index.stories.tsx +++ b/src/components/atoms/typography/Code/index.stories.tsx @@ -14,9 +14,9 @@ type CodeMeta = Meta; * Code block for displaying code snippets. */ const meta = { - component: Code, - tags: ['autodocs'], - title: 'Atoms/Typography/Code' + component: Code, + tags: ['autodocs'], + title: 'Atoms/Typography/Code' } satisfies CodeMeta; export default meta; @@ -34,7 +34,7 @@ type CodeStory = StoryObj; * Default code block. */ export const Default = { - args: { - children: 'src/app/page.tsx' - } + args: { + children: 'src/app/page.tsx' + } } satisfies CodeStory; diff --git a/src/components/atoms/typography/Code/index.tsx b/src/components/atoms/typography/Code/index.tsx index 312681b..b6213b2 100644 --- a/src/components/atoms/typography/Code/index.tsx +++ b/src/components/atoms/typography/Code/index.tsx @@ -8,8 +8,8 @@ import { cn } from '~/lib/utils'; type CodeProps = React.HTMLAttributes; export const Code = forwardRef(({ className, ...props }, ref) => { - /* JSX */ + /* JSX */ - return ; + return ; }); Code.displayName = 'Code'; diff --git a/src/components/atoms/typography/Heading/index.stories.tsx b/src/components/atoms/typography/Heading/index.stories.tsx index bf92aae..c1a62a7 100644 --- a/src/components/atoms/typography/Heading/index.stories.tsx +++ b/src/components/atoms/typography/Heading/index.stories.tsx @@ -14,9 +14,9 @@ type HeadingMeta = Meta; * Heading 1 (h1) to heading 6 (h6) elements. */ const meta = { - component: Heading, - tags: ['autodocs'], - title: 'Atoms/Typography/Heading' + component: Heading, + tags: ['autodocs'], + title: 'Atoms/Typography/Heading' } satisfies HeadingMeta; export default meta; @@ -34,58 +34,58 @@ type HeadingStory = StoryObj; * Heading 1 (h1) element. */ export const H1 = { - args: { - children: 'Heading 1', - level: 1 - } + args: { + children: 'Heading 1', + level: 1 + } } satisfies HeadingStory; /** * Heading 2 (h2) element. */ export const H2 = { - args: { - children: 'Heading 2', - level: 2 - } + args: { + children: 'Heading 2', + level: 2 + } } satisfies HeadingStory; /** * Heading 3 (h3) element. */ export const H3 = { - args: { - children: 'Heading 3', - level: 3 - } + args: { + children: 'Heading 3', + level: 3 + } } satisfies HeadingStory; /** * Heading 4 (h4) element. */ export const H4 = { - args: { - children: 'Heading 4', - level: 4 - } + args: { + children: 'Heading 4', + level: 4 + } } satisfies HeadingStory; /** * Heading 5 (h5) element. */ export const H5 = { - args: { - children: 'Heading 5', - level: 5 - } + args: { + children: 'Heading 5', + level: 5 + } } satisfies HeadingStory; /** * Heading 6 (h6) element. */ export const H6 = { - args: { - children: 'Heading 6', - level: 6 - } + args: { + children: 'Heading 6', + level: 6 + } } satisfies HeadingStory; diff --git a/src/components/atoms/typography/Heading/index.tsx b/src/components/atoms/typography/Heading/index.tsx index 9189b00..0a12e47 100644 --- a/src/components/atoms/typography/Heading/index.tsx +++ b/src/components/atoms/typography/Heading/index.tsx @@ -4,16 +4,16 @@ import { forwardRef } from 'react'; import { cn } from '~/lib/utils'; const headingVariants = cva('font-semibold', { - variants: { - level: { - 1: 'text-3xl', - 2: 'text-2xl', - 3: 'text-xl', - 4: 'text-lg', - 5: 'text-base', - 6: 'text-sm' - } - } + variants: { + level: { + 1: 'text-3xl', + 2: 'text-2xl', + 3: 'text-xl', + 4: 'text-lg', + 5: 'text-base', + 6: 'text-sm' + } + } }); /** @@ -22,21 +22,21 @@ const headingVariants = cva('font-semibold', { type HeadingProps = React.HTMLAttributes & VariantProps; export const Heading = forwardRef(({ className, level = 1, ...props }, ref) => { - /* JSX */ + /* JSX */ - switch (level) { - case 1: - return

; - case 2: - return

; - case 3: - return

; - case 4: - return

; - case 5: - return

; - case 6: - return
; - } + switch (level) { + case 1: + return

; + case 2: + return

; + case 3: + return

; + case 4: + return

; + case 5: + return

; + case 6: + return
; + } }); Heading.displayName = 'Heading'; diff --git a/src/components/atoms/utils/ScreenSize/index.stories.tsx b/src/components/atoms/utils/ScreenSize/index.stories.tsx index 813c7b9..00fdd21 100644 --- a/src/components/atoms/utils/ScreenSize/index.stories.tsx +++ b/src/components/atoms/utils/ScreenSize/index.stories.tsx @@ -15,9 +15,9 @@ type ScreenSizeMeta = Meta; * This components displays the current screen size with its corresponding Tailwind CSS breakpoint. */ const meta = { - component: ScreenSize, - tags: ['autodocs'], - title: 'Atoms/Utils/ScreenSize' + component: ScreenSize, + tags: ['autodocs'], + title: 'Atoms/Utils/ScreenSize' } satisfies ScreenSizeMeta; export default meta; @@ -35,52 +35,52 @@ type ScreenSizeStory = StoryObj; * Responsive screen size. */ export const Responsive: ScreenSizeStory = { - parameters: { - viewport: { defaultViewport: 'responsive' } - } + parameters: { + viewport: { defaultViewport: 'responsive' } + } }; /** * Small screen size. Minimum width: 640px */ export const SM: ScreenSizeStory = { - parameters: { - viewport: { defaultViewport: 'sm' } - } + parameters: { + viewport: { defaultViewport: 'sm' } + } }; /** * Medium screen size. Minimum width: 768px */ export const MD: ScreenSizeStory = { - parameters: { - viewport: { defaultViewport: 'md' } - } + parameters: { + viewport: { defaultViewport: 'md' } + } }; /** * Large screen size. Minimum width: 1024px */ export const LG: ScreenSizeStory = { - parameters: { - viewport: { defaultViewport: 'lg' } - } + parameters: { + viewport: { defaultViewport: 'lg' } + } }; /** * Extra large screen size. Minimum width: 1280px */ export const XL: ScreenSizeStory = { - parameters: { - viewport: { defaultViewport: 'xl' } - } + parameters: { + viewport: { defaultViewport: 'xl' } + } }; /** * Extra extra large screen size. Minimum width: 1536px */ export const XXL: ScreenSizeStory = { - parameters: { - viewport: { defaultViewport: '2xl' } - } + parameters: { + viewport: { defaultViewport: '2xl' } + } }; diff --git a/src/components/atoms/utils/ScreenSize/index.tsx b/src/components/atoms/utils/ScreenSize/index.tsx index d98c216..06da551 100644 --- a/src/components/atoms/utils/ScreenSize/index.tsx +++ b/src/components/atoms/utils/ScreenSize/index.tsx @@ -3,46 +3,46 @@ import { useEffect, useState } from 'react'; export function ScreenSize() { - /* State */ - - const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); - - /* Derived State */ - - const { width, height } = dimensions; - - /* Render */ - - useEffect(() => { - function updateDimensions() { - setDimensions({ - width: window.innerWidth, - height: window.innerHeight - }); - } - - updateDimensions(); - window.addEventListener('resize', updateDimensions); - - return () => { - window.removeEventListener('resize', updateDimensions); - }; - }, []); - - /* JSX */ - - return ( -
- - {width.toLocaleString()} x {height.toLocaleString()} - -
- XS - SM - MD - LG - XL - 2XL -
- ); + /* State */ + + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + + /* Derived State */ + + const { width, height } = dimensions; + + /* Render */ + + useEffect(() => { + function updateDimensions() { + setDimensions({ + width: window.innerWidth, + height: window.innerHeight + }); + } + + updateDimensions(); + window.addEventListener('resize', updateDimensions); + + return () => { + window.removeEventListener('resize', updateDimensions); + }; + }, []); + + /* JSX */ + + return ( +
+ + {width.toLocaleString()} x {height.toLocaleString()} + +
+ XS + SM + MD + LG + XL + 2XL +
+ ); } diff --git a/src/components/molecules/blocks/CreatorCredit/index.stories.tsx b/src/components/molecules/blocks/CreatorCredit/index.stories.tsx index 7472f08..8b17eb0 100644 --- a/src/components/molecules/blocks/CreatorCredit/index.stories.tsx +++ b/src/components/molecules/blocks/CreatorCredit/index.stories.tsx @@ -14,9 +14,9 @@ type CreatorCreditMeta = Meta; * Displays the creator credit for the content. */ const meta = { - component: CreatorCredit, - tags: ['autodocs'], - title: 'Molecules/Blocks/CreatorCredit' + component: CreatorCredit, + tags: ['autodocs'], + title: 'Molecules/Blocks/CreatorCredit' } satisfies CreatorCreditMeta; export default meta; diff --git a/src/components/molecules/blocks/CreatorCredit/index.tsx b/src/components/molecules/blocks/CreatorCredit/index.tsx index 423d622..b0fec32 100644 --- a/src/components/molecules/blocks/CreatorCredit/index.tsx +++ b/src/components/molecules/blocks/CreatorCredit/index.tsx @@ -1,16 +1,16 @@ import { VercelLogo } from '~/components/atoms/logos/VercelLogo'; export function CreatorCredit() { - /* JSX */ + /* JSX */ - return ( - - By - - ); + return ( + + By + + ); } diff --git a/src/components/molecules/blocks/GetStarted/index.stories.tsx b/src/components/molecules/blocks/GetStarted/index.stories.tsx index f96696b..a5f80db 100644 --- a/src/components/molecules/blocks/GetStarted/index.stories.tsx +++ b/src/components/molecules/blocks/GetStarted/index.stories.tsx @@ -15,9 +15,9 @@ type GetStartedMeta = Meta; * This component displays a call-to-action block to help users get started with the application. */ const meta = { - component: GetStarted, - tags: ['autodocs'], - title: 'Molecules/Blocks/GetStarted' + component: GetStarted, + tags: ['autodocs'], + title: 'Molecules/Blocks/GetStarted' } satisfies GetStartedMeta; export default meta; diff --git a/src/components/molecules/blocks/GetStarted/index.tsx b/src/components/molecules/blocks/GetStarted/index.tsx index 7345689..96ce9ca 100644 --- a/src/components/molecules/blocks/GetStarted/index.tsx +++ b/src/components/molecules/blocks/GetStarted/index.tsx @@ -1,12 +1,12 @@ import { Code } from '~/components/atoms/typography/Code'; export function GetStarted() { - /* JSX */ + /* JSX */ - return ( -

- Get started by editing  - src/app/page.tsx -

- ); + return ( +

+ Get started by editing  + src/app/page.tsx +

+ ); } diff --git a/src/components/molecules/cards/ResourceCard/index.stories.tsx b/src/components/molecules/cards/ResourceCard/index.stories.tsx index b673d64..e0f334b 100644 --- a/src/components/molecules/cards/ResourceCard/index.stories.tsx +++ b/src/components/molecules/cards/ResourceCard/index.stories.tsx @@ -14,9 +14,9 @@ type ResourceCardMeta = Meta; * Displays a resource card with a title, description, and link. */ const meta = { - component: ResourceCard, - tags: ['autodocs'], - title: 'Molecules/Cards/ResourceCard' + component: ResourceCard, + tags: ['autodocs'], + title: 'Molecules/Cards/ResourceCard' } satisfies ResourceCardMeta; export default meta; @@ -34,25 +34,25 @@ type ResourceCardStory = StoryObj; * Default resource card. */ export const Default = { - args: { - title: 'Docs', - description: 'Find in-depth information about Next.js features and API.', - link: 'https://nextjs.org/docs' - } + args: { + title: 'Docs', + description: 'Find in-depth information about Next.js features and API.', + link: 'https://nextjs.org/docs' + } } satisfies ResourceCardStory; /** * Resource card with hover state. */ export const Hover = { - args: { - title: 'Docs', - description: 'Find in-depth information about Next.js features and API.', - link: 'https://nextjs.org/docs' - }, - parameters: { - pseudo: { - hover: true - } - } + args: { + title: 'Docs', + description: 'Find in-depth information about Next.js features and API.', + link: 'https://nextjs.org/docs' + }, + parameters: { + pseudo: { + hover: true + } + } } satisfies ResourceCardStory; diff --git a/src/components/molecules/cards/ResourceCard/index.tsx b/src/components/molecules/cards/ResourceCard/index.tsx index b52a5b4..6c797ec 100644 --- a/src/components/molecules/cards/ResourceCard/index.tsx +++ b/src/components/molecules/cards/ResourceCard/index.tsx @@ -5,33 +5,33 @@ import { Heading } from '~/components/atoms/typography/Heading'; * Props for the ArrowIcon component. */ type ResourceCardProps = { - /** Title of the card */ - title: string; + /** Title of the card */ + title: string; - /** Description of the card */ - description: string; + /** Description of the card */ + description: string; - /** Link to the resource */ - link: string; + /** Link to the resource */ + link: string; }; export function ResourceCard({ title, description, link }: ResourceCardProps) { - /* JSX */ + /* JSX */ - return ( - - - {title}{' '} - - -

{description}

-
- ); + return ( + + + {title}{' '} + + +

{description}

+
+ ); } diff --git a/src/components/organisms/sections/HeaderSection/index.stories.tsx b/src/components/organisms/sections/HeaderSection/index.stories.tsx index bd3747a..1585c3e 100644 --- a/src/components/organisms/sections/HeaderSection/index.stories.tsx +++ b/src/components/organisms/sections/HeaderSection/index.stories.tsx @@ -14,9 +14,9 @@ type HeaderSectionMeta = Meta; * Header section with a get started block and a creator credit block. */ const meta = { - component: HeaderSection, - tags: ['autodocs'], - title: 'Organisms/Sections/HeaderSection' + component: HeaderSection, + tags: ['autodocs'], + title: 'Organisms/Sections/HeaderSection' } satisfies HeaderSectionMeta; export default meta; diff --git a/src/components/organisms/sections/HeaderSection/index.tsx b/src/components/organisms/sections/HeaderSection/index.tsx index 63d247d..562cd7f 100644 --- a/src/components/organisms/sections/HeaderSection/index.tsx +++ b/src/components/organisms/sections/HeaderSection/index.tsx @@ -2,15 +2,15 @@ import { CreatorCredit } from '~/components/molecules/blocks/CreatorCredit'; import { GetStarted } from '~/components/molecules/blocks/GetStarted'; export function HeaderSection() { - /* JSX */ + /* JSX */ - return ( -
- + return ( +
+ -
- -
-
- ); +
+ +
+
+ ); } diff --git a/src/components/organisms/sections/LogoSection/index.stories.tsx b/src/components/organisms/sections/LogoSection/index.stories.tsx index b99ee25..0a5c046 100644 --- a/src/components/organisms/sections/LogoSection/index.stories.tsx +++ b/src/components/organisms/sections/LogoSection/index.stories.tsx @@ -14,9 +14,9 @@ type LogoSectionMeta = Meta; * Section with a centered Next.js logo. */ const meta = { - component: LogoSection, - tags: ['autodocs'], - title: 'Organisms/Sections/LogoSection' + component: LogoSection, + tags: ['autodocs'], + title: 'Organisms/Sections/LogoSection' } satisfies LogoSectionMeta; export default meta; diff --git a/src/components/organisms/sections/LogoSection/index.tsx b/src/components/organisms/sections/LogoSection/index.tsx index 2a35dd8..a529332 100644 --- a/src/components/organisms/sections/LogoSection/index.tsx +++ b/src/components/organisms/sections/LogoSection/index.tsx @@ -1,11 +1,11 @@ import { NextJSLogo } from '~/components/atoms/logos/NextJSLogo'; export function LogoSection() { - /* JSX */ + /* JSX */ - return ( -
- -
- ); + return ( +
+ +
+ ); } diff --git a/src/components/organisms/sections/ResourceSection/index.stories.tsx b/src/components/organisms/sections/ResourceSection/index.stories.tsx index 157ed28..fd01ba1 100644 --- a/src/components/organisms/sections/ResourceSection/index.stories.tsx +++ b/src/components/organisms/sections/ResourceSection/index.stories.tsx @@ -14,9 +14,9 @@ type ResourceSectionMeta = Meta; * Section with a list of resources. */ const meta = { - component: ResourceSection, - tags: ['autodocs'], - title: 'Organisms/Sections/ResourceSection' + component: ResourceSection, + tags: ['autodocs'], + title: 'Organisms/Sections/ResourceSection' } satisfies ResourceSectionMeta; export default meta; diff --git a/src/components/organisms/sections/ResourceSection/index.tsx b/src/components/organisms/sections/ResourceSection/index.tsx index ea08e45..623a114 100644 --- a/src/components/organisms/sections/ResourceSection/index.tsx +++ b/src/components/organisms/sections/ResourceSection/index.tsx @@ -1,33 +1,33 @@ import { ResourceCard } from '../../../molecules/cards/ResourceCard'; export function ResourceSection() { - /* JSX */ + /* JSX */ - return ( -
- + return ( +
+ - + - + - -
- ); + +
+ ); } diff --git a/src/env.js b/src/env.js index 43fac90..a5b733f 100644 --- a/src/env.js +++ b/src/env.js @@ -2,44 +2,44 @@ import { createEnv } from '@t3-oss/env-nextjs'; import { z } from 'zod'; export const env = createEnv({ - /** - * Specify your client-side environment variables schema here. This way you can ensure the app - * isn't built with invalid env vars. To expose them to the client, prefix them with - * `NEXT_PUBLIC_`. - */ - client: { - // NEXT_PUBLIC_CLIENT_VAR: z.string(), - }, + /** + * Specify your client-side environment variables schema here. This way you can ensure the app + * isn't built with invalid env vars. To expose them to the client, prefix them with + * `NEXT_PUBLIC_`. + */ + client: { + // NEXT_PUBLIC_CLIENT_VAR: z.string(), + }, - /** - * Specify your server-side environment variables schema here. This way you can ensure the app - * isn't built with invalid env vars. - */ - server: { - NODE_ENV: z.enum(['development', 'test', 'production']).default('development') - }, + /** + * Specify your server-side environment variables schema here. This way you can ensure the app + * isn't built with invalid env vars. + */ + server: { + NODE_ENV: z.enum(['development', 'test', 'production']).default('development') + }, - /** - * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. - * middlewares) or client-side so we need to destruct manually. - */ - runtimeEnv: { - /* Client */ - // NEXT_PUBLIC_CLIENT_VAR: process.env.NEXT_PUBLIC_CLIENT_VAR + /** + * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. + * middlewares) or client-side so we need to destruct manually. + */ + runtimeEnv: { + /* Client */ + // NEXT_PUBLIC_CLIENT_VAR: process.env.NEXT_PUBLIC_CLIENT_VAR - /* Server */ - NODE_ENV: process.env.NODE_ENV - }, + /* Server */ + NODE_ENV: process.env.NODE_ENV + }, - /** - * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially - * useful for Docker builds. - */ - skipValidation: !!process.env.SKIP_ENV_VALIDATION, + /** + * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially + * useful for Docker builds. + */ + skipValidation: !!process.env.SKIP_ENV_VALIDATION, - /** - * Makes it so that empty strings are treated as undefined. - * `SOME_VAR: z.string()` and `SOME_VAR=''` will throw an error. - */ - emptyStringAsUndefined: true + /** + * Makes it so that empty strings are treated as undefined. + * `SOME_VAR: z.string()` and `SOME_VAR=''` will throw an error. + */ + emptyStringAsUndefined: true }); diff --git a/src/lib/__tests__/utils.test.mjs b/src/lib/__tests__/utils.test.mjs index b210997..96ac7f9 100644 --- a/src/lib/__tests__/utils.test.mjs +++ b/src/lib/__tests__/utils.test.mjs @@ -1,11 +1,11 @@ import { cn } from '~/lib/utils'; describe('cn', () => { - it('merges tailwind class names', () => { - const classNames = cn('bg-red hover:bg-dark-red px-2 py-1', 'bg-[#b91c1c] p-3'); - const expectedClasses = ['hover:bg-dark-red', 'p-3', 'bg-[#b91c1c]']; + it('merges tailwind class names', () => { + const classNames = cn('bg-red hover:bg-dark-red px-2 py-1', 'bg-[#b91c1c] p-3'); + const expectedClasses = ['hover:bg-dark-red', 'p-3', 'bg-[#b91c1c]']; - expect(classNames.split(' ').sort()).toEqual(expectedClasses.sort()); - expect(classNames.split(' ').length).toBe(expectedClasses.length); - }); + expect(classNames.split(' ').sort()).toEqual(expectedClasses.sort()); + expect(classNames.split(' ').length).toBe(expectedClasses.length); + }); }); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 23840fe..c5db915 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,13 +2,13 @@ import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); + return twMerge(clsx(inputs)); } export function toPascalCase(string: string) { - return string - .replace(/(\w)(\w*)/g, function (g0: string, g1: string, g2: string) { - return g1.toUpperCase() + g2.toLowerCase(); - }) - .replace(/\s+/g, ''); + return string + .replace(/(\w)(\w*)/g, function (g0: string, g1: string, g2: string) { + return g1.toUpperCase() + g2.toLowerCase(); + }) + .replace(/\s+/g, ''); } diff --git a/src/styles/globals.css b/src/styles/globals.css index 64437f4..8d56888 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -1,18 +1,18 @@ :root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; } @media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } } body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); + color: rgb(var(--foreground-rgb)); + background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); } diff --git a/src/styles/tailwind.css b/src/styles/tailwind.css index f4ec6ab..79008c2 100644 --- a/src/styles/tailwind.css +++ b/src/styles/tailwind.css @@ -3,7 +3,7 @@ @tailwind utilities; @layer utilities { - .text-balance { - text-wrap: balance; - } + .text-balance { + text-wrap: balance; + } } diff --git a/tailwind.config.ts b/tailwind.config.ts index d6a71aa..c4bbb1a 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,25 +1,25 @@ import type { Config } from 'tailwindcss'; const config: Config = { - /** - * Configure the paths to all of your HTML templates, JS components, and any other files that contain Tailwind class names. - * @see https://tailwindcss.com/docs/content-configuration - */ - content: ['./src/**/*.tsx'], + /** + * Configure the paths to all of your HTML templates, JS components, and any other files that contain Tailwind class names. + * @see https://tailwindcss.com/docs/content-configuration + */ + content: ['./src/**/*.tsx'], - /** - * Define your project’s color palette, type scale, fonts, breakpoints, border radius values, and more. - */ - theme: { - extend: { - backgroundImage: { - 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', - 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))' - } - } - }, + /** + * Define your project’s color palette, type scale, fonts, breakpoints, border radius values, and more. + */ + theme: { + extend: { + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))' + } + } + }, - safelist: ['dark'] + safelist: ['dark'] }; export default config; diff --git a/tests/example.test.ts b/tests/example.test.ts index 56f8d93..0b5c765 100644 --- a/tests/example.test.ts +++ b/tests/example.test.ts @@ -1,18 +1,18 @@ import { expect, test } from '@playwright/test'; test('has title', async ({ page }) => { - await page.goto('https://playwright.dev/'); + await page.goto('https://playwright.dev/'); - // Expect a title "to contain" a substring. - await expect(page).toHaveTitle(/Playwright/); + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); }); test('get started link', async ({ page }) => { - await page.goto('https://playwright.dev/'); + await page.goto('https://playwright.dev/'); - // Click the get started link. - await page.getByRole('link', { name: 'Get started' }).click(); + // Click the get started link. + await page.getByRole('link', { name: 'Get started' }).click(); - // Expects page to have a heading with the name of Installation. - await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); + // Expects page to have a heading with the name of Installation. + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); }); diff --git a/tsconfig.json b/tsconfig.json index 11b39e6..3b5ceb5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,43 +1,43 @@ { - "compilerOptions": { - /* Base Options */ - "esModuleInterop": true, - "skipLibCheck": true, - "target": "ES2022", - "allowJs": true, - "resolveJsonModule": true, - "moduleDetection": "force", - "isolatedModules": true, + "compilerOptions": { + /* Base Options */ + "esModuleInterop": true, + "skipLibCheck": true, + "target": "ES2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, - /* Strictness */ - "strict": true, - "noUncheckedIndexedAccess": true, - "checkJs": true, + /* Strictness */ + "strict": true, + "noUncheckedIndexedAccess": true, + "checkJs": true, - /* Bundled Projects */ - "lib": ["dom", "dom.iterable", "ES2022"], - "noEmit": true, - "module": "ESNext", - "moduleResolution": "Bundler", - "jsx": "preserve", - "plugins": [{ "name": "next" }], - "incremental": true, + /* Bundled Projects */ + "lib": ["dom", "dom.iterable", "ES2022"], + "noEmit": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "preserve", + "plugins": [{ "name": "next" }], + "incremental": true, - /* Path Aliases */ - "baseUrl": ".", - "paths": { - "~/*": ["./src/*"] - } - }, - "include": [ - ".eslintrc.cjs", - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - "**/*.cjs", - "**/*.js", - "**/*.mjs", - ".next/types/**/*.ts" - ], - "exclude": ["node_modules"] + /* Path Aliases */ + "baseUrl": ".", + "paths": { + "~/*": ["./src/*"] + } + }, + "include": [ + ".eslintrc.cjs", + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + "**/*.cjs", + "**/*.js", + "**/*.mjs", + ".next/types/**/*.ts" + ], + "exclude": ["node_modules"] }