first commit
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# `<BrandingProvider>` and tenant branding hooks
|
||||
|
||||
## Purpose
|
||||
|
||||
`branding-context.tsx` ships the `<BrandingProvider>` React context provider
|
||||
together with the hooks `useBranding`, `useTenantBranding`, and `useLogo`.
|
||||
The provider's job is to resolve a `TenantBranding` payload (either static
|
||||
via prop or fetched from a configurable endpoint) and to project that
|
||||
payload onto two surfaces: (1) the `@suaveplan/react-ui` runtime
|
||||
theme-token layer for every `--brand-*` CSS custom property, and (2) the
|
||||
non-CSS DOM chrome — `document.title`, `<link rel="icon">`, and the tenant
|
||||
custom-CSS `<style>` injection. The CSS path goes through
|
||||
`useThemeMutator().setExtensionTokens(...)` so per-tenant tokens stack on
|
||||
top of the active chassis theme exactly like any other mutator layer, with
|
||||
automatic cleanup on unmount.
|
||||
|
||||
## Features
|
||||
|
||||
- Static branding via prop OR endpoint fetch via `brandingEndpoint` (+
|
||||
optional `/tenantId` segment).
|
||||
- Injectable `fetch: FetchLike` for testability.
|
||||
- Zod validation (`TenantBrandingSchema.safeParse`) on every fetched
|
||||
payload — Zod failures surface as `BRANDING_INVALID_PAYLOAD`.
|
||||
- Network failures (transport throw or non-ok response) surface as
|
||||
`BRANDING_FETCH_FAILED`.
|
||||
- `--brand-primary`, `--brand-secondary`, `--brand-accent`, `--brand-font`
|
||||
written via `setExtensionTokens` — cleanup is automatic on unmount.
|
||||
- `document.title`, favicon `<link>`, and tenant `<style>` injection are
|
||||
direct DOM writes (not CSS variables; no conflict with the mutator).
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```tsx
|
||||
import { ThemeProvider, lightTheme } from "@suaveplan/react-ui/theme";
|
||||
import { BrandingProvider } from "@suaveplan/react-auth-whitelabel";
|
||||
|
||||
<ThemeProvider themes={[lightTheme]} defaultTheme="light">
|
||||
<BrandingProvider staticBranding={tenantBranding}>
|
||||
<App />
|
||||
</BrandingProvider>
|
||||
</ThemeProvider>
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```tsx
|
||||
<BrandingProvider
|
||||
brandingEndpoint="/api/branding"
|
||||
tenantId={tenantSlug}
|
||||
fetch={customFetch}
|
||||
>
|
||||
<App />
|
||||
</BrandingProvider>
|
||||
```
|
||||
|
||||
```tsx
|
||||
const branding = useTenantBranding();
|
||||
const lightLogo = useLogo(false);
|
||||
const darkLogo = useLogo(true);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `BrandingProvider(props)` — props include `brandingEndpoint?`,
|
||||
`tenantId?`, `staticBranding?`, `fetch?`, `children`.
|
||||
- `useBranding()` — returns `{ branding, isLoading, error, refresh }`.
|
||||
- `useTenantBranding()` — returns the current `TenantBranding`.
|
||||
- `useLogo(preferDark?)` — returns the most-appropriate logo URL or
|
||||
`undefined`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The mutator effect runs on every `branding` change AND on every
|
||||
`mutator` identity change. The mutator handle is stable across re-renders
|
||||
(it's `useMemo`-wrapped inside `@suaveplan/react-ui`), so the effect only
|
||||
re-fires when the branding payload actually changes. The cleanup
|
||||
contract for `--brand-*` is delegated to `useThemeMutator` — when the
|
||||
provider unmounts, the runtime invokes the mutator's `clear()` automatically,
|
||||
removing every `--brand-*` declaration from `document.documentElement.style`.
|
||||
Reference in New Issue
Block a user