WIDGET DEVELOPMENT STANDARDS
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
Widget Development Standards - AI-First Edition
A comprehensive guide for developing widgets in the GoGrab Space page builder. Every widget must be AI-ready, responsive, and provide an excellent editing experience.
Table of Contents
- The Widget Development Checklist
- Required Files Structure
- AI Context & Training Schema
- Property Templates (JSON)
- Preview Components
- Inline Editing Requirements
- Widget Categories & Capabilities
- AI-First Development Pattern
- Validation & Error Handling
- Testing Guidelines
The Widget Development Checklist
Every widget MUST have:
1. TypeScript Interface
Location: libs/libs-widgets/widgets/src/lib/{widget-name}/{widget-name}.types.ts
/**
* List Widget Configuration
*/
export interface ListWidgetConfig {
items: string[];
listType: 'ordered' | 'unordered' | 'none';
markerStyle?: string;
itemSpacing?: number;
}
2. JSON Property Template
Location: libs/libs-foundational/core-properties/src/lib/templates/{widget-name}.template.json
Every property MUST include AI metadata for training purposes.
3. Preview Component with Inline Editing
Location: libs/libs-widgets/widgets/src/lib/{widget-name}/components/{widget-name}-preview.component.ts
Must support:
- Click-to-edit for text content
- Visual controls for complex properties
- AI prompt integration
- Keyboard shortcuts (Enter, Escape, Delete)
- Accessibility (ARIA labels, keyboard nav)
4. Widget Registration
Location: libs/libs-widgets/widget-system/src/lib/registry/unified-widget-registry.service.ts
this.registerWidget({
id: 'list',
type: 'list',
displayName: 'List',
icon: 'fa-list',
category: 'content',
capabilities: {
canResize: true,
canMove: true,
canDelete: true,
canDuplicate: true,
hasInlineEdit: true,
aiGenerable: true,
},
});
5. Validation Rules
Every widget must validate its configuration:
export function validateListConfig(config: ListWidgetConfig): ValidationResult {
const errors: string[] = [];
if (!config.items || !Array.isArray(config.items)) {
errors.push('Items must be an array');
}
return {
valid: errors.length === 0,
errors,
};
}
Required Files Structure
libs/libs-widgets/widgets/src/lib/{widget-name}/
├── {widget-name}.types.ts # TypeScript interfaces
├── {widget-name}.widget.ts # Widget registration
├── components/
│ ├── {widget-name}-preview.component.ts # Preview/edit component
│ ├── {widget-name}-preview.component.html
│ └── {widget-name}-preview.component.scss
├── validators/
│ └── {widget-name}.validator.ts # Config validation
└── index.ts # Barrel exports
libs/libs-foundational/core-properties/src/lib/templates/
└── {widget-name}.template.json # Property definitions with AI context
AI Context & Training Schema
Every widget must export an AI training schema for Space Agents.
Schema Structure
{
"widgetType": "list",
"version": "1.0.0",
"aiCapabilities": {
"canGenerate": true,
"canModify": true,
"canStyle": true,
"supportsInlinePrompts": true
},
"naturalLanguageExamples": [
"Add a bulleted list with 3 items",
"Change the list to numbered",
"Add 'Introduction' to the list",
"Make the list items have more spacing"
],
"propertyMappings": {
"items": {
"naturalLanguage": ["items", "list items", "bullets", "entries", "points"],
"type": "array<string>",
"aiGenerable": true,
"importance": "essential"
},
"listType": {
"naturalLanguage": ["numbered", "bulleted", "ordered", "unordered", "type"],
"type": "enum",
"values": ["unordered", "ordered", "none"],
"importance": "high"
},
"itemSpacing": {
"naturalLanguage": ["spacing", "gap", "space between"],
"type": "number",
"unit": "px",
"constraints": { "min": 0, "max": 48 },
"importance": "medium"
}
},
"contextualPrompts": {
"empty": "This list is empty. What items should I add?",
"fewItems": "Add more items to this list?",
"manyItems": "This list has {count} items. Should I organize them?"
}
}
AI Property Metadata
Each property in the JSON template MUST include:
{
"id": "list-items",
"key": "items",
"label": "List Items",
"controlType": "array",
"ai": {
"importance": "essential",
"prompt": "Modify list items",
"examples": [
"Add 'Buy groceries' to the list",
"Change first item to 'Introduction'",
"Add 5 benefits of our product"
],
"constraints": ["Must be non-empty strings"],
"useCases": ["list content", "bullet points", "steps", "features"]
}
}
Importance Levels
| Level | Description | Example Properties |
|---|---|---|
essential |
Core content, always needed | Text content, list items, image src |
high |
Important for appearance/function | Variant, size, listType |
medium |
Useful customization | Spacing, colors, alignment |
low |
Advanced/optional | Animation, custom CSS |
Property Templates (JSON)
Complete Template Example
{
"id": "template_list",
"name": "List",
"category": "content",
"builtin": true,
"order": 10,
"widgetTypes": ["list"],
"properties": [
{
"id": "list-items",
"key": "items",
"label": "List Items",
"controlType": "array",
"category": "content",
"order": 1,
"defaultValue": ["Item 1", "Item 2", "Item 3"],
"ai": {
"importance": "essential",
"prompt": "Modify list items",
"examples": ["Add 'Buy groceries'", "Remove second item"],
"useCases": ["list content", "bullet points"]
}
},
{
"id": "list-type",
"key": "listType",
"label": "List Type",
"controlType": "select",
"category": "appearance",
"order": 2,
"defaultValue": "unordered",
"validation": {
"options": [
{ "label": "Bulleted", "value": "unordered" },
{ "label": "Numbered", "value": "ordered" }
]
},
"ai": {
"importance": "high",
"prompt": "Change list style",
"examples": ["Make it numbered", "Use bullets"]
}
}
]
}
Control Types
| Type | Description | Use For |
|---|---|---|
text |
Single-line text input | Short text, URLs |
textarea |
Multi-line text | Long content, descriptions |
number |
Numeric input | Sizes, counts |
slider |
Range slider | Spacing, opacity |
boolean |
Toggle switch | Enable/disable features |
select |
Dropdown menu | Fixed options |
color |
Color picker | Colors |
array |
List editor | Items, tags |
Preview Components
Requirements
- Separate Files: .ts, .html, .scss (NO inline templates)
- OnPush Change Detection: For performance
- Signals: Use Angular signals for state
- Input/Output: Standard component communication
Template
@Component({
selector: 'lib-list-preview',
standalone: true,
imports: [CommonModule],
templateUrl: './list-preview.component.html',
styleUrl: './list-preview.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListPreviewComponent {
/** Widget configuration */
config = input.required<ListWidgetConfig>();
/** Emitted when config changes */
configChange = output<Partial<ListWidgetConfig>>();
/** Currently editing item index */
editingIndex = signal<number | null>(null);
/** Computed list items */
items = computed(() => this.config().items || []);
/** Computed styles */
styles = computed(() => ({
listStyleType: this.getMarkerStyle(),
gap: `${this.config().itemSpacing || 8}px`,
}));
}
Inline Editing Requirements
Every Editable Widget Must Support:
- Click to Edit: Click on content to enter edit mode
- Blur to Save: Clicking outside saves changes
- Enter to Confirm: Pressing Enter saves and exits edit mode
- Escape to Cancel: Pressing Escape cancels changes
- Delete Handling: Backspace on empty removes item (for arrays)
- Tab Navigation: Tab moves to next editable item
- AI Integration: Button/shortcut to invoke AI assistance
Example Implementation
<!-- List Item Inline Editing -->
<li class="list-item" [class.editing]="editingIndex() === idx">
<input
#itemInput
type="text"
[value]="item"
(focus)="startEditing(idx)"
(blur)="finishEditing(idx, $event)"
(keydown.enter)="addItemAfter(idx, $event)"
(keydown.escape)="cancelEditing()"
(keydown.backspace)="maybeRemoveItem(idx, $event)"
[attr.aria-label]="'List item ' + (idx + 1)"
/>
</li>
<!-- AI Add Items Button -->
<button class="ai-add-btn" (click)="showAIItemPrompt()">
<i class="fas fa-magic"></i>
Ask AI to add items
</button>
Widget Categories & Capabilities
Categories
| Category | Widgets | Description |
|---|---|---|
| Basic | Text, Heading, Button, List, Link | Fundamental content widgets |
| Layout | Section, Container, Grid | Structural widgets |
| Media | Image, Video, Icon | Visual content |
| Form | Input, Textarea, Select, Checkbox | User input |
| Data | Table, Chart, Card | Data display |
| Navigation | Nav, Breadcrumb, Tabs | Navigation elements |
Capability Matrix
| Widget | AI Gen | AI Modify | Inline Edit | Responsive | Has Children |
|---|---|---|---|---|---|
| Text | ✅ | ✅ | ✅ (contenteditable) | ✅ | ❌ |
| Heading | ✅ | ✅ | ✅ (contenteditable) | ✅ | ❌ |
| Button | ✅ | ✅ | ✅ (text only) | ✅ | ❌ |
| List | ✅ | ✅ | ✅ (each item) | ✅ | ❌ |
| Link | ✅ | ✅ | ✅ (text + URL modal) | ✅ | ❌ |
| Section | ✅ | ✅ | ✅ (name + layout) | ✅ | ✅ |
| Container | ✅ | ✅ | ✅ (layout controls) | ✅ | ✅ |
| Grid | ✅ | ✅ | ✅ (column adjust) | ✅ | ✅ |
| Image | ✅ AI gen | ✅ | ⚠️ (upload/URL/AI) | ✅ | ❌ |
| Video | ✅ AI gen | ✅ | ⚠️ (upload/URL/AI) | ✅ | ❌ |
| Icon | ✅ | ✅ | ✅ (icon picker) | ✅ | ❌ |
| Divider | ❌ | ✅ | ❌ | ✅ | ❌ |
| Spacer | ❌ | ✅ | ⚠️ (drag handle) | ✅ | ❌ |
AI-First Development Pattern
1. Define Widget Schema with AI Metadata
export interface WidgetConfig {
property: string;
ai: PropertyAIMetadata; // ← Required
}
interface PropertyAIMetadata {
importance: 'essential' | 'high' | 'medium' | 'low';
prompt: string;
examples: string[];
constraints?: string[];
}
2. Create Property Template JSON with AI Prompts
{
"properties": [{
"key": "property",
"ai": {
"prompt": "Change widget property",
"examples": ["Make it blue", "Set to 100px"]
}
}]
}
3. Implement Preview with Inline AI
<div class="widget-ai-controls">
<input placeholder="Tell AI what to change..." />
<button (click)="executeAIPrompt()">
<i class="fas fa-magic"></i>
</button>
</div>
4. Export AI Training Data
// Every widget should export getAIContext()
widget.getAIContext() → Used for Space Agent training
Validation & Error Handling
Config Validation
interface ValidationResult {
valid: boolean;
errors?: string[];
warnings?: string[];
}
export function validateConfig(config: WidgetConfig): ValidationResult {
const errors: string[] = [];
const warnings: string[] = [];
// Required field validation
if (!config.items?.length) {
errors.push('List must have at least one item');
}
// Type validation
if (typeof config.itemSpacing !== 'number') {
warnings.push('Item spacing should be a number');
}
// Range validation
if (config.itemSpacing && (config.itemSpacing < 0 || config.itemSpacing > 100)) {
errors.push('Item spacing must be between 0 and 100');
}
return { valid: errors.length === 0, errors, warnings };
}
Error Boundary
Widgets should gracefully handle errors:
@Component({
template: `
@if (hasError()) {
<div class="widget-error">
<i class="fas fa-exclamation-triangle"></i>
<span>Widget failed to render</span>
<button (click)="retry()">Retry</button>
</div>
} @else {
<ng-content></ng-content>
}
`
})
export class WidgetErrorBoundary { }
Testing Guidelines
Unit Tests Required
- Config Validation: Test all validation rules
- Default Values: Verify defaults are applied correctly
- AI Context: Ensure AI metadata is complete
- Computed Properties: Test derived values
Integration Tests
- Inline Editing: Test edit mode enter/exit
- Keyboard Navigation: Test all shortcuts
- AI Integration: Test prompt handling
- Responsive Behavior: Test breakpoint changes
Example Test
describe('ListPreviewComponent', () => {
it('should render all items', () => {
const config = { items: ['A', 'B', 'C'], listType: 'ordered' };
component.config.set(config);
expect(component.items()).toEqual(['A', 'B', 'C']);
});
it('should enter edit mode on item click', () => {
component.startEditing(0);
expect(component.editingIndex()).toBe(0);
});
it('should emit config change on edit', () => {
const spy = vi.spyOn(component.configChange, 'emit');
component.updateItem(0, 'New Value');
expect(spy).toHaveBeenCalledWith({ items: ['New Value', 'B', 'C'] });
});
});
Quick Reference
Adding a New Widget
- Create type interface in
{widget-name}.types.ts - Create JSON property template in
templates/{widget-name}.template.json - Create preview component with inline editing
- Register in widget registry
- Add AI training schema
- Write tests
AI Property Checklist
-
importancelevel set -
promptdescription clear -
examplesinclude 2-3 real examples -
constraintslist any limitations -
useCasesdescribe when to use
Files to Update When Adding Widget
core-properties/src/lib/templates/{widget}.template.jsoncore-properties/src/lib/templates/index.tswidget-system/src/lib/registry/unified-widget-registry.service.tswidgets/src/lib/{widget}/index.tswidgets/src/index.ts
Last Updated: Session 55 - AI-First Page Builder Enhancement