docs: make provider dependency chain explicit with documentation and tests

This addresses issue #19 by making the implicit provider dependency order
explicit and order-sensitive.

Changes:
1. Created PROVIDER_ORDER.md - comprehensive documentation explaining:
   - The provider hierarchy from outermost to innermost
   - Why each provider must be at its position
   - Order-sensitive pitfalls and what would break
   - Guidelines for adding new providers in the future

2. Added provider composition tests (providerComposition.test.tsx):
   - 13 comprehensive tests validating provider order and dependencies
   - Tests verify all providers mount correctly
   - Tests check that hooks only work inside correct providers
   - Tests validate async initialization (AuthProvider, TimezoneProvider)
   - Tests verify theme persistence and notification propagation

3. Updated App.tsx with inline documentation:
   - Added detailed provider order contract in JSDoc header
   - Inline comments explaining each provider's position
   - Reference to PROVIDER_ORDER.md for detailed rationale

4. Updated Web-Development.md:
   - Added new section 5.5 'Provider Order Contract'
   - Documents provider hierarchy and rationale
   - Links to comprehensive provider documentation
   - References regression test suite

All tests pass. TypeScript compilation succeeds. Build succeeds.
The provider order is now explicit and future refactors can validate
compliance through the regression test suite.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-28 09:30:22 +02:00
parent d10145e5d6
commit 2fea513c9c
5 changed files with 748 additions and 26 deletions

View File

@@ -1,10 +1,16 @@
/**
* Application root component.
*
* Wraps the entire application in:
* 1. `FluentProvider` — supplies the Fluent UI theme and design tokens.
* 2. `BrowserRouter` — enables client-side routing via React Router.
* 3. `AuthProvider` — manages session state and exposes `useAuth()`.
* Provider order (see `src/providers/PROVIDER_ORDER.md` for detailed contract):
* 1. `ThemeProvider` — OUTERMOST; provides theme context to AppContents
* 2. `FluentProvider` — supplies Fluent UI theme and design tokens
* 3. `NotificationProvider` — provides notification service to all descendants
* 4. `ErrorBoundary` — catches catastrophic errors
* 5. `BrowserRouter` — enables client-side routing via React Router
* 6. `AuthProvider` — manages session state; validates on mount; uses useNavigate()
* 7. `TimezoneProvider` — INNERMOST (inside protected routes); fetches timezone after auth
*
* CRITICAL: Provider order is order-sensitive. See PROVIDER_ORDER.md before refactoring.
*
* Routes:
* - `/setup` — first-run setup wizard (always accessible; redirects to /login if already done)
@@ -51,22 +57,36 @@ const BlocklistsPage = lazy(() => import("./pages/BlocklistsPage").then((m) => (
/**
* Root application component — mounts providers and top-level routes.
*
* Provider stack (see PROVIDER_ORDER.md for detailed contract):
* - FluentProvider (2) — receives theme from useThemeMode()
* - NotificationProvider (3) — provides notification service
* - ErrorBoundary (4) — catches catastrophic errors
* - BrowserRouter (5) — enables routing
* - AuthProvider (6) — session validation; uses useNavigate()
* - TimezoneProvider (7) — inside protected routes only
*/
function AppContents(): React.JSX.Element {
const { colorMode } = useThemeMode();
const theme = colorMode === "dark" ? darkTheme : lightTheme;
return (
// 2. FluentProvider — supplies Fluent UI theme and tokens
<FluentProvider theme={theme}>
{/* 3. NotificationProvider — makes notification service available */}
<NotificationProvider>
{/* 4. ErrorBoundary — catches catastrophic errors that would crash the app */}
<ErrorBoundary
title="Application Error"
message="The application encountered a critical error. Reloading may help."
isFullPage={true}
>
{/* Notification container must be rendered inside ErrorBoundary */}
<NotificationContainer />
{/* 5. BrowserRouter — enables routing; required by AuthProvider's useNavigate() */}
<BrowserRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
<Suspense fallback={<Spinner size="large" label="Loading…" />}>
{/* 6. AuthProvider — validates session on mount; must be inside BrowserRouter */}
<AuthProvider>
<Routes>
{/* Setup wizard — always accessible; redirects to /login if already done */}
@@ -96,6 +116,7 @@ function AppContents(): React.JSX.Element {
element={
<SetupGuard>
<RequireAuth>
{/* 7. TimezoneProvider — INNERMOST; fetches timezone after auth validation */}
<TimezoneProvider>
<MainLayout />
</TimezoneProvider>
@@ -174,6 +195,7 @@ function AppContents(): React.JSX.Element {
}
function App(): React.JSX.Element {
// ThemeProvider (1. OUTERMOST) — provides theme context needed by AppContents
return (
<ThemeProvider>
<AppContents />