From 0f90fda9cc8ec609ad7e2880910ca3faef42dc59 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 16 Feb 2026 21:06:33 +0100 Subject: [PATCH] remove planning docs Signed-off-by: Patrik Oldsberg --- header-architecture-summary.md | 419 ------------------------ header.md | 43 --- subpage-implementation-summary.md | 270 --------------- swappable-component-refactor-summary.md | 337 ------------------- 4 files changed, 1069 deletions(-) delete mode 100644 header-architecture-summary.md delete mode 100644 header.md delete mode 100644 subpage-implementation-summary.md delete mode 100644 swappable-component-refactor-summary.md diff --git a/header-architecture-summary.md b/header-architecture-summary.md deleted file mode 100644 index 30173f353a..0000000000 --- a/header-architecture-summary.md +++ /dev/null @@ -1,419 +0,0 @@ -# Page Header Architecture - Implementation Summary - -## Overview - -This branch implements a new page header architecture for Backstage's New Frontend System (NFS) that provides: - -- Consistent headers across all plugin pages -- Support for sub-pages with tab navigation -- Header actions (buttons/controls in the header) -- Plugin-level display metadata (titles and icons) - -## Architectural Changes - -### 1. New Blueprint: `HeaderActionBlueprint` - -**Location:** `packages/frontend-plugin-api/src/blueprints/HeaderActionBlueprint.tsx` - -A new extension blueprint that allows plugins to register actions (buttons, controls) that appear in the page header. - -**Key Features:** - -- Attaches to `app/routes` with input `headerActions` -- Outputs `coreExtensionData.reactElement` -- Uses lazy loading with `ExtensionBoundary` - -**Usage Example** (from api-docs plugin): - -```typescript -HeaderActionBlueprint.make({ - name: 'register-api', - params: { - loader: async () => - compatWrapper( - Register Existing API, - ), - }, -}); -``` - -### 2. Enhanced `PageBlueprint` - -**Location:** `packages/frontend-plugin-api/src/blueprints/PageBlueprint.tsx` - -The `PageBlueprint` has been significantly enhanced to support sub-pages and tabbed navigation. - -**New Features:** - -#### Inputs - -- New `pages` input that accepts sub-pages with: - - `routePath` - - `routeRef` (optional) - - `reactElement` - - `title` (optional) - -#### Outputs - -- Added `title` output (optional) - -#### Configuration - -- New `title` config schema property - -#### Parameters - -- `title?: string` - Page title displayed in header -- `loader` is now optional (not required when using sub-pages) -- `routeRef` now accepts both `RouteRef` and `SubRouteRef` - -#### Behavior Changes - -1. **With loader (traditional page):** Renders Header + page content -2. **Without loader (parent page with tabs):** Renders Header with tabs populated from `inputs.pages` - -### 3. Central Routing Changes: `AppRoutes` - -**Location:** `plugins/app/src/extensions/AppRoutes.tsx` - -The core routing extension has been substantially refactored to orchestrate the new header architecture. - -**Key Implementation Details:** - -#### Header Action Aggregation - -```typescript -const headerActionsByPluginId = new Map>(); -``` - -- Collects all header actions from inputs -- Groups them by plugin ID -- Passes them to the Header component via `customActions` prop - -#### Page Aggregation by Path - -```typescript -const pagesByPath = new Map< - string, - Array<{ element: JSX.Element; title: string; node: AppNode }> ->(); -``` - -- Groups pages by their route path -- Enables detection of pages with sub-pages -- Stores title and node reference for each page - -#### Conditional Rendering Logic - -1. **Multiple pages at same path (tabbed navigation):** - - - Renders `Header` with tabs - - Uses nested `` for tab content - - Tab matching uses prefix strategy - -2. **Single page (traditional):** - - Renders `Header` with plugin title - - Renders page element directly - -### 4. Plugin Display Metadata - -**Location:** `plugins/catalog/src/alpha/plugin.tsx` - -Plugins can now define display metadata: - -```typescript -export default createFrontendPlugin({ - pluginId: 'catalog', - info: { - packageJson: () => import('../../package.json'), - }, - display: { - icon: 'catalog', - title: 'Software Catalog', - }, - // ... -}); -``` - -### 5. Header Component Styling Updates - -**Location:** `packages/ui/src/components/Header/Header.module.css` - -- Commented out default `margin-bottom` on `.bui-HeaderToolbar` -- Allows tighter integration between header and content -- Maintains conditional margin for tabbed headers - -## Implementation Examples - -### Example 1: App Visualizer (Multi-page with Tabs) - -**Location:** `plugins/app-visualizer/src/plugin.tsx` - -Demonstrates the sub-page pattern: - -1. **Parent page** (no loader): - -```typescript -const appVisualizerPage = PageBlueprint.make({ - params: { - path: '/visualizer', - routeRef: rootRouteRef, - title: 'Visualizer', - // No loader - will show tabs - }, -}); -``` - -2. **Sub-pages** (using `SubPageBlueprint` - not yet fully implemented): - -```typescript -const appVisualizerTreePage = SubPageBlueprint.make({ - attachTo: { id: 'page:app-visualizer', input: 'pages' }, - name: 'tree', - params: { - path: '/tree', - title: 'Tree', - loader: () => import('./components/...'), - }, -}); -``` - -Multiple sub-pages are defined: - -- Tree view (`/tree`) -- Detailed view (`/details`) -- Text view (`/text`) - -### Example 2: API Docs (Header Actions) - -**Location:** `plugins/api-docs/src/alpha.tsx` - -Demonstrates header actions: - -```typescript -HeaderActionBlueprint.make({ - name: 'register-api', - params: { - loader: async () => - compatWrapper( - Register Existing API, - ), - }, -}); -``` - -## Data Flow - -``` -┌─────────────────────────────────────────────────────────────┐ -│ AppRoutes │ -│ (Central orchestrator) │ -└─────────────────────────────────────────────────────────────┘ - │ - ┌─────────────┴─────────────┐ - │ │ - ▼ ▼ - ┌───────────────────┐ ┌────────────────────┐ - │ headerActions │ │ routes │ - │ input │ │ input │ - └───────────────────┘ └────────────────────┘ - │ │ - │ │ - ▼ ▼ - Group by plugin ID Group by route path - │ │ - │ │ - └─────────────┬─────────────┘ - │ - ▼ - ┌─────────────────┐ - │ Route Renderer │ - └─────────────────┘ - │ - ┌─────────────┴──────────────┐ - │ │ - ▼ ▼ - ┌──────────────────┐ ┌──────────────────────┐ - │ Single Page │ │ Multi-page (Tabs) │ - │ │ │ │ - │ Header + │ │ Header (w/ tabs) + │ - │ Page Element │ │ Nested Routes │ - └──────────────────┘ └──────────────────────┘ -``` - -## Extension Data Types Used - -The implementation relies on these core extension data types: - -- `coreExtensionData.routePath` - Route paths for pages -- `coreExtensionData.routeRef` - Route references for navigation -- `coreExtensionData.reactElement` - React components to render -- `coreExtensionData.title` - Page/sub-page titles - -## Technical Considerations - -### 1. Plugin-Relative Attachment Points - -The implementation uses string-based attachment points: - -```typescript -attachTo: { id: 'page:app-visualizer', input: 'pages' } -``` - -**Future consideration:** TypeScript-based attachment points for type safety: - -```typescript -attachTo: appVisualizerPage.inputs.pages; -``` - -### 2. SubPageBlueprint - -The code references `SubPageBlueprint` which is not yet fully implemented in the blueprint exports. This suggests it's either: - -- Still being developed -- Implemented locally for testing -- Planned for future implementation - -### 3. Header Component Integration - -The `Header` component from `@backstage/ui` is used directly in: - -- `PageBlueprint` rendering -- `AppRoutes` conditional rendering - -This component accepts: - -- `title` - Page title -- `tabs` - Array of tab configurations -- `customActions` - Array of React nodes for header actions - -### 4. Route Matching Strategy - -Sub-pages use different route matching: - -- Parent pages: Match with trailing `/*` for catch-all -- Tab content: Direct path matching -- Tab component: Uses `matchStrategy: 'prefix'` - -## Outstanding Questions & TODOs - -From `header.md`: - -### Decisions Needed - -- [ ] Should we add titles to all plugins? -- [ ] Should we add icons to all plugins? -- [ ] Is `HeaderActionBlueprint` too specific? (Note: "a bit wonky") -- [ ] PageBlueprint additions or ContentBlueprint? - -### Implementation Items - -- [ ] Ship the Figma design -- [ ] TopBarActionBlueprint? (alternative naming consideration) -- [ ] Add `icon` and `title` to plugin info (partially done) -- [ ] Add support for plugin-relative attachment points -- [ ] Consider TypeScript-based attachment points -- [ ] Add `coreExtensionData.navTarget` -- [ ] Add swappable component for PageBlueprint React element - -## Breaking Changes - -This implementation introduces breaking changes: - -1. **PageBlueprint API changes:** - - - New optional `title` parameter - - `loader` is now optional - - `routeRef` accepts `SubRouteRef` in addition to `RouteRef` - -2. **AppRoutes behavior:** - - - Automatically wraps all pages with Header component - - Changes route structure for multi-page plugins - -3. **Header styling:** - - Removes default bottom margin on headers - -## Migration Path - -For existing plugins to adopt the new header architecture: - -1. **Add display metadata to plugin:** - -```typescript -display: { - icon: 'plugin-icon', - title: 'Plugin Name', -} -``` - -2. **Add titles to pages:** - -```typescript -PageBlueprint.make({ - params: { - title: 'Page Title', - // ... other params - }, -}); -``` - -3. **Optional - Add header actions:** - -```typescript -HeaderActionBlueprint.make({ - params: { - loader: () => , - }, -}); -``` - -4. **Optional - Add sub-pages for tabbed navigation:** - Create multiple pages at the same route path with different titles. - -## Files Changed Summary - -| File | Lines Changed | Type of Change | -| ------------------------------------------ | ------------- | ---------------------- | -| `header.md` | +43 | Documentation | -| `HeaderActionBlueprint.tsx` | +35 | New Blueprint | -| `PageBlueprint.tsx` | +52/-12 | Enhancement | -| `blueprints/index.ts` | +1 | Export | -| `Header.module.css` | +1/-1 | Styling | -| `plugins/api-docs/src/alpha.tsx` | +13/-1 | Example Implementation | -| `plugins/app-visualizer/src/plugin.tsx` | +120/-1 | Example Implementation | -| `plugins/app/package.json` | +1 | Dependency | -| `plugins/app/src/extensions/AppRoutes.tsx` | +77/-2 | Core Logic | -| `plugins/catalog/src/alpha/plugin.tsx` | +9/-1 | Display Metadata | -| `yarn.lock` | +1 | Dependency | - -**Total: 11 files, ~354 insertions, ~20 deletions** - -## Next Steps - -1. **Complete SubPageBlueprint implementation** - Currently referenced but not fully exported -2. **Decide on naming** - HeaderActionBlueprint vs TopBarActionBlueprint -3. **Type-safe attachment points** - Consider TypeScript-based approach -4. **Migration guide** - Document for plugin authors -5. **Testing** - Comprehensive tests for new routing logic -6. **Figma alignment** - Ensure implementation matches design specs -7. **Plugin adoption** - Roll out to all core plugins -8. **API documentation** - Update API docs for new blueprints - -## Architecture Benefits - -1. **Consistency:** All pages have consistent header treatment -2. **Flexibility:** Supports both simple pages and complex tabbed interfaces -3. **Extensibility:** Header actions allow plugins to add controls -4. **Navigation:** Two-level navigation (page + sub-pages) is now native -5. **Declarative:** Uses extension system patterns for discoverability -6. **Centralized:** AppRoutes orchestrates header rendering for consistency - -## Potential Concerns - -1. **Complexity:** AppRoutes has increased complexity with conditional rendering -2. **Performance:** Multiple map operations on every render -3. **Breaking:** Changes existing PageBlueprint behavior -4. **Incomplete:** SubPageBlueprint appears incomplete -5. **Debugging:** Console.log statements present (need cleanup) -6. **Migration:** All existing plugins will need updates for full adoption diff --git a/header.md b/header.md deleted file mode 100644 index d3cf8fc4cd..0000000000 --- a/header.md +++ /dev/null @@ -1,43 +0,0 @@ -## TODO - -- [ ] Shipping https://www.figma.com/design/zJxHainw6yO7L9oEsStAh7/Header?node- [ ]id=103- [ ]5709&t=HNkpc2MqdUbaN8Jz- [ ]1 -- [ ] TopBarActionBlueprint? -- [ ] PageBlueprint additions or ContentBlueprint? -- [ ] Should we add titles to all plugins? -- [ ] Should we add icons to all plugins? - -- [ ] Add `icon` and `title` to the plugin info - -## Notes - -- HeaderActionBlueprint is a bit wonky, too specific? - -## Requirements - -- Consistency of headers across all plugins in NFS -- Possibility to put breadcrumbs in the top bar -- Two levels of navigation for plugins - page + sub pages - -## Technical Requirements - -- Sub pages must be represented in some form as extensions -- Sub pages must have a title and path relative to the parent page -- Plugins must have titles and icons -- Sub pages must be attachments of the parent page - -## Potential Solutions - -### Make all(\*) pages navigable, get rid of nav items - -## Implementation Plan - -- Add support for plugin-relative attachment points, i.e. `attachTo: { id: 'page:{pluginId}', input: 'pages' }` - - Alternative: typescript-based attachment points, i.e. `attachTo: appVisualizerPage` or `attachTo: appVisualizerPage.inputs.pages` -- Add a `SubPageBlueprint` that attaches to the `pages` of `PageBlueprint`, using plugin-relative attachment -- Add a swappable component for the `PageBlueprint` React element -- Add `title` param and output to `PageBlueprint` - -To consider: - -- Add `display` options for plugins -- Add `coreExtensionData.navTarget`, either marker or with actual data diff --git a/subpage-implementation-summary.md b/subpage-implementation-summary.md deleted file mode 100644 index 496dfd8fe4..0000000000 --- a/subpage-implementation-summary.md +++ /dev/null @@ -1,270 +0,0 @@ -# SubPageBlueprint Implementation Summary - -## What Was Implemented - -I've successfully implemented the `SubPageBlueprint` pattern for the header architecture, addressing one of the critical gaps identified in the RFC alignment analysis. - -## Files Created/Modified - -### New Files - -1. **`packages/frontend-plugin-api/src/blueprints/SubPageBlueprint.tsx`** - - New blueprint for creating sub-pages that attach to parent pages - - Outputs: `routePath`, `reactElement`, `title` - - Supports lazy loading of sub-page content - - Requires users to specify `attachTo` to target their parent page - -### Modified Files - -2. **`packages/frontend-plugin-api/src/blueprints/index.ts`** - - - Exported `SubPageBlueprint` for public use - -3. **`packages/frontend-plugin-api/src/blueprints/PageBlueprint.tsx`** - - - Enhanced to handle sub-pages via `inputs.pages` - - Three rendering modes: - - **With loader**: Renders Header + lazy-loaded content - - **With sub-pages**: Renders Header with tabs + nested Routes for sub-pages - - **Empty**: Renders just Header - - Removed duplicate header rendering issues - -4. **`plugins/app/src/extensions/AppRoutes.tsx`** - - - Simplified to focus on top-level routing only - - Removed duplicate header orchestration logic - - Removed debug console.log statements - - Sub-page routing is now handled by PageBlueprint - -5. **`plugins/app-visualizer/src/plugin.tsx`** - - - Added import for `SubPageBlueprint` - - Cleaned up unused `SubRouteRef` declarations - - Now properly uses SubPageBlueprint pattern - -6. **`plugins/api-docs/src/alpha.tsx`** - - - Removed `HeaderActionBlueprint` usage (feature temporarily removed) - - Cleaned up unused imports - -7. **`plugins/catalog/src/alpha/plugin.tsx`** - - Removed `display` property (not yet supported in plugin options) - -## Architecture Overview - -### SubPageBlueprint API - -```typescript -const mySubPage = SubPageBlueprint.make({ - attachTo: { id: 'page:my-plugin', input: 'pages' }, - name: 'overview', - params: { - path: '/overview', // Relative path from parent - title: 'Overview', // Tab title - loader: () => import('./components/Overview').then(m => ), - }, -}); -``` - -### How It Works - -1. **Parent Page Declaration** (without loader, with sub-pages): - - ```typescript - const parentPage = PageBlueprint.make({ - params: { - path: '/visualizer', - routeRef: rootRouteRef, - title: 'Visualizer', - // No loader - will show tabs - }, - }); - ``` - -2. **Sub-Page Declarations**: - - ```typescript - const treePage = SubPageBlueprint.make({ - attachTo: { id: 'page:app-visualizer', input: 'pages' }, - name: 'tree', - params: { - path: '/tree', - title: 'Tree', - loader: () => import('./TreeView'), - }, - }); - ``` - -3. **Rendering Flow**: - ``` - AppRoutes - └─> PageBlueprint (detects sub-pages via inputs.pages) - ├─> Header (with tabs) - └─> Routes - ├─> /tree -> TreePage content - ├─> /details -> DetailsPage content - └─> /text -> TextPage content - ``` - -## Key Design Decisions - -### 1. **Simplified AppRoutes** - -- Removed header orchestration from AppRoutes -- AppRoutes now only handles top-level routing -- PageBlueprint is responsible for its own header rendering - -### 2. **No Header Duplication** - -- Pages with loaders: Header rendered by PageBlueprint -- Pages with sub-pages: Header with tabs rendered by PageBlueprint -- No duplicate headers between AppRoutes and PageBlueprint - -### 3. **Plugin-Relative Attachment** - -- Sub-pages attach using: `{ id: 'page:plugin-id', input: 'pages' }` -- Follows the pattern described in header.md and the RFC - -### 4. **Lazy Loading** - -- All sub-pages use lazy loading via `ExtensionBoundary.lazy()` -- Improves initial page load performance - -### 5. **Removed Features (Temporarily)** - -- **HeaderActionBlueprint**: Removed from AppRoutes orchestration (needs redesign) -- **Plugin display metadata**: Removed (not yet supported in plugin options type) -- **SubRouteRef support**: Simplified to only support RouteRef - -## Example Usage: App Visualizer - -The app-visualizer plugin demonstrates the complete pattern: - -```typescript -// Parent page without loader -const appVisualizerPage = PageBlueprint.make({ - params: { - path: '/visualizer', - routeRef: rootRouteRef, - title: 'Visualizer', - }, -}); - -// Three sub-pages attached to parent -const appVisualizerTreePage = SubPageBlueprint.make({ - attachTo: { id: 'page:app-visualizer', input: 'pages' }, - name: 'tree', - params: { - path: '/tree', - title: 'Tree', - loader: () => - import('./components/AppVisualizerPage/TreeVisualizer').then(m => { - const Component = () => { - const appTreeApi = useApi(appTreeApiRef); - const { tree } = appTreeApi.getTree(); - return ; - }; - return ; - }), - }, -}); - -// Similar for details and text pages... -``` - -## Technical Benefits - -1. **Declarative**: Sub-pages are extensions that can be discovered and manipulated -2. **Type-Safe**: Full TypeScript support with proper type checking -3. **Consistent**: All pages get headers automatically -4. **Flexible**: Supports both simple pages and complex tabbed interfaces -5. **Performant**: Lazy loading of sub-page content - -## What's Still Missing - -1. **HeaderActionBlueprint Integration**: Needs to be redesigned to work without AppRoutes orchestration -2. **Plugin Display Metadata**: `display: { icon, title }` type support in plugin options -3. **SubRouteRef Support**: Currently only RouteRef is supported for routing -4. **Sidebar Navigation**: The RFC's primary goal for Portal (sidebar navigation API) -5. **Breadcrumbs**: Mentioned in RFC but not implemented - -## Testing Recommendations - -1. **Test sub-page navigation**: Click through tabs in app-visualizer -2. **Test lazy loading**: Verify sub-pages load on demand -3. **Test routing**: Verify URLs update correctly when switching tabs -4. **Test nested routes**: Verify sub-pages render at correct paths -5. **Test simple pages**: Verify pages with loaders still work - -## Migration Path for Other Plugins - -To add sub-pages to an existing plugin: - -1. **Update parent page** to remove loader: - - ```typescript - const myPage = PageBlueprint.make({ - params: { - path: '/my-plugin', - title: 'My Plugin', - // Remove loader to enable sub-pages - }, - }); - ``` - -2. **Create sub-pages**: - - ```typescript - const mySubPage = SubPageBlueprint.make({ - attachTo: { id: 'page:my-plugin', input: 'pages' }, - name: 'overview', - params: { - path: '/overview', - title: 'Overview', - loader: () => import('./Overview'), - }, - }); - ``` - -3. **Add to plugin extensions**: - ```typescript - export default createFrontendPlugin({ - extensions: [ - myPage, - mySubPage, - // ... other sub-pages - ], - }); - ``` - -## Type Safety - -All type errors have been resolved: - -- ✅ No unused imports -- ✅ Proper type checking for all parameters -- ✅ Correct extension data types -- ✅ Valid blueprint definitions - -## Next Steps - -1. **Add tests** for SubPageBlueprint and updated PageBlueprint -2. **Implement HeaderActionBlueprint redesign** (perhaps as page-level extensions) -3. **Add plugin display metadata support** in plugin options types -4. **Implement sidebar navigation API** (RFC primary goal) -5. **Add breadcrumb support** for navigation hierarchy -6. **Document the pattern** for community plugin authors -7. **Update existing plugins** to use the new pattern - -## Conclusion - -The SubPageBlueprint implementation successfully delivers on the core technical requirements from the RFC: - -- ✅ Sub-pages are represented as extensions -- ✅ Sub-pages have titles and relative paths -- ✅ Sub-pages attach to parent pages -- ✅ Two levels of navigation (page + sub-pages) -- ✅ Consistent headers across plugins -- ✅ Plugin-relative attachment points - -The implementation is type-safe, follows Backstage patterns, and provides a clean API for plugin authors to create rich, multi-page experiences within their plugins. diff --git a/swappable-component-refactor-summary.md b/swappable-component-refactor-summary.md deleted file mode 100644 index 86393f97a4..0000000000 --- a/swappable-component-refactor-summary.md +++ /dev/null @@ -1,337 +0,0 @@ -# Swappable Component Architecture Refactor - -## Problem - -The initial implementation had `@backstage/frontend-plugin-api` directly importing `Header` from `@backstage/ui`, which violated the package dependency architecture: - -- `@backstage/frontend-plugin-api` is a low-level API package -- `@backstage/ui` is a higher-level UI component library -- API packages should not depend on UI libraries - -## Solution - -Implemented a **swappable component pattern** that: - -1. Provides a default implementation using plain HTML elements in `@backstage/frontend-plugin-api` -2. Ships the `@backstage/ui` Header implementation via the `@backstage/plugin-app` plugin -3. Allows apps to override the component with custom implementations - -This follows the same pattern used for other swappable components like `Progress`, `NotFoundErrorPage`, and `ErrorDisplay`. - -## Architecture - -### 1. Created PageWrapper Swappable Component - -**Location:** `packages/frontend-plugin-api/src/components/PageWrapper.tsx` - -```typescript -export interface PageWrapperProps { - title?: string; - tabs?: PageTab[]; - children?: ReactNode; -} - -// Default implementation using plain HTML -function DefaultPageWrapper(props: PageWrapperProps): JSX.Element { - // Uses plain
,
,