Design system

Publish dialog

Picks where a landing page goes live, shows what it goes live with, and takes a live page offline.

import { PublishDialog } from "@/pages/library/PageSettings";
import "@/pages/library/library.css";

PublishDialog is the short dialog behind the page builder's Publish button. It asks only where the page goes: a domain and an address. Confirming it is the approval, and the page goes live straight away. Everything else about the page lives in Page settings, and this dialog shows a summary of those settings so nothing is published blind.

The two dialogs are kept apart on purpose. Publishing is a decision people make often and quickly. Page settings change rarely and have many fields.

A draft page

The first publish. The address starts from the page's name: "Q4 demo request page" becomes q4-demo-request, through slugFor in src/mocks/pageBuilder.js. The full address is shown under the field as it is typed.

{publishing && (
  <PublishDialog
    page={page}
    ownDomain="go.meridian.com"
    onClose={() => setPublishing(false)}
    onSettings={() => { setPublishing(false); setSettingsOpen(true); }}
    onUnpublish={() => { unpublish(page.id); setPublishing(false); }}
    onPublish={({ domain, slug }) => { publish(page.id, { domain, slug }); setPublishing(false); }}
  />
)}

The address field cleans what is typed as it goes: lowercase letters, digits and single hyphens only, with no hyphen at the start (cleanSlug). A trailing hyphen is removed when the page is published.

A live page with changes

Once a page is live, later edits are held back until it is published again (page.unpublished). The title becomes "Publishing", the confirm label "Publish changes", and a "Take offline" group appears with an Unpublish button.

<PublishDialog page={{ ...page, status: "published", unpublished: true }} … />

A live page with no changes

With nothing waiting, the only thing confirming can change is the address, so the confirm label is "Update address". The domain and address start from where the page is live now.

<PublishDialog page={{ ...page, status: "published", unpublished: false, settings: { domain: "go.meridian.com", slug: "q4-demo" } }} … />

States

PageTitleConfirm labelLast group
status: "draft"Publish pagePublish pageA note: "The page goes live at this address as soon as you publish."
status: "published", unpublished: truePublishingPublish changesTake offline, with Unpublish
status: "published", unpublished: falsePublishingUpdate addressTake offline, with Unpublish

If the address is cleared, the field shows "Give the page an address." and confirming does nothing.

What "Goes live with" shows

The summary reads the page's settings through metaOf(page), which fills in the defaults.

RowFromDefault
Search titlemeta.title, or the page nameThe page name
Accessmeta.access"Anyone on the internet"
Search enginesmeta.sitemap"Can list this page"
Tracking pixelmeta.pixel"On"

"Change in page settings" calls onSettings. The page builder closes this dialog and opens Page settings.

Layout

PartStyle
PanelDialog at md, 650px, with .lib-dialog--publish: the body scrolls past min(64vh, 620px).
Group.lib-publish__group: a 12px uppercase heading (.lib-publish__heading, semibold, 0.05em tracking), 20px padding above and below, a --color-grey-100 line between groups.
Address preview.lib-publish__url: 12px monospace with a 14px Globe icon.
Summary.lib-publish__summary: a bordered list with 8px corners, 12px text, labels in --color-grey-500, values in medium weight.
Note.lib-publish__note: 12px text on --color-grey-50, 8px corners, 10px by 12px padding.
FooterCancel and the confirm action at 32px, as in all library dialogs.

Props

PropTypeDefaultNotes
pagepage objectRequired. Reads name, status, unpublished, settings ({ domain, slug }) and meta.
ownDomainstringRequired. The workspace's connected domain, offered as the second radio option. The page builder makes it from the brand: go.meridian.com.
onPublish({ domain, slug }) => voidCalled on confirm when the address is not empty. Does not close the dialog.
onUnpublish() => voidThe Unpublish button on a live page.
onSettings() => void"Change in page settings".
onClose() => voidEscape, scrim click, the close icon and Cancel.

The domain options are pages.petavue.com (exported as PETAVUE_DOMAIN) and ownDomain.

Accessibility

  • The wrapper has role="dialog", aria-modal="true" and aria-label "Publish page" or "Publishing", matching the title.
  • The domain choice is a RadioGroup, a fieldset with a "Domain" legend, so arrow keys move between the two domains.
  • The address error is announced with role="alert" and linked to the field.
  • Nothing is focused when the dialog opens, focus is not kept inside, and it is not returned to the Publish button on close.

Do and don't

  • Do keep this dialog to where the page goes. Add new page options to Page settings, and a row to "Goes live with" if people should see it before publishing.
  • Do say in the toast where the page went: "Published at go.meridian.com/q4-demo."
  • Don't ask for confirmation again after this dialog. Confirming it is the approval.
  • Don't hide Unpublish behind a menu. Taking a page offline should be as easy to find as publishing it.

When to use it

Use it for landing pages built in the page builder. Publishing a set of changes from a workflow or a recommendation is a different decision; use a Dialog from @/ui that lists the changes, as in Dialogs and confirmation.

Where it is used

FileWhat
src/pages/library/PageBuilderPage.jsxOpened by Publish, Publish changes and Publishing in the editor header. After a publish it shows a toast and, for a page drafted for a recommendation, marks the recommendation accepted.

On this page