From c8cc049651fe46de8c572c40e7ef7ba604bf913b Mon Sep 17 00:00:00 2001 From: Louis King Date: Wed, 22 Jul 2026 22:55:20 +0100 Subject: [PATCH] feat(web): React SPA shell, markdown migration, vitest coverage, v0.17 docs - Migrate footer, error page, and announcements to React; slim Jinja2 shell to SEO/head/config/fonts/theme-init only - Replace server-side markdown rendering with react-markdown (removes Python `markdown` dependency); custom pages + announcements ship raw markdown to the client - Add heading-anchor deep-links with CSS override for inherited colors - Extract pure helpers from pages (messageHelpers, mapMath, routesHelpers, profileHelpers, packetHelpers, packetGroupHelpers) for unit testability - Add comprehensive vitest suite: 59 test files, 315 tests covering all components, pages, and extracted helpers; global i18next mock + matchMedia stub + AbortController-aware apiMock in test infrastructure - Fix SortableTable test DOM nesting ( inside proper context) - Update docs for v0.17.0: upgrading.md release notes, README.md project structure + build description, i18n.md path fix - Delete REACT_MIGRATION.md (migration complete, unreferenced) - Add announcements dismiss-persistence + custom-page 404 E2E specs --- REACT_MIGRATION.md | 261 --- README.md | 10 +- docs/content.md | 17 +- docs/i18n.md | 2 +- docs/upgrading.md | 15 + e2e/content/pages/about.md | 4 + e2e/docker-compose.test.yml | 2 + e2e/tests/announcements.spec.ts | 31 + e2e/tests/custom-page.spec.ts | 32 +- e2e/tests/global.spec.ts | 17 + package-lock.json | 1548 ++++++++++++++++- package.json | 4 + pyproject.toml | 2 - src/meshcore_hub/web/app.py | 47 +- src/meshcore_hub/web/pages.py | 23 +- src/meshcore_hub/web/static/css/app.css | 14 + .../web/static/js/spa-react/App.tsx | 8 +- .../js/spa-react/components/Alerts.test.tsx | 37 + .../components/Announcements.test.tsx | 15 +- .../js/spa-react/components/Announcements.tsx | 15 +- .../spa-react/components/AuthSection.test.tsx | 65 + .../components/AutoRefreshToggle.test.tsx | 7 - .../components/ErrorBoundary.test.tsx | 59 + .../spa-react/components/FilterForm.test.tsx | 7 - .../js/spa-react/components/Footer.test.tsx | 94 + .../static/js/spa-react/components/Footer.tsx | 115 ++ .../js/spa-react/components/JsonTree.test.tsx | 63 + .../spa-react/components/ListToolbar.test.tsx | 9 +- .../js/spa-react/components/Markdown.test.tsx | 111 ++ .../js/spa-react/components/Markdown.tsx | 45 + .../spa-react/components/MobileNav.test.tsx | 28 + .../js/spa-react/components/Navbar.test.tsx | 9 +- .../spa-react/components/NodeDisplay.test.tsx | 46 + .../components/ObserverBadges.test.tsx | 86 + .../spa-react/components/PacketParts.test.tsx | 9 +- .../spa-react/components/Pagination.test.tsx | 56 + .../components/RouteTypeBadge.test.tsx | 36 + .../components/SortableTable.test.tsx | 126 ++ .../js/spa-react/components/StatCard.test.tsx | 36 + .../spa-react/components/ThemeToggle.test.tsx | 39 + .../components/charts/Charts.test.tsx | 70 + .../spa-react/pages/Advertisements.test.tsx | 64 + .../js/spa-react/pages/Channels.test.tsx | 72 + .../js/spa-react/pages/CustomPage.test.tsx | 83 + .../static/js/spa-react/pages/CustomPage.tsx | 21 +- .../js/spa-react/pages/Dashboard.test.tsx | 57 + .../static/js/spa-react/pages/Home.test.tsx | 59 + .../js/spa-react/pages/Maintenance.test.tsx | 14 + .../js/spa-react/pages/MapPage.test.tsx | 67 + .../web/static/js/spa-react/pages/MapPage.tsx | 55 +- .../js/spa-react/pages/Members.test.tsx | 54 + .../js/spa-react/pages/Messages.test.tsx | 65 + .../static/js/spa-react/pages/Messages.tsx | 158 +- .../js/spa-react/pages/NodeDetail.test.tsx | 77 + .../static/js/spa-react/pages/Nodes.test.tsx | 78 + .../js/spa-react/pages/NotFound.test.tsx | 23 + .../js/spa-react/pages/PacketDetail.test.tsx | 74 + .../pages/PacketGroupDetail.test.tsx | 73 + .../js/spa-react/pages/PacketGroupDetail.tsx | 15 +- .../js/spa-react/pages/Packets.test.tsx | 67 + .../web/static/js/spa-react/pages/Packets.tsx | 23 +- .../js/spa-react/pages/Profile.test.tsx | 76 + .../web/static/js/spa-react/pages/Profile.tsx | 12 +- .../static/js/spa-react/pages/Routes.test.tsx | 78 + .../web/static/js/spa-react/pages/Routes.tsx | 47 +- .../web/static/js/spa-react/test/apiMock.ts | 29 + .../js/spa-react/test/renderWithProviders.tsx | 13 +- .../web/static/js/spa-react/test/setup.ts | 26 +- .../static/js/spa-react/utils/mapMath.test.ts | 64 + .../web/static/js/spa-react/utils/mapMath.ts | 56 + .../js/spa-react/utils/messageHelpers.test.ts | 173 ++ .../js/spa-react/utils/messageHelpers.ts | 176 ++ .../utils/packetGroupHelpers.test.ts | 26 + .../js/spa-react/utils/packetGroupHelpers.ts | 19 + .../js/spa-react/utils/packetHelpers.test.ts | 36 + .../js/spa-react/utils/packetHelpers.ts | 24 + .../js/spa-react/utils/profileHelpers.test.ts | 28 + .../js/spa-react/utils/profileHelpers.ts | 11 + .../js/spa-react/utils/routesHelpers.test.ts | 93 + .../js/spa-react/utils/routesHelpers.ts | 56 + src/meshcore_hub/web/static/locales/en.json | 3 +- src/meshcore_hub/web/static/locales/nl.json | 3 +- src/meshcore_hub/web/templates/spa.html | 46 +- tests/test_web/test_app.py | 75 +- tests/test_web/test_home.py | 35 +- tests/test_web/test_pages.py | 63 +- 86 files changed, 4886 insertions(+), 801 deletions(-) delete mode 100644 REACT_MIGRATION.md create mode 100644 e2e/tests/announcements.spec.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/Alerts.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/AuthSection.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/ErrorBoundary.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/Footer.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/Footer.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/JsonTree.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/Markdown.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/Markdown.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/MobileNav.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/NodeDisplay.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/ObserverBadges.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/Pagination.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/RouteTypeBadge.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/SortableTable.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/StatCard.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/ThemeToggle.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/components/charts/Charts.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Advertisements.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Channels.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/CustomPage.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Dashboard.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Home.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Maintenance.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/MapPage.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Members.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Messages.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/NodeDetail.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Nodes.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/NotFound.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/PacketDetail.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/PacketGroupDetail.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Packets.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Profile.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/pages/Routes.test.tsx create mode 100644 src/meshcore_hub/web/static/js/spa-react/test/apiMock.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/mapMath.test.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/mapMath.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/messageHelpers.test.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/messageHelpers.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/packetGroupHelpers.test.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/packetGroupHelpers.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/packetHelpers.test.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/packetHelpers.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/profileHelpers.test.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/profileHelpers.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/routesHelpers.test.ts create mode 100644 src/meshcore_hub/web/static/js/spa-react/utils/routesHelpers.ts diff --git a/REACT_MIGRATION.md b/REACT_MIGRATION.md deleted file mode 100644 index a4bf4be..0000000 --- a/REACT_MIGRATION.md +++ /dev/null @@ -1,261 +0,0 @@ -# React Migration Plan - -Migration from lit-html (functional templates) to React 19 + TypeScript + Vite. - -## Status - -| Phase | Description | Status | -|-------|-------------|--------| -| 1 | Infrastructure (Vite, React shell, router, LitBridge, build pipeline, shared components) | **Complete** | -| 2 | Convert pages one-by-one from LitBridge to native React | **Complete** | -| 3 | Chart & map components (react-chartjs-2, react-leaflet) | **Complete** | -| 4 | Cleanup (remove lit-html, old spa/, LitBridge, @legacy alias) | **Complete** | -| 5 | Frontend CI + vitest unit/component tests + navbar → React (SPA shell) | **Complete** | - -> **Phase 3 status:** All `window.Chart` / `window.L` / `window.QRCode` globals and the -> `charts.js` helper script are gone. Charts now use **react-chartjs-2** (typed builders in -> `spa-react/utils/charts.ts` + components in `spa-react/components/charts/Charts.tsx`), -> maps use **react-leaflet** (`MapPage.tsx`, `NodeDetail.tsx`), and QR codes use -> **react-qr-code** (`Channels.tsx`, `NodeDetail.tsx`). Chart.js, Leaflet (+ its CSS), and -> react-qr-code are bundled by Vite — the vendor `