Compare commits

..

3 Commits

Author SHA1 Message Date
e560248
9ad33b98e7 add ComponentWrapper 2025-04-08 09:12:38 +02:00
e560248
4502c7aef0 update folder structure 2025-04-08 09:00:32 +02:00
e560248
b7ce3dbc21 update vitest setup 2025-04-08 08:55:21 +02:00
30 changed files with 1783 additions and 24 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,12 @@
"dev": "vite", "dev": "vite",
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview" "preview": "vite preview",
"test": "vitest",
"test:watch": "vitest --watch",
"test:coverage": "vitest --coverage",
"test:coverage:report": "vitest --coverage --reporter=html",
"test:coverage:report:open": "vitest --coverage --reporter=html && open coverage/index.html"
}, },
"dependencies": { "dependencies": {
"react": "^19.0.0", "react": "^19.0.0",
@@ -16,13 +21,17 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.21.0", "@eslint/js": "^9.21.0",
"@testing-library/react": "^16.3.0",
"@types/react": "^19.0.10", "@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4", "@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"@vitest/coverage-v8": "^3.1.1",
"@vitest/ui": "^3.1.1",
"eslint": "^9.21.0", "eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19", "eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0", "globals": "^15.15.0",
"jsdom": "^26.0.0",
"typescript": "~5.7.2", "typescript": "~5.7.2",
"typescript-eslint": "^8.24.1", "typescript-eslint": "^8.24.1",
"vite": "^6.2.0", "vite": "^6.2.0",

View File

@@ -7,6 +7,7 @@ import UsersPage from './routes/UsersPage'
import EffectExercisesPage from './routes/EffectExercisesPage' import EffectExercisesPage from './routes/EffectExercisesPage'
import FormsPage from './routes/FormsPage' import FormsPage from './routes/FormsPage'
import MemoCallbackPage from './routes/MemoCallbackPage' import MemoCallbackPage from './routes/MemoCallbackPage'
import ComponentWrapperPage from './routes/ComponentWrapperPage'
function App() { function App() {
@@ -17,6 +18,8 @@ function App() {
<Route path="/effect" element={<EffectExercisesPage />} /> <Route path="/effect" element={<EffectExercisesPage />} />
<Route path="/forms" element={<FormsPage />} /> <Route path="/forms" element={<FormsPage />} />
<Route path="/memocallback" element={<MemoCallbackPage />} /> <Route path="/memocallback" element={<MemoCallbackPage />} />
<Route path="/componentwrapper" element={<ComponentWrapperPage />} />
</Routes> </Routes>
</MainLayout> </MainLayout>
} }

View File

@@ -0,0 +1,19 @@
.component-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
background-color: #1a1a1a;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.component-content {
width: 100%;
max-width: 800px;
background-color: #888;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

View File

@@ -0,0 +1,17 @@
import './ComponentWrapper.css'
interface ComponentWrapperProps {
title?: string;
children: React.ReactNode;
}
export default function ComponentWrapper({ children, title }: ComponentWrapperProps): React.ReactElement {
console.log("ComponentWrapper rendered");
return (
<div className='component-wrapper'>
{title && <h2>{title}</h2>}
<div className='component-content'>
{children}
</div>
</div>
);
}

View File

@@ -0,0 +1,10 @@
import { render, screen } from "@testing-library/react";
import Footer from "./Footer";
import { describe, it, expect } from "vitest";
describe("Footer Component", () => {
it("should render the footer with correct content", () => {
render(<Footer />);
expect(screen.getByText(/© 2025 Your Company/i)).toBeDefined();
});
});

View File

@@ -7,6 +7,7 @@ export default function Navigation () {
<li><a href="/effect">Effects</a></li> <li><a href="/effect">Effects</a></li>
<li><a href="/forms">Forms</a></li> <li><a href="/forms">Forms</a></li>
<li><a href="/memocallback">Memo und Callback</a></li> <li><a href="/memocallback">Memo und Callback</a></li>
<li><a href="/componentwrapper">Component Wrapper</a></li>
</ul> </ul>
</nav>); </nav>);
} }

View File

@@ -0,0 +1,42 @@
import { render, screen, fireEvent } from "@testing-library/react";
import MemoCallback from "./index";
import { describe, it, expect } from "vitest";
import { users } from "../../../utils/shuffle";
describe("MemoCallback Component", () => {
it("should render the component with initial users", () => {
render(<MemoCallback />);
users.forEach((user) => {
expect(screen.getByText(user)).toBeDefined();
});
});
it("should filter users based on search input", () => {
render(<MemoCallback />);
const searchInput = screen.getByPlaceholderText(/search/i);
fireEvent.change(searchInput, { target: { value: "a" } });
const filteredUsers = users.filter((user) => user.toLowerCase().includes("a"));
filteredUsers.forEach((user) => {
expect(screen.getByText(user)).toBeDefined();
});
const nonMatchingUsers = users.filter((user) => !user.toLowerCase().includes("a"));
nonMatchingUsers.forEach((user) => {
expect(screen.queryByText(user)).not.toBeDefined();
});
});
it("should shuffle users when the shuffle button is clicked", () => {
render(<MemoCallback />);
const shuffleButton = screen.getByText(/shuffle/i);
const initialOrder = screen.getAllByText(/./).map((el) => el.textContent);
fireEvent.click(shuffleButton);
const shuffledOrder = screen.getAllByText(/./).map((el) => el.textContent);
expect(initialOrder).not.toEqual(shuffledOrder);
});
});

View File

@@ -1,6 +1,6 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { shuffleArray, users } from "../../../utils/shuffle";
import Search from "./Search"; import Search from "./Search";
import { users, shuffleArray } from "../../../utils/shuffle";
export default function MemoCallback() { export default function MemoCallback() {
console.log("MemoCallback rendered"); console.log("MemoCallback rendered");

View File

@@ -1,7 +1,7 @@
import { useState } from "react"; import { useState } from "react";
import Footer from "../components/Footer";
import Header from "../components/Header";
import './MainLayout.css' import './MainLayout.css'
import Footer from "../common/components/Footer";
import Header from "../common/components/Header";
export default function MainLayout({children}: {children: React.ReactNode | React.ReactElement | React.ReactElement[]}): React.ReactElement { export default function MainLayout({children}: {children: React.ReactNode | React.ReactElement | React.ReactElement[]}): React.ReactElement {

View File

@@ -0,0 +1,15 @@
import ComponentWrapper from "../common/components/ComponentWrapper/ComponentWrapper";
import EffectExercises from "../common/components/exercises/EffectExercises";
export default function ComponentWrapperPage() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '3rem' }}>
<ComponentWrapper title='Komponenten Wrapper Test'>
<h1>Hallo</h1>
</ComponentWrapper>
<ComponentWrapper title='Komponenten Wrapper Test2'>
<EffectExercises />
</ComponentWrapper>
</div>
);
}

View File

@@ -1,5 +1,6 @@
import EffectExercises from "../components/exercises/EffectExercises"; import EffectExercises from "../common/components/exercises/EffectExercises";
import Heading from "../components/globals/Heading" import Heading from "../common/components/globals/Heading";
export default function EffectExercisesPage() { export default function EffectExercisesPage() {
return ( return (

View File

@@ -1,5 +1,5 @@
import RefExercise from "../components/exercises/RefExercise"; import RefExercise from "../common/components/exercises/RefExercise";
import Heading from "../components/globals/Heading"; import Heading from "../common/components/globals/Heading";
export default function FormsPage() { export default function FormsPage() {
return ( return (

View File

@@ -1,4 +1,4 @@
import Heading from "../components/globals/Heading" import Heading from "../common/components/globals/Heading";
export default function HomePage() { export default function HomePage() {
return ( return (

View File

@@ -1,5 +1,5 @@
import MemoCallback from "../components/exercises/MemoCallback"; import MemoCallback from "../common/components/exercises/MemoCallback";
import Heading from "../components/globals/Heading"; import Heading from "../common/components/globals/Heading";
export default function MemoCallbackPage() { export default function MemoCallbackPage() {
return ( return (

View File

@@ -1,6 +1,6 @@
import UserEffect from "../components/exercises/UserEffect"; import UserEffect from "../common/components/exercises/UserEffect";
import { UserOverview } from "../components/exercises/UserOverview"; import { UserOverview } from "../common/components/exercises/UserOverview";
import Heading from "../components/globals/Heading"; import Heading from "../common/components/globals/Heading";
export default function UsersPage() { export default function UsersPage() {
return ( return (

View File

@@ -1,7 +1,11 @@
import { defineConfig } from 'vite' import { defineConfig, UserConfig } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
}) test: {
// 👋 add the line below to add jsdom to vite
environment: 'jsdom',
}
} as UserConfig)