|
| 1 | +# Blueprint Buttons Configuration - Implementation Summary |
| 2 | + |
| 3 | +## Overview |
| 4 | +This document describes the implementation of dynamic blueprint buttons in the "Start a new Playground" overlay, which now reads from `/blueprints/blueprints.json` instead of using hardcoded values. |
| 5 | + |
| 6 | +## Changes Made |
| 7 | + |
| 8 | +### 1. TypeScript Type Definitions |
| 9 | +**File:** `packages/playground/website/src/lib/types/blueprints-config.ts` |
| 10 | + |
| 11 | +Defines the structure for the `blueprints.json` file: |
| 12 | + |
| 13 | +```typescript |
| 14 | +export interface BlueprintButton { |
| 15 | + id: string; // Unique identifier |
| 16 | + title: string; // Display title |
| 17 | + path: string; // Navigation path |
| 18 | + icon?: string; // Optional: React component name or SVG URL |
| 19 | + disabled?: boolean; // Optional: button disabled state |
| 20 | +} |
| 21 | + |
| 22 | +export type BlueprintsConfig = BlueprintButton[]; |
| 23 | +``` |
| 24 | + |
| 25 | +### 2. Component Updates |
| 26 | +**File:** `packages/playground/website/src/components/saved-playgrounds-overlay/index.tsx` |
| 27 | + |
| 28 | +Updated the `SavedPlaygroundsOverlay` component to: |
| 29 | +- Use the `useFetch` hook to load `/blueprints/blueprints.json` |
| 30 | +- Fall back to hardcoded defaults if the JSON file cannot be fetched |
| 31 | +- Transform the JSON data into button configurations with onClick handlers |
| 32 | +- Resolve icons dynamically from React component names or SVG URLs |
| 33 | + |
| 34 | +Key changes: |
| 35 | +```typescript |
| 36 | +// Helper function to resolve icon from string (component name or URL) |
| 37 | +function resolveIcon(iconSpec?: string): React.ReactNode { |
| 38 | + if (!iconSpec) { |
| 39 | + return <WordPressIcon />; |
| 40 | + } |
| 41 | + |
| 42 | + // Check if it's a URL (SVG from external source) |
| 43 | + if (iconSpec.startsWith('http://') || iconSpec.startsWith('https://')) { |
| 44 | + return <img src={iconSpec} alt="" style={{ width: '100%', height: '100%' }} />; |
| 45 | + } |
| 46 | + |
| 47 | + // Try to resolve as a React component from @wp-playground/components |
| 48 | + const IconComponent = (PlaygroundIcons as any)[iconSpec]; |
| 49 | + if (IconComponent && typeof IconComponent === 'function') { |
| 50 | + return <IconComponent />; |
| 51 | + } |
| 52 | + |
| 53 | + // Fallback to WordPressIcon if component not found |
| 54 | + return <WordPressIcon />; |
| 55 | +} |
| 56 | + |
| 57 | +// Fetch from JSON (direct array) |
| 58 | +const { data: blueprintsConfig } = useFetch<BlueprintsConfig>( |
| 59 | + '/blueprints/blueprints.json' |
| 60 | +); |
| 61 | + |
| 62 | +// Fallback to defaults |
| 63 | +const defaultCreationOptions: BlueprintButton[] = [...]; |
| 64 | +const buttonsConfig = blueprintsConfig || defaultCreationOptions; |
| 65 | + |
| 66 | +// Transform to button props with dynamic icon resolution |
| 67 | +const creationOptions = buttonsConfig.map((button) => ({ |
| 68 | + id: button.id, |
| 69 | + title: button.title, |
| 70 | + iconComponent: resolveIcon(button.icon), |
| 71 | + onClick: () => { window.location.href = button.path; }, |
| 72 | + disabled: button.disabled ?? false, |
| 73 | +})); |
| 74 | +``` |
| 75 | + |
| 76 | +### 3. Documentation |
| 77 | +**File:** `packages/playground/website/public/blueprints/README.md` |
| 78 | + |
| 79 | +Comprehensive documentation explaining: |
| 80 | +- The JSON structure and schema |
| 81 | +- Each property's purpose and type |
| 82 | +- Example configuration |
| 83 | +- Deployment process |
| 84 | +- Fallback behavior |
| 85 | + |
| 86 | +## JSON File Structure |
| 87 | + |
| 88 | +The CI process should deploy a `blueprints.json` file to `/blueprints/blueprints.json` as a direct array: |
| 89 | + |
| 90 | +```json |
| 91 | +[ |
| 92 | + { |
| 93 | + "id": "tinyrelated", |
| 94 | + "title": "tinyRelated", |
| 95 | + "path": "/tinyrelated", |
| 96 | + "disabled": false |
| 97 | + }, |
| 98 | + { |
| 99 | + "id": "tinyrating", |
| 100 | + "title": "tinyRating", |
| 101 | + "path": "/tinyrating", |
| 102 | + "disabled": false |
| 103 | + }, |
| 104 | + { |
| 105 | + "id": "tinyevent", |
| 106 | + "title": "tinyEvent", |
| 107 | + "path": "/tinyevent", |
| 108 | + "disabled": false |
| 109 | + } |
| 110 | +] |
| 111 | +``` |
| 112 | + |
| 113 | +## Behavior |
| 114 | + |
| 115 | +### Normal Operation |
| 116 | +1. When the overlay loads, it fetches `/blueprints/blueprints.json` |
| 117 | +2. If successful, buttons are created from the JSON data |
| 118 | +3. Icons are resolved dynamically: |
| 119 | + - If `icon` is omitted, uses `WordPressIcon` (default) |
| 120 | + - If `icon` starts with `http://` or `https://`, loads as external SVG image |
| 121 | + - If `icon` matches a component name in `@wp-playground/components`, uses that component |
| 122 | + - If none of the above, falls back to `WordPressIcon` |
| 123 | +4. Each button navigates to the specified `path` when clicked |
| 124 | + |
| 125 | +### Fallback Mechanism |
| 126 | +If the JSON file cannot be fetched (404, network error, etc.): |
| 127 | +- The component uses hardcoded default buttons (same as the original implementation) |
| 128 | +- This ensures the overlay always displays buttons, even if the JSON file is missing |
| 129 | + |
| 130 | +### Loading State |
| 131 | +- The component doesn't show a loading spinner |
| 132 | +- Buttons appear immediately using defaults or fetched data |
| 133 | +- This provides a seamless user experience |
| 134 | + |
| 135 | +### Icon Support |
| 136 | +Buttons can display custom icons in two ways: |
| 137 | +1. **React Component Names**: Use exported icon components from `@wp-playground/components` (e.g., "WordPressIcon", "ClockIcon", "playgroundLogo") |
| 138 | +2. **SVG URLs**: Provide a direct URL to an SVG file (e.g., "https://example.com/icon.svg") |
| 139 | + |
| 140 | +## Testing |
| 141 | + |
| 142 | +### Type Checking |
| 143 | +```bash |
| 144 | +npx nx typecheck playground-website |
| 145 | +``` |
| 146 | +✅ Passed |
| 147 | + |
| 148 | +### Linting |
| 149 | +```bash |
| 150 | +npx nx lint playground-website |
| 151 | +``` |
| 152 | +✅ Passed |
| 153 | + |
| 154 | +### Dev Server |
| 155 | +```bash |
| 156 | +npm run dev |
| 157 | +``` |
| 158 | +✅ Started successfully at http://127.0.0.1:5400/ |
| 159 | + |
| 160 | +## Deployment Notes |
| 161 | + |
| 162 | +1. **JSON File Location**: The `blueprints.json` file should be deployed to the public directory at `/blueprints/blueprints.json` |
| 163 | + |
| 164 | +2. **Git Ignore**: The `/blueprints/` directory is already in `.gitignore`, so the JSON file won't be committed to the repository |
| 165 | + |
| 166 | +3. **CI Process**: The CI process should deploy the JSON file separately from the application build |
| 167 | + |
| 168 | +4. **Validation**: Consider adding JSON schema validation in your CI process to ensure the file structure is correct before deployment |
| 169 | + |
| 170 | +5. **Icon Support**: Icons can be specified as React component names (from `@wp-playground/components`) or as URLs to SVG files. External SVG icons must be accessible from the client (CORS considerations apply). |
| 171 | + |
| 172 | +## Icon Customization |
| 173 | + |
| 174 | +Icons are now fully customizable via the `icon` field in the JSON configuration: |
| 175 | + |
| 176 | +### Available React Component Icons |
| 177 | +From `@wp-playground/components`: |
| 178 | +- `WordPressIcon` - WordPress logo (default) |
| 179 | +- `ClockIcon` - Clock icon |
| 180 | +- `playgroundLogo` - Playground logo |
| 181 | +- `temporaryStorage` - Temporary storage icon |
| 182 | + |
| 183 | +### Using External SVG Icons |
| 184 | +Provide a direct URL to an SVG file: |
| 185 | +```json |
| 186 | +{ |
| 187 | + "icon": "https://example.com/custom-icon.svg" |
| 188 | +} |
| 189 | +``` |
| 190 | + |
| 191 | +Note: External SVG icons must be accessible from the client browser. Ensure proper CORS headers are set if the SVG is hosted on a different domain. |
| 192 | + |
| 193 | +## Security Considerations |
| 194 | + |
| 195 | +The implementation: |
| 196 | +- Uses standard fetch API (no eval or dynamic code execution) |
| 197 | +- Only reads from a known, controlled endpoint (`/blueprints/blueprints.json`) |
| 198 | +- Uses TypeScript for type safety |
| 199 | +- Falls back to safe defaults if fetch fails |
| 200 | + |
| 201 | +The existing .htaccess rules protecting the `/blueprints/` directory continue to apply. |
0 commit comments