├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── setup └── vitest.ts ├── src ├── components │ ├── App │ │ ├── App.css │ │ ├── App.test.tsx │ │ └── App.tsx │ └── Button.css │ │ ├── Button.module.css │ │ ├── Button.test.tsx │ │ └── Button.tsx ├── index.css ├── main.tsx └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── vitest.config.ts /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2022: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/strict-type-checked", 7 | "plugin:react-hooks/recommended", 8 | ], 9 | parserOptions: { 10 | project: true, 11 | tsconfigRootDir: __dirname, 12 | }, 13 | ignorePatterns: ["dist", ".eslintrc.cjs"], 14 | parser: "@typescript-eslint/parser", 15 | plugins: ["react-refresh"], 16 | }; 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node artifact files 2 | node_modules/ 3 | dist/ 4 | coverage/ 5 | 6 | # Log files 7 | *.log 8 | 9 | # JetBrains IDE 10 | .idea/ 11 | 12 | # Visual Studio Code 13 | .vscode/ 14 | 15 | # Generated by MacOS 16 | .DS_Store 17 | 18 | # Generated by Windows 19 | Thumbs.db 20 | 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Boilerplate: React single page application 2 | 3 | An enterprise SPA Web project template 4 | based on modern approaches and the latest version of React library. 5 | 6 | ## Features: 7 | - Quickly setup and configure your application. 8 | - [React](https://react.dev/) library lets you build user interfaces out of individual pieces called components. 9 | - **Typescript**: up-to-date version. 10 | - [Vite](https://vitejs.dev/) provides a modern development environment. 11 | - **Linter**: eslint + prettier = ❤️ 12 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Valletta Software 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-react-typescript-starter", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 11 | "test": "vitest", 12 | "coverage": "vitest run --coverage", 13 | "update": "npx npm-check-updates -i" 14 | }, 15 | "dependencies": { 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0" 18 | }, 19 | "devDependencies": { 20 | "@testing-library/jest-dom": "^6.2.0", 21 | "@testing-library/react": "^14.1.2", 22 | "@testing-library/user-event": "^14.5.2", 23 | "@types/react": "^18.2.48", 24 | "@types/react-dom": "^18.2.18", 25 | "@typescript-eslint/eslint-plugin": "^6.19.0", 26 | "@typescript-eslint/parser": "^6.19.0", 27 | "@vitejs/plugin-react": "^4.2.1", 28 | "@vitest/coverage-v8": "^1.2.1", 29 | "eslint": "^8.56.0", 30 | "eslint-plugin-react-hooks": "^4.6.0", 31 | "eslint-plugin-react-refresh": "^0.4.5", 32 | "jsdom": "^24.0.0", 33 | "prettier": "^3.2.4", 34 | "typescript": "^5.3.3", 35 | "vite": "^5.0.12", 36 | "vitest": "^1.2.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /setup/vitest.ts: -------------------------------------------------------------------------------- 1 | import { afterEach } from "vitest"; 2 | import { cleanup } from "@testing-library/react"; 3 | import "@testing-library/jest-dom/vitest"; 4 | 5 | afterEach(() => { 6 | cleanup(); 7 | }); 8 | -------------------------------------------------------------------------------- /src/components/App/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .App { 9 | display: flex; 10 | flex-direction: column; 11 | place-items: center; 12 | gap: 1rem; 13 | } 14 | 15 | .App__header { 16 | font-size: 3rem; 17 | font-weight: 700; 18 | margin: 0; 19 | } 20 | 21 | .App__count { 22 | font-size: 2rem; 23 | margin: 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/App/App.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from "@testing-library/react"; 2 | import user from "@testing-library/user-event"; 3 | import { expect, test, describe } from "vitest"; 4 | import { App } from "./App"; 5 | 6 | describe("Render the app correctly", () => { 7 | test("should render the title", async () => { 8 | render(); 9 | 10 | const header = await screen.findByText("Valletta Software"); 11 | 12 | expect(header).toBeInTheDocument(); 13 | }); 14 | 15 | test("should have a functioning button that increments the counter", async () => { 16 | render(); 17 | 18 | const button = await screen.findByRole("button"); 19 | const counter = await screen.findByText("0"); 20 | 21 | expect(counter.innerHTML).toBe("0"); 22 | 23 | await user.click(button); 24 | await user.click(button); 25 | 26 | expect(counter.innerHTML).toBe("2"); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/components/App/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Button } from "../Button.css/Button.tsx"; 3 | import "./App.css"; 4 | 5 | export function App() { 6 | const [count, setCount] = useState(0); 7 | 8 | const handleClick = () => { 9 | setCount(count + 1); 10 | }; 11 | 12 | return ( 13 |
14 |

React Boilerplate

15 |

by Valletta Software Development

16 |

{count}

17 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Button.css/Button.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | background-color: var(--Cyan-700); 3 | border: 0.25rem solid var(--Cyan-300); 4 | transition: border 0.2s ease-in-out; 5 | padding: 1rem 2rem; 6 | border-radius: 0.5rem; 7 | width: max-content; 8 | } 9 | 10 | .button:hover { 11 | border: 0.25rem solid var(--Cyan-500); 12 | } 13 | 14 | .text { 15 | color: var(--Cyan-300); 16 | font-size: 1.5rem; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Button.css/Button.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from "@testing-library/react"; 2 | import user from "@testing-library/user-event"; 3 | import { expect, test, describe, vi } from "vitest"; 4 | import { Button } from "./Button"; 5 | 6 | describe("Render the button correctly", () => { 7 | test("should render the button with the correct text", async () => { 8 | render( 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: system-ui, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | --Rose-800: #7d314b; 7 | --Rose-800: color(display-p3 0.4235 0.2235 0.2941); 8 | 9 | --Rose-200: #ffd9e4; 10 | --Rose-200: color(display-p3 0.9765 0.8627 0.8941); 11 | 12 | --Cyan-700: #007b67; 13 | --Cyan-700: color(display-p3 0.0902 0.4667 0.4078); 14 | 15 | --Cyan-500: #00c1a2; 16 | --Cyan-500: color(display-p3 0.2235 0.7294 0.6431); 17 | 18 | --Cyan-300: #77dfce; 19 | --Cyan-300: color(display-p3 0.6353 0.8588 0.8078); 20 | } 21 | 22 | body { 23 | margin: 0; 24 | padding: 0; 25 | box-sizing: border-box; 26 | display: flex; 27 | place-items: center; 28 | min-width: 320px; 29 | min-height: 100vh; 30 | color: var(--Rose-800); 31 | background: var(--Rose-200); 32 | } 33 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { App } from "./components/App/App.tsx"; 4 | import "./index.css"; 5 | 6 | createRoot(document.getElementById("root") as Element).render( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2022", "DOM"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | }, 23 | "include": ["src", "setup/vitest.ts", "vite.config.ts", "vitest.config.ts"], 24 | "references": [{ "path": "./tsconfig.node.json" }], 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }); 8 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from "vitest/config"; 2 | import viteConfig from "./vite.config"; 3 | 4 | export default mergeConfig( 5 | viteConfig, 6 | defineConfig({ 7 | test: { 8 | environment: "jsdom", 9 | setupFiles: "./setup/vitest.ts", 10 | include: ["**/*.test.tsx"], 11 | }, 12 | }) 13 | ); 14 | --------------------------------------------------------------------------------