Skip to content

Consolidate the wp-build dashboard chassis across modernized products (PHP + SCSS + React shell) #48828

@keoshi

Description

@keoshi

Background

Three product-modernization umbrellas now scaffold a wp-build dashboard on top of @wordpress/admin-ui Page + Tabs from @wordpress/ui:

Each one ships near-identical glue code:

  1. PHP chassis helpersis_modernized(), is_<product>_admin_request(), load_wp_build() (require build/build.php + register WP_Build_Polyfills), alias_screen_id_for_wp_build(), plus the admin_menu priority-1 hook that swaps the menu callback. ~80 lines per product, copy-pasted with the slug/filter name swapped.
  2. Page-shell SCSS — sticky tab row, body-slot scroll model, header border override, content padding. Identical between boost-page.scss and social-page.scss aside from the body-class selector and inset token.
  3. <ProductPage> React shellPage + Tabs.Root + JetpackFooter, with the product name, subtitles, and class prefix being the only product-specific bits.
  4. Route boilerplateroutes/dashboard/route.tsx is literally export const route = {}; in every product; routes/dashboard/package.json is the same dependency set with the page slug swapped.

Proposed extractions

automattic/jetpack-wp-build-chassis (new package, PHP)

\\Automattic\\Jetpack\\WP_Build_Chassis\\Setup::register( array(
    'filter'     => 'rsm_jetpack_ui_modernization_social',
    'menu_slug'  => 'jetpack-social',
    'page_slug'  => 'jetpack-social-dashboard',
    'handle'     => 'jetpack-social',
    'build_dir'  => __DIR__ . '/../build',
) );

Encapsulates the priority-1 hook, filter check, polyfill registration, screen-ID alias, and exposes the menu-callback name for the consumer to branch on. Standardizes the integration so polyfill registration can't be forgotten on a future product.

Shared page-shell CSS

Two options:

  • Inside @wordpress/admin-ui — the Page component grows a tabbedShell / appShell prop that paints the scroll model directly. Right long-term home, requires upstream review.
  • @automattic/jetpack-base-styles — ship a SCSS partial (e.g. _product-page-shell.scss) scoped to a wrapper class the Stage applies. Lighter lift, but the dedicated upstream home is cleaner.

<JetpackProductPage> React component

Lives in @automattic/jetpack-components. Props:

type Props = {
    productName: string;            // \"Boost\", \"Social\", \"Newsletter\"
    subtitles: Record< string, () => string >;
    activeTab: string;
    tabs: Array< { value: string; label: string } >;
    actions?: ReactNode;
    children: ReactNode;
};

Encapsulates the Page + Tabs.Root + JetpackFooter + the URL ?tab= ↔ tab-value binding. Each consumer reduces from ~90 lines to ~30.

Route-boilerplate codegen

routes/dashboard/route.tsx and routes/dashboard/package.json could be synthesized by wp-build from a single config block in the package's top-level package.json — out of our control here, but worth raising with the wp-build team.

Why not now

  • The umbrellas explicitly mirror Boost and VideoPress cadence. Diverging mid-flight means re-architecting un-merged work in three places at once.
  • The right moment is once 3 products have shipped through the pattern — after Social PR 5 and Newsletter completion, we'll have proven the shape and the abstraction won't be premature.
  • Cross-cutting refactors right now would block five PRs (Social PRs 2–5 + Boost PR 5) on review.

Suggested trigger

Open this after all four of the following have landed in trunk:

  • Boost umbrella (#48717) — including PR 5 (legacy retire).
  • Newsletter umbrella (#48530) — including the final flip.
  • VideoPress scaffold flow (#48494) — including any tab work that comes after.
  • Social umbrella (#48824) — including PR 5.

At that point we have 4 in-tree consumers and the abstraction has clear contours.

References

  • Boost PR 1 chassis PHP: projects/plugins/boost/app/admin/class-admin.php on the update/boost-modernization-scaffold branch.
  • Boost PR 1 page shell: projects/plugins/boost/_inc/components/boost-page.{tsx,scss}.
  • Social PR 1 (#48826) — equivalent files under projects/packages/publicize/.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions