diff --git a/eslint.config.ts b/eslint.config.ts index 40dc3da..1cf4f46 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -83,6 +83,7 @@ const config: Config[] = defineConfig([ }, }, rules: { + 'react/prop-types': 'off', 'react/jsx-curly-brace-presence': 'error', }, }, diff --git a/package-lock.json b/package-lock.json index 7c8c071..8b7b94a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -365,7 +365,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", - "extraneous": true, + "dev": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/tests-e2e/package-lock.json b/tests-e2e/package-lock.json index f61ed31..455b964 100644 --- a/tests-e2e/package-lock.json +++ b/tests-e2e/package-lock.json @@ -19,8 +19,8 @@ "@rspack/cli": "^1.4.11", "@rspack/core": "^1.4.11", "@types/node": "^24.2.1", - "@types/react": "^19.1.10", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "css-loader": "^7.1.2", "prettier": "^3.6.2", "typescript": "^5.9.2" @@ -995,23 +995,23 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz", - "integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", "dependencies": { - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz", - "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/retry": { @@ -1812,9 +1812,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "dev": true, "license": "MIT" }, @@ -4340,9 +4340,9 @@ "license": "MIT" }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "dev": true, "license": "MIT" }, diff --git a/tests-e2e/package.json b/tests-e2e/package.json index b7d825b..02544b7 100644 --- a/tests-e2e/package.json +++ b/tests-e2e/package.json @@ -16,8 +16,8 @@ "@rspack/cli": "^1.4.11", "@rspack/core": "^1.4.11", "@types/node": "^24.2.1", - "@types/react": "^19.1.10", - "@types/react-dom": "^19.1.7", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "css-loader": "^7.1.2", "prettier": "^3.6.2", "typescript": "^5.9.2" diff --git a/tests-e2e/src/reset.css b/tests-e2e/src/reset.css index c737884..925a4b9 100644 --- a/tests-e2e/src/reset.css +++ b/tests-e2e/src/reset.css @@ -45,3 +45,10 @@ input:not([type='checkbox']) { box-shadow: inset 0 0 0 2px #000; border-radius: 6px; } + +textarea { + font-size: 16px; + padding: 0 8px; + box-shadow: inset 0 0 0 2px #000; + border-radius: 6px; +} diff --git a/tests-e2e/stories/error-boundary/primary.m.css b/tests-e2e/stories/error-boundary/primary.m.css new file mode 100644 index 0000000..f9d8825 --- /dev/null +++ b/tests-e2e/stories/error-boundary/primary.m.css @@ -0,0 +1,18 @@ +.container { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; +} + +.broken { + border: 1px solid #eee; + border-radius: 8px; + padding: 16px; +} + +.fallback { + background: rgb(255, 162, 162); + border-radius: 8px; + padding: 16px; +} diff --git a/tests-e2e/stories/error-boundary/primary.story.tsx b/tests-e2e/stories/error-boundary/primary.story.tsx new file mode 100644 index 0000000..1a4bc8e --- /dev/null +++ b/tests-e2e/stories/error-boundary/primary.story.tsx @@ -0,0 +1,36 @@ +import { useState } from 'react'; +import { ErrorBoundary } from '@krutoo/utils/react'; +import styles from './primary.m.css'; + +export const meta = { + category: 'React components/ErrorBoundary', + title: 'Primary example', +}; + +function BrokenUI({ broken }: { broken?: boolean }) { + if (broken) { + throw Error('Render error'); + } + + return
I am fine 👍
; +} + +function Fallback() { + return
Oops, something broke =(
; +} + +export default function Example() { + const [broken, setBroken] = useState(false); + + return ( +
+ + + }> + + +
+ ); +} diff --git a/tests-e2e/tests/error-boundary.spec.ts b/tests-e2e/tests/error-boundary.spec.ts new file mode 100644 index 0000000..211c21d --- /dev/null +++ b/tests-e2e/tests/error-boundary.spec.ts @@ -0,0 +1,12 @@ +import { expect, test } from '@playwright/test'; + +test('ErrorBoundary', async ({ page }) => { + await page.goto('/sandbox.html?path=/error-boundary/primary'); + + expect(await page.textContent('body')).toContain('I am fine 👍'); + + await page.getByRole('button', { name: 'Broke it!' }).click(); + + expect(await page.textContent('body')).not.toContain('I am fine 👍'); + expect(await page.textContent('body')).toContain('Oops, something broke =('); +}); diff --git a/tests-e2e/tests/portal.spec.ts b/tests-e2e/tests/portal.spec.ts new file mode 100644 index 0000000..f058b46 --- /dev/null +++ b/tests-e2e/tests/portal.spec.ts @@ -0,0 +1,18 @@ +import { expect, test } from '@playwright/test'; + +test('Portal', async ({ page }) => { + await page.goto('/sandbox.html?path=/portal/primary'); + + const button = page.getByRole('button', { name: 'Toggle' }); + + expect(await button.count()).toBe(1); + expect(await page.locator('body > *').count()).toBe(1); + + await button.click(); + + expect(await page.locator('body > *').count()).toBe(2); + expect(await page.locator('body > *').last().textContent()).toContain('Hello from Portal!'); + + await button.click(); + expect(await page.locator('body > *').count()).toBe(1); +}); diff --git a/tests-e2e/tsconfig.json b/tests-e2e/tsconfig.json index 158a1db..a40bf36 100644 --- a/tests-e2e/tsconfig.json +++ b/tests-e2e/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "rootDir": ".", "noEmit": true, "target": "ESNext", "module": "NodeNext", @@ -13,18 +14,7 @@ "noUnusedLocals": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, - "allowImportingTsExtensions": true, - "paths": { - "@krutoo/utils": ["../src/mod.ts"], - "@krutoo/utils/di": ["../src/di/mod.ts"], - "@krutoo/utils/dom": ["../src/dom/mod.ts"], - "@krutoo/utils/math": ["../src/math/mod.ts"], - "@krutoo/utils/misc": ["../src/misc/mod.ts"], - "@krutoo/utils/react": ["../src/react/mod.ts"], - "@krutoo/utils/router": ["../src/router/mod.ts"], - "@krutoo/utils/rspack": ["../src/rspack/mod.ts"], - "@krutoo/utils/types": ["../src/types/mod.ts"] - } + "allowImportingTsExtensions": true }, "exclude": ["node_modules"] }