feat(playground): a local app for running every hook against a real project - #17
Merged
Merged
Conversation
The visitor supplies their own web config, kept in localStorage. usePhoneSignIn sends real SMS and the email hooks consume quota, so a demo on the maintainer's project would be a standing invitation to run up their bill.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
… feat/demo-app
Renames `apps/demo` to `apps/playground` and rewrites it as a three-pane API-reference harness: sidebar site map, an explanation and snippet in the centre, and a sticky console on the right showing the hook's live `loading`, `error`, and resolved value. The app is never deployed. It exists so a contributor can exercise a change against a real Firebase project before opening a PR, which unit tests cannot cover because they mock `firebase/auth`. Config comes from `.env.local` only. - one page per docs group, `useSignup` first — a fresh project has no accounts, so nothing else can succeed on a first run - Shiki highlighting and a single token-based theme on `prefers-color-scheme`, replacing the mix of `dark:` and an unset `.dark` class - `/auth/callback` and `/auth/action` for the flows Firebase redirects back into - CONTRIBUTING gains a section on running it, and CI builds it so a broken export surfaces before publish
None of these were supported by anything — no measurement, no cited source. Replaced with what the code or the page can actually show. The playground carried a related one, a stale "roughly eighteen lines" against a snippet the docs put at 21; that went with the preceding commit.
The console showed what a hook returned but gave no way to change how it was called, so the prose could describe an option with nothing on screen to set it. Each hook now has an Options panel wired to its real options, and the snippet beside it regenerates from those settings — the code shown is the config the result came from. Callback options can be set to throw, which is the behaviour worth seeing: they run inside the flow, so throwing aborts the operation. The error panel used to show three renderings of a failure with no indication of how to get any of them. Each row is now labelled with the formatErrorMessage config that produces it, and that option is a control above it. Also: - wrap is one page-wide setting rather than per-response, and covers snippets too; responses are highlighted as JSON - "Format errors" reaches the sidebar at last — the provider state existed and the README documented it, but nothing ever rendered it - the sidebar tracks the section being read, via a scroll listener; IntersectionObserver only fires when the intersecting set changes, which leaves the highlight still through a tall section - only the centre column sticks now. A sticky column taller than the viewport can never scroll to its own bottom, and the console is the one that grows with the result
The four group pages became one page at /auth, with the groups as sections on it. A service is now an entry in lib/hooks-map.ts plus a page at /[slug] — the sidebar reads that map, so adding Firestore or Storage later touches nothing else. Each hook is its own file under components/auth/ rather than a function inside a growing page: 20 components averaging 68 lines. Also: - theme toggle — light, dark, or system. data-theme on <html> rather than prefers-color-scheme, since overriding the system is the point; an inline script resolves it before first paint, which an effect cannot do without flashing the wrong theme - settings moved out of the sidebar's foot into a bar across the top. Below the hook list there was nothing to announce them, and no reason to scroll a nav you had already read - the sidebar's brand block is fixed; only the list scrolls - useOAuthSignIn gets provider and method selects. The provider is an argument to signIn(), not an option on the hook, so it belongs in Options with the snippet showing which one you picked
Not one of the 21 pages showed initializeApp or getAuth. Getting started imported `auth` from '@/lib/firebase' behind a comment saying "your initialised Auth instance" — pointing at a file it never showed, which assumes the reader already knows the thing they came to learn. Getting started now opens with that file, the console screen the four config values come from, and why they are safe in client code. How hooks work says outright that `auth` comes from the firebase package rather than this one, for readers who land on a hook page first. Also rewrites the provider section. It led with "optional, but it's where app-wide defaults live" — the bonus first and the reason buried. The reason is that nothing else subscribes to auth state, so without it a sign-in on one page cannot tell a navbar on another. It now leads with that and shows useAuth beside it.
`pnpm typecheck` is recursive and the playground resolves the library through its exports map, so on a clean checkout it fails with 27 "cannot find module @timonwa/firebase-hooks/auth" errors — dist does not exist yet. Locally it passed only because a previous build had left dist behind. That made CONTRIBUTING's promise false: a green `pnpm verify` locally did not mean a green PR, and a contributor cloning fresh would hit it on their first run. Verified by moving dist aside and running verify from clean.
The bar had five things competing for attention — project, status, two labelled checkboxes with descriptions, and a three-button theme group — so it was two rows tall and read as heavier than the page under it. The project name now carries the weight, since every button below it mutates that project; everything else recedes. - settings move behind a gear popover, which gives the hints room to explain that a hook's own Options overrides the provider - one theme button cycling light → dark → system. The label names the current state and the next, because an icon cannot say what a click will do - lucide icons rather than ☀ ☾ ◐, whose optical weights differ by font - the bar mirrors <main>'s box: padding outside the width cap, not inside it. Sharing an element, its padding ate into the cap and every item sat 24px inboard of the content below - project and account stack below lg. Inline, truncation ate the account — the half you need before pressing Delete account Below lg the sidebar is now an off-canvas drawer, opened from the bar and closed by the ✕, the backdrop, Escape, or following any link. Links close it themselves: they are in-page anchors, so the pathname never changes and the drawer would otherwise sit over the section you just jumped to. It goes `invisible` when closed rather than only translating away — a drawer parked off-screen still holds its links in the tab order. Motion is one `--ease-panel` token shared by the drawer, backdrop, and popover, on transform and opacity only, with a global prefers-reduced-motion rule the app had been missing.
Format errors and Wrap code were plain useState, so every reload threw them away — only the theme was stored. The write lives in the setter rather than a second effect. Keyed on the value, that effect runs in the same commit as the read with the fallback still in hand, so it writes the fallback over what was stored; Strict Mode then re-runs the read, which picks up the value it just clobbered. It would have half-worked in a build and looked completely dead in dev. Reading stays in an effect: these pages are prerendered, and reading storage during render would disagree with the server's HTML.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
apps/playground— a Next app that runs all 20 auth hooks against a live Firebase project, so a change to the library can be exercised before a PR goes up. Unit tests mockfirebase/auth, so they prove orchestration and nothing about Firebase itself; this covers the other half.It is never deployed. Config comes from
.env.localonly. Bring your own project.What it looks like
Three panes per hook: the sidebar is the site map, the middle column explains the hook and shows the code, and the right column is a console you run it in — the hook's live
loadinganderror, then the value it resolved to.Options are settable
Every hook's own options are controls in an Options panel, and the snippet regenerates from them, so the code shown is the config the result came from.
sendVerificationEmail,recaptchaSize,actionCodeSettings,storageKey, the OAuth provider, andformatErrorMessageare all live.Callback options (
onIdToken,onBeforeSignOut,onBeforeDelete) can be set to throw — the operation aborts, which is the whole reason they run inside the flow rather than after it.Structure
One page per service at
/auth, groups as sections on it. Each hook is its own component undercomponents/auth/— 20 files, averaging 68 lines. A new service is an entry inlib/hooks-map.tsplus a page at/[slug]; the shell reads that map and needs no other change.Docs
Separately,
getting-startednever showed whereauthcomes from — no page in the docs calledinitializeApporgetAuth, and the example imported it from a file it never showed. Fixed, along with the provider section, which led with app-wide defaults and buried the actual reason the provider exists.Checks
pnpm verifygreen — 109 tests, typecheck, lint, build, publint, attw. Docs and playground both build. CI builds the playground on every PR, so a broken export surfaces before publish rather than after.No changeset: nothing here touches the published package.