Skip to content

fix(admin): wire admin.logo/siteName branding into login, signup, and invite-accept pages#1902

Open
marcusbellamyshaw-cell wants to merge 2 commits into
emdash-cms:mainfrom
marcusbellamyshaw-cell:fix/login-branding-step0
Open

fix(admin): wire admin.logo/siteName branding into login, signup, and invite-accept pages#1902
marcusbellamyshaw-cell wants to merge 2 commits into
emdash-cms:mainfrom
marcusbellamyshaw-cell:fix/login-branding-step0

Conversation

@marcusbellamyshaw-cell

Copy link
Copy Markdown
Contributor

What does this PR do?

Discussion #639 ("Admin white-labeling") was approved and merged as #705, adding admin: { logo, siteName, favicon } config. That approved scope explicitly listed "Login/onboarding page (large logo)" as a target surface, but it was never wired up — LoginPage, SignupPage, and InviteAcceptPage still render the stock hardcoded EmDash mark instead of the configured branding, so a configured logo doesn't show until after sign-in.

Root cause: authProviders reaches these pre-auth pages via a synchronous server-side prop chain (admin.astroAdminWrapperAdminApp → context), so there's no logo flash. The Sidebar/SetupWizard instead get branding via an async fetchManifest() call, which isn't usable pre-auth without a flash. This PR mirrors the authProviders pattern for branding: a new AdminBrandingProvider context fed from the same adminConfig admin.astro already reads server-side, threaded through AdminWrapper/AdminApp into LoginPage, SignupPage, and InviteAcceptPage (which already had BrandLogo/LogoLockup components with the right fallback logic — Signup/Invite just weren't passing them a logo/siteName).

Fully backward compatible: no branding configured → context defaults to {} → stock EmDash mark renders exactly as today.

Closes #639 (completes its approved-but-undelivered scope)

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Sonnet 5, via Claude Code's multi-agent (ultracode) workflow — implementation and independent adversarial review were done by separate agent instances; I (the requesting developer) verified the diff, ran the tests, and confirmed the red→green regression test myself before opening this PR.

… invite-accept pages

The sidebar and setup wizard already honored the configured admin.logo/
siteName white-label overrides (emdash-cms#639/PR emdash-cms#705), but LoginPage, SignupPage,
and InviteAcceptPage still rendered the stock hardcoded EmDash mark since
they run before authentication. admin.astro already reads the branding
config server-side per request; it's now passed down through AdminWrapper
-> AdminApp -> a new AdminBrandingProvider context (mirroring how
authProviders reaches the same pre-auth pages), so the configured logo
shows on first render with no flash and no unauthenticated API call.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 068a5c0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
emdash Patch
@emdash-cms/admin Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@1902

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@1902

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@1902

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@1902

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@1902

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@1902

emdash

npm i https://pkg.pr.new/emdash@1902

create-emdash

npm i https://pkg.pr.new/create-emdash@1902

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@1902

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@1902

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@1902

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@1902

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@1902

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@1902

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@1902

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@1902

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@1902

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@1902

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@1902

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@1902

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@1902

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@1902

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@1902

commit: 068a5c0

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right change for the right problem: the approved white-label scope (#639/#705) explicitly covered the pre-auth surfaces, and wiring admin.logo/siteName through a synchronous, server-side prop chain mirrors the existing authProviders pattern and avoids the logo flash that an async fetchManifest() would introduce. The implementation is backward compatible and localized correctly (no new user-facing strings).

I checked the changed Astro route, the new AdminBrandingProvider context, the prop threading through AdminWrapper/AdminApp, and the three page components that now consume it. The runtime logic is clean. My one material reservation is test coverage: only LoginPage.test.tsx received regression tests; SignupPage.test.tsx was not updated and there is no InviteAcceptPage.test.tsx at all, so the same bug could silently regress on the other two surfaces. Per the repo’s TDD-for-bugs convention, a bug without a reproducing test is not fixed.

Headline: merge after adding analogous branding tests for SignupPage and InviteAcceptPage.

{/* Header */}
<div className="text-center mb-8">
<LogoLockup className="h-10 mx-auto mb-2" />
<BrandLogo logoUrl={brandLogo} siteName={brandSiteName} className="h-10 mx-auto mb-2" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] SignupPage now consumes the new useAdminBranding() context and renders a configurable logo/site name, but packages/admin/tests/components/SignupPage.test.tsx was not updated. Existing tests render SignupPage without a provider and do not assert either the custom-branding or fallback behavior, so the fix here is unverified. Add tests analogous to the new LoginPage regression tests:

it("renders the configured admin logo/site name instead of the stock mark", async () => {
  const screen = await render(
    <AdminBrandingProvider
      adminBranding={{ logo: "https://example.com/logo.png", siteName: "Acme CMS" }}
    >
      <SignupPage />
    </AdminBrandingProvider>,
  );
  const logoImg = screen.getByRole("img", { name: "Acme CMS" });
  await expect.element(logoImg).toBeInTheDocument();
  expect(logoImg.element().getAttribute("src")).toBe("https://example.com/logo.png");
  expect(screen.getByRole("img", { name: "EmDash" }).query()).toBeNull();
});

it("falls back to the stock EmDash mark when no admin branding is configured", async () => {
  const screen = await render(<SignupPage />);
  await expect.element(screen.getByRole("img", { name: "EmDash" })).toBeInTheDocument();
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added — new renders the configured admin logo/site name instead of the stock mark + falls back to the stock EmDash mark tests in SignupPage.test.tsx, matching the LoginPage pattern. 22/22 tests pass.

<div className="w-full max-w-md">
<div className="text-center mb-8">
<LogoLockup className="h-10 mx-auto mb-2" />
<BrandLogo logoUrl={brandLogo} siteName={brandSiteName} className="h-10 mx-auto mb-2" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] InviteAcceptPage now consumes the new branding context and renders BrandLogo, but there is no test file for this component (packages/admin/tests/components/InviteAcceptPage.test.tsx does not exist). The invite-accept surface is part of the stated bug fix, so it also needs a regression test that verifies both the configured-logo path and the fallback to the EmDash lockup. Create InviteAcceptPage.test.tsx with at least those two assertions, using the same AdminBrandingProvider pattern as the new LoginPage tests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added InviteAcceptPage.test.tsx with both requested assertions (configured-logo path via the register step, and fallback to the stock EmDash mark).

interface AdminWrapperProps {
locale: string;
messages: Messages;
/** Configured admin white-label branding (logo, site name), read server-side from `admin.astro`. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] The adminBranding prop type is duplicated inline here ({ logo?: string; siteName?: string }). Since AdminBranding is now defined in @emdash-cms/admin, consider exporting that type from the admin package and importing it here to avoid drift if more branding fields are added later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — exported AdminBranding from @emdash-cms/admin and imported it here instead of the inline duplicate.

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Jul 10, 2026
…minBranding type

Adds the SignupPage and InviteAcceptPage branding regression tests
requested by review on PR emdash-cms#1902 (mirrors the existing LoginPage tests:
configured logo/siteName renders, falls back to stock EmDash mark when
unconfigured). Also exports `AdminBranding` from @emdash-cms/admin and
uses it in PluginRegistry's AdminWrapperProps instead of a duplicated
inline type, per the review suggestion.
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review size/L and removed review/awaiting-author Reviewed; waiting on the author to respond size/M labels Jul 10, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Jul 10, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right change for the right problem: the approved white-label scope (#639/#705) explicitly covered the pre-auth login/onboarding surfaces, and wiring admin.logo/siteName through a synchronous, server-side prop chain mirrors the existing authProviders pattern and avoids a logo flash.

I re-checked the full diff and my previous reservation has been addressed:

  • SignupPage.test.tsx now has branding regression tests (configured logo + fallback to stock mark).
  • A new InviteAcceptPage.test.tsx was added with the same two regression tests.
  • LoginPage.test.tsx retains its previously added branding regression tests.

Implementation remains clean:

  • AdminBrandingProvider is a thin context with a sensible {} default, so unconfigured installs fall back to the stock EmDash mark exactly as before.
  • admin.astro reads Astro.locals.emdash.config.admin server-side and passes { logo, siteName } down through AdminWrapperAdminApp, matching the authProviders prop chain.
  • LoginPage, SignupPage, and InviteAcceptPage now consume useAdminBranding() and pass the values to the existing BrandLogo component, which already had the right fallback logic.
  • No new user-facing strings are added, so Lingui wrapping stays complete.
  • Changeset correctly describes the user-facing fix and applies patch to both affected packages (emdash and @emdash-cms/admin).

No blocking issues found. LGTM.

@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-rereview Author pushed changes since the last review labels Jul 11, 2026
@cyface

cyface commented Jul 13, 2026

Copy link
Copy Markdown

Tested #1902's fix shape against our production config (the site from my comment on #1493) — it holds: the login page renders our configured branding.

Method, for transparency: I didn't build the fork directly. I verified the branch is exactly upstream main + the two PR commits, then applied that same diff onto our installed emdash/@emdash-cms/admin 0.27.0 (admin.astro, PluginRegistry, and the equivalent context/prop changes in the admin bundle) in an isolated copy of our site, with our real config: admin: { siteName: "Seasoned Reviewer", logo: "/favicon.svg", favicon: "/favicon.svg" }.

Results:

  • Before: /_emdash/admin/login accessibility tree shows image "EmDash" — the stock lockup.
  • After: image "Seasoned Reviewer" with src="/favicon.svg" — logo from config, accessible name = siteName, matching this PR's regression tests.
  • adminBranding is present in the server-rendered astro-island props ({"logo":"/favicon.svg","siteName":"Seasoned Reviewer"}), so there's no logo flash and no pre-auth fetch — the manifest-allowlist trap I flagged on RFC: First-class white-labeling for the EmDash admin & login (completing #639) #1493 is fully avoided by this shape.
  • The authenticated admin is unaffected: sidebar/setup branding still correct, dashboard renders normally, no new console errors.
  • One confirmed-out-of-scope observation: the first-login WelcomeModal still shows the stock EmDash mark and "Welcome to EmDash" copy (RFC RFC: First-class white-labeling for the EmDash admin & login (completing #639) #1493 §2.2(G)) — consistent with this PR's stated Step-0 scope; just noting it for the follow-up PR.

Signup and invite-accept couldn't be exercised live on our instance (public signup disabled; invite requires a valid token), but they consume the identical shared context.

From an integrator's perspective this fixes exactly what we hit in production. 👍

(Verified with AI assistance — Claude Code / Claude Fable 5; the backport and browser verification were run by the session and reviewed by @cyface.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/admin area/core bot:review Trigger an emdashbot code review on this PR review/approved Approved; no new commits since size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants