Companion to whp PR #48 (feat/publish-broken-asset-check). Ship together — the backend emits the warnings, this renders them.
The bug this fixes
handlePublish has always returned a warnings array in its publish response. Nothing in the editor has ever read it — TopBar.tsx checked only result.success and flashed "Published" for 3 seconds.
So the contact-form relay warning — "submissions will not be delivered until an administrator enables it" — has been dead code for its entire life. No customer has ever seen it. A customer whose contact form silently went nowhere was never told.
This makes warnings visible, which revives that warning for free and carries the new broken-asset warnings from the companion PR.
What's here
PublishWarnings.tsx — a non-blocking, dismissible banner. Renders nothing when there are no warnings (the common, healthy-site case).
TopBar.tsx — holds warnings in their own state and renders the banner in both the mobile and desktop branches (TopBar early-returns for mobile, so a single render site would silently not exist on mobile).
Details worth knowing
Non-blocking. The site did publish. role="status" (not alert), amber rather than the error red, dismissible.
Warnings survive the 3s "Published" flash. They're held separately from publishStatus precisely so the existing setTimeout(…, 3000) doesn't take them down before the customer can read them.
Backend strings render as-is via a plain JSX text node ({w}), so React escapes them — the strings interpolate customer-controlled page names, and this is what stops a page titled with markup from self-injecting. No dangerouslySetInnerHTML.
.topbar gained position: relative — the banner's position: absolute; top: 100% needs a positioned ancestor or it renders off-screen. Verified zero blast radius: no other absolutely-positioned descendant lives under .topbar, the modals portal to body, and the mobile overflow menu is position: fixed.
Testing
918 tests / 90 files passing, tsc clean.
TopBar.test.tsx covers the wiring seam specifically — and that matters here more than usual, because the wiring was the bug: the relay warning died precisely because TopBar discarded result.warnings. The test was confirmed to fail against deliberately-broken wiring:
reverting to discarding result.warnings (the original bug) → 4/5 tests fail
re-coupling the 3s timer to clear warnings → only the "survives the flash" test fails, isolating that regression class
Tests use the repo's existing react-dom/client + act() harness (@testing-library/react isn't installed here).
Companion to **whp PR #48** (`feat/publish-broken-asset-check`). Ship together — the backend emits the warnings, this renders them.
## The bug this fixes
`handlePublish` has **always** returned a `warnings` array in its publish response. Nothing in the editor has ever read it — `TopBar.tsx` checked only `result.success` and flashed "Published" for 3 seconds.
So the contact-form relay warning — *"submissions will not be delivered until an administrator enables it"* — has been **dead code for its entire life**. No customer has ever seen it. A customer whose contact form silently went nowhere was never told.
This makes warnings visible, which revives that warning for free and carries the new broken-asset warnings from the companion PR.
## What's here
- **`PublishWarnings.tsx`** — a non-blocking, dismissible banner. Renders nothing when there are no warnings (the common, healthy-site case).
- **`TopBar.tsx`** — holds warnings in their own state and renders the banner in **both** the mobile and desktop branches (TopBar early-returns for mobile, so a single render site would silently not exist on mobile).
## Details worth knowing
- **Non-blocking.** The site *did* publish. `role="status"` (not `alert`), amber rather than the error red, dismissible.
- **Warnings survive the 3s "Published" flash.** They're held separately from `publishStatus` precisely so the existing `setTimeout(…, 3000)` doesn't take them down before the customer can read them.
- **Backend strings render as-is** via a plain JSX text node (`{w}`), so React escapes them — the strings interpolate customer-controlled page names, and this is what stops a page titled with markup from self-injecting. No `dangerouslySetInnerHTML`.
- `.topbar` gained `position: relative` — the banner's `position: absolute; top: 100%` needs a positioned ancestor or it renders off-screen. Verified zero blast radius: no other absolutely-positioned descendant lives under `.topbar`, the modals portal to body, and the mobile overflow menu is `position: fixed`.
## Testing
**918 tests / 90 files passing**, `tsc` clean.
`TopBar.test.tsx` covers the wiring seam specifically — and that matters here more than usual, because **the wiring *was* the bug**: the relay warning died precisely because TopBar discarded `result.warnings`. The test was confirmed to fail against deliberately-broken wiring:
- reverting to discarding `result.warnings` (the original bug) → **4/5 tests fail**
- re-coupling the 3s timer to clear warnings → **only** the "survives the flash" test fails, isolating that regression class
Tests use the repo's existing `react-dom/client` + `act()` harness (`@testing-library/react` isn't installed here).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
handlePublish's JSON response has always included a `warnings` array
(e.g. the contact-form relay's "submissions will not be delivered"
notice), but nothing in the editor ever read it. Adds a PublishWarnings
banner, held in its own state independent of the 3s publishStatus
flash so the customer has time to read it, rendered in both the
desktop and mobile TopBar branches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PublishWarnings.tsx had unit tests for the presentational banner, but
nothing asserted that result.warnings from publish() actually flows
through TopBar's handlePublish into it. That 3-line seam is exactly what
this feature exists to fix -- the backend always returned warnings, and
TopBar discarded them by only checking result.success, so the
contact-form-relay warning was dead code for its entire life.
Adds TopBar.test.tsx asserting: warnings render after a successful
publish with warnings, no banner renders when warnings is absent, a
warning doesn't present as a publish failure, warnings survive the 3s
"Published" flash (fake timers, advanced past 3000ms), and a fresh
publish clears stale warnings from the previous one.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jknapp
merged commit f5d2a23a5f into main2026-07-16 12:34:33 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Companion to whp PR #48 (
feat/publish-broken-asset-check). Ship together — the backend emits the warnings, this renders them.The bug this fixes
handlePublishhas always returned awarningsarray in its publish response. Nothing in the editor has ever read it —TopBar.tsxchecked onlyresult.successand flashed "Published" for 3 seconds.So the contact-form relay warning — "submissions will not be delivered until an administrator enables it" — has been dead code for its entire life. No customer has ever seen it. A customer whose contact form silently went nowhere was never told.
This makes warnings visible, which revives that warning for free and carries the new broken-asset warnings from the companion PR.
What's here
PublishWarnings.tsx— a non-blocking, dismissible banner. Renders nothing when there are no warnings (the common, healthy-site case).TopBar.tsx— holds warnings in their own state and renders the banner in both the mobile and desktop branches (TopBar early-returns for mobile, so a single render site would silently not exist on mobile).Details worth knowing
role="status"(notalert), amber rather than the error red, dismissible.publishStatusprecisely so the existingsetTimeout(…, 3000)doesn't take them down before the customer can read them.{w}), so React escapes them — the strings interpolate customer-controlled page names, and this is what stops a page titled with markup from self-injecting. NodangerouslySetInnerHTML..topbargainedposition: relative— the banner'sposition: absolute; top: 100%needs a positioned ancestor or it renders off-screen. Verified zero blast radius: no other absolutely-positioned descendant lives under.topbar, the modals portal to body, and the mobile overflow menu isposition: fixed.Testing
918 tests / 90 files passing,
tscclean.TopBar.test.tsxcovers the wiring seam specifically — and that matters here more than usual, because the wiring was the bug: the relay warning died precisely because TopBar discardedresult.warnings. The test was confirmed to fail against deliberately-broken wiring:result.warnings(the original bug) → 4/5 tests failTests use the repo's existing
react-dom/client+act()harness (@testing-library/reactisn't installed here).🤖 Generated with Claude Code