From 205ea9eb75b65a31da1882b16db701417551ac98 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 13:48:34 +0000 Subject: [PATCH 1/4] Initial plan From d6441900881e66218ecfbf703d03730566fa47e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 14:00:15 +0000 Subject: [PATCH 2/4] Add defaultProps to all components for initial state in designer Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- packages/renderer/src/renderers/basic/div.tsx | 5 +- .../src/renderers/basic/separator.tsx | 6 +- .../renderer/src/renderers/basic/span.tsx | 6 ++ .../renderer/src/renderers/basic/text.tsx | 5 +- .../src/renderers/complex/carousel.tsx | 12 +++- .../src/renderers/complex/resizable.tsx | 12 +++- .../src/renderers/complex/scroll-area.tsx | 11 ++- .../renderer/src/renderers/complex/table.tsx | 15 +++- .../src/renderers/data-display/alert.tsx | 7 +- .../src/renderers/data-display/avatar.tsx | 6 +- .../src/renderers/data-display/badge.tsx | 6 +- .../src/renderers/disclosure/accordion.tsx | 24 ++++++- .../src/renderers/disclosure/collapsible.tsx | 7 +- .../src/renderers/feedback/progress.tsx | 6 +- .../src/renderers/feedback/skeleton.tsx | 7 +- .../src/renderers/feedback/toaster.tsx | 5 +- .../renderer/src/renderers/form/button.tsx | 7 +- .../renderer/src/renderers/form/calendar.tsx | 6 +- .../renderer/src/renderers/form/checkbox.tsx | 6 +- .../renderer/src/renderers/form/input-otp.tsx | 5 +- .../renderer/src/renderers/form/input.tsx | 8 ++- .../src/renderers/form/radio-group.tsx | 10 ++- .../renderer/src/renderers/form/select.tsx | 11 ++- .../renderer/src/renderers/form/slider.tsx | 9 ++- .../renderer/src/renderers/form/switch.tsx | 6 +- .../renderer/src/renderers/form/textarea.tsx | 7 +- .../renderer/src/renderers/form/toggle.tsx | 19 ++++- .../renderer/src/renderers/layout/card.tsx | 7 +- .../renderer/src/renderers/layout/tabs.tsx | 11 ++- .../src/renderers/navigation/header-bar.tsx | 8 ++- .../src/renderers/navigation/sidebar.tsx | 72 +++++++++++++++++-- .../src/renderers/overlay/alert-dialog.tsx | 9 ++- .../src/renderers/overlay/context-menu.tsx | 11 ++- .../renderer/src/renderers/overlay/dialog.tsx | 9 ++- .../renderer/src/renderers/overlay/drawer.tsx | 8 ++- .../src/renderers/overlay/dropdown-menu.tsx | 13 +++- .../src/renderers/overlay/hover-card.tsx | 7 +- .../src/renderers/overlay/popover.tsx | 8 ++- .../renderer/src/renderers/overlay/sheet.tsx | 10 ++- .../src/renderers/overlay/tooltip.tsx | 8 ++- 40 files changed, 370 insertions(+), 45 deletions(-) diff --git a/packages/renderer/src/renderers/basic/div.tsx b/packages/renderer/src/renderers/basic/div.tsx index 0021f85c8..2937c3e35 100644 --- a/packages/renderer/src/renderers/basic/div.tsx +++ b/packages/renderer/src/renderers/basic/div.tsx @@ -11,6 +11,9 @@ ComponentRegistry.register('div', label: 'Container', inputs: [ { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + className: 'p-4 border border-dashed border-gray-300 rounded min-h-[100px]' + } } ); diff --git a/packages/renderer/src/renderers/basic/separator.tsx b/packages/renderer/src/renderers/basic/separator.tsx index 455f2f7b8..c8d42a0bb 100644 --- a/packages/renderer/src/renderers/basic/separator.tsx +++ b/packages/renderer/src/renderers/basic/separator.tsx @@ -16,6 +16,10 @@ ComponentRegistry.register('separator', label: 'Orientation' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + orientation: 'horizontal', + className: 'my-4' + } } ); diff --git a/packages/renderer/src/renderers/basic/span.tsx b/packages/renderer/src/renderers/basic/span.tsx index 41e59a2d3..9b19f1405 100644 --- a/packages/renderer/src/renderers/basic/span.tsx +++ b/packages/renderer/src/renderers/basic/span.tsx @@ -11,6 +11,12 @@ ComponentRegistry.register('span', label: 'Inline Container', inputs: [ { name: 'className', type: 'string', label: 'CSS Class' } + ], + defaultProps: { + className: 'px-2 py-1' + }, + defaultChildren: [ + { type: 'text', content: 'Inline text' } ] } ); diff --git a/packages/renderer/src/renderers/basic/text.tsx b/packages/renderer/src/renderers/basic/text.tsx index 9585d51a5..a842d7641 100644 --- a/packages/renderer/src/renderers/basic/text.tsx +++ b/packages/renderer/src/renderers/basic/text.tsx @@ -8,6 +8,9 @@ ComponentRegistry.register('text', label: 'Text', inputs: [ { name: 'content', type: 'string', label: 'Content', required: true } - ] + ], + defaultProps: { + content: 'Text content' + } } ); diff --git a/packages/renderer/src/renderers/complex/carousel.tsx b/packages/renderer/src/renderers/complex/carousel.tsx index dd2e0c710..dc9eea3f7 100644 --- a/packages/renderer/src/renderers/complex/carousel.tsx +++ b/packages/renderer/src/renderers/complex/carousel.tsx @@ -44,6 +44,16 @@ ComponentRegistry.register('carousel', }, { name: 'itemClassName', type: 'string', label: 'Item CSS Class' }, { name: 'className', type: 'string', label: 'Container CSS Class' } - ] + ], + defaultProps: { + orientation: 'horizontal', + showArrows: true, + items: [ + [{ type: 'div', className: 'p-8 border rounded bg-slate-50', body: [{ type: 'text', content: 'Slide 1' }] }], + [{ type: 'div', className: 'p-8 border rounded bg-slate-50', body: [{ type: 'text', content: 'Slide 2' }] }], + [{ type: 'div', className: 'p-8 border rounded bg-slate-50', body: [{ type: 'text', content: 'Slide 3' }] }] + ], + className: 'w-full max-w-xs' + } } ); diff --git a/packages/renderer/src/renderers/complex/resizable.tsx b/packages/renderer/src/renderers/complex/resizable.tsx index 63691b99d..af1759692 100644 --- a/packages/renderer/src/renderers/complex/resizable.tsx +++ b/packages/renderer/src/renderers/complex/resizable.tsx @@ -38,6 +38,16 @@ ComponentRegistry.register('resizable', description: 'Array of { defaultSize, minSize, maxSize, content }' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + direction: 'horizontal', + minHeight: '200px', + withHandle: true, + panels: [ + { defaultSize: 50, content: [{ type: 'div', className: 'p-4', body: [{ type: 'text', content: 'Panel 1' }] }] }, + { defaultSize: 50, content: [{ type: 'div', className: 'p-4', body: [{ type: 'text', content: 'Panel 2' }] }] } + ], + className: 'rounded-lg border' + } } ); diff --git a/packages/renderer/src/renderers/complex/scroll-area.tsx b/packages/renderer/src/renderers/complex/scroll-area.tsx index b2aadda67..14e507e13 100644 --- a/packages/renderer/src/renderers/complex/scroll-area.tsx +++ b/packages/renderer/src/renderers/complex/scroll-area.tsx @@ -17,6 +17,15 @@ ComponentRegistry.register('scroll-area', { name: 'orientation', type: 'enum', enum: ['vertical', 'horizontal', 'both'], defaultValue: 'vertical', label: 'Orientation' }, { name: 'content', type: 'slot', label: 'Content' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + height: '200px', + width: '100%', + orientation: 'vertical', + content: [ + { type: 'div', className: 'p-4', body: [{ type: 'text', content: 'Scrollable content goes here. Add more content to see scrolling behavior.' }] } + ], + className: 'rounded-md border' + } } ); diff --git a/packages/renderer/src/renderers/complex/table.tsx b/packages/renderer/src/renderers/complex/table.tsx index 5405b52a7..8c7822cbd 100644 --- a/packages/renderer/src/renderers/complex/table.tsx +++ b/packages/renderer/src/renderers/complex/table.tsx @@ -63,6 +63,19 @@ ComponentRegistry.register('table', description: 'Array of objects' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + caption: 'Table Caption', + columns: [ + { header: 'Column 1', accessorKey: 'col1' }, + { header: 'Column 2', accessorKey: 'col2' }, + { header: 'Column 3', accessorKey: 'col3' } + ], + data: [ + { col1: 'Row 1, Col 1', col2: 'Row 1, Col 2', col3: 'Row 1, Col 3' }, + { col1: 'Row 2, Col 1', col2: 'Row 2, Col 2', col3: 'Row 2, Col 3' }, + { col1: 'Row 3, Col 1', col2: 'Row 3, Col 2', col3: 'Row 3, Col 3' } + ] + } } ); diff --git a/packages/renderer/src/renderers/data-display/alert.tsx b/packages/renderer/src/renderers/data-display/alert.tsx index 5710b9432..8d52a2a9e 100644 --- a/packages/renderer/src/renderers/data-display/alert.tsx +++ b/packages/renderer/src/renderers/data-display/alert.tsx @@ -26,6 +26,11 @@ ComponentRegistry.register('alert', label: 'Variant' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + title: 'Alert Title', + description: 'This is an alert message.', + variant: 'default' + } } ); diff --git a/packages/renderer/src/renderers/data-display/avatar.tsx b/packages/renderer/src/renderers/data-display/avatar.tsx index d1e21d53b..65b4edc6e 100644 --- a/packages/renderer/src/renderers/data-display/avatar.tsx +++ b/packages/renderer/src/renderers/data-display/avatar.tsx @@ -19,6 +19,10 @@ ComponentRegistry.register('avatar', { name: 'alt', type: 'string', label: 'Alt Text' }, { name: 'fallback', type: 'string', label: 'Fallback Initials', defaultValue: 'CN' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + fallback: 'CN', + alt: 'Avatar' + } } ); diff --git a/packages/renderer/src/renderers/data-display/badge.tsx b/packages/renderer/src/renderers/data-display/badge.tsx index b422f1675..1b713725e 100644 --- a/packages/renderer/src/renderers/data-display/badge.tsx +++ b/packages/renderer/src/renderers/data-display/badge.tsx @@ -20,6 +20,10 @@ ComponentRegistry.register('badge', label: 'Variant' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + label: 'Badge', + variant: 'default' + } } ); diff --git a/packages/renderer/src/renderers/disclosure/accordion.tsx b/packages/renderer/src/renderers/disclosure/accordion.tsx index 28267f0d7..bf4344e80 100644 --- a/packages/renderer/src/renderers/disclosure/accordion.tsx +++ b/packages/renderer/src/renderers/disclosure/accordion.tsx @@ -32,6 +32,28 @@ ComponentRegistry.register('accordion', description: 'Array of { trigger, content, value }' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + accordionType: 'single', + collapsible: true, + items: [ + { + label: 'Item 1', + value: 'item-1', + content: [{ type: 'text', content: 'Content for item 1' }] + }, + { + label: 'Item 2', + value: 'item-2', + content: [{ type: 'text', content: 'Content for item 2' }] + }, + { + label: 'Item 3', + value: 'item-3', + content: [{ type: 'text', content: 'Content for item 3' }] + } + ], + className: 'w-full' + } } ); diff --git a/packages/renderer/src/renderers/disclosure/collapsible.tsx b/packages/renderer/src/renderers/disclosure/collapsible.tsx index 19f00616d..9d5d79f76 100644 --- a/packages/renderer/src/renderers/disclosure/collapsible.tsx +++ b/packages/renderer/src/renderers/disclosure/collapsible.tsx @@ -33,6 +33,11 @@ ComponentRegistry.register('collapsible', label: 'Content' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + trigger: [{ type: 'button', label: 'Toggle', variant: 'outline' }], + content: [{ type: 'text', content: 'Collapsible content goes here' }], + className: 'w-full' + } } ); diff --git a/packages/renderer/src/renderers/feedback/progress.tsx b/packages/renderer/src/renderers/feedback/progress.tsx index 51d8303d5..61b320940 100644 --- a/packages/renderer/src/renderers/feedback/progress.tsx +++ b/packages/renderer/src/renderers/feedback/progress.tsx @@ -10,6 +10,10 @@ ComponentRegistry.register('progress', inputs: [ { name: 'value', type: 'number', label: 'Value', defaultValue: 0 }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + value: 50, + className: 'w-full' + } } ); diff --git a/packages/renderer/src/renderers/feedback/skeleton.tsx b/packages/renderer/src/renderers/feedback/skeleton.tsx index f8a3d898d..ce0baa416 100644 --- a/packages/renderer/src/renderers/feedback/skeleton.tsx +++ b/packages/renderer/src/renderers/feedback/skeleton.tsx @@ -11,6 +11,11 @@ ComponentRegistry.register('skeleton', { name: 'width', type: 'string', label: 'Width' }, { name: 'height', type: 'string', label: 'Height' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + width: '100%', + height: '20px', + className: 'rounded-md' + } } ); diff --git a/packages/renderer/src/renderers/feedback/toaster.tsx b/packages/renderer/src/renderers/feedback/toaster.tsx index 2c2ee67bb..4b3e20002 100644 --- a/packages/renderer/src/renderers/feedback/toaster.tsx +++ b/packages/renderer/src/renderers/feedback/toaster.tsx @@ -17,6 +17,9 @@ ComponentRegistry.register('toaster', label: 'Toaster', inputs: [ { name: 'provider', type: 'enum', enum: ['default', 'sonner'], defaultValue: 'default', label: 'Provider' } - ] + ], + defaultProps: { + provider: 'default' + } } ); diff --git a/packages/renderer/src/renderers/form/button.tsx b/packages/renderer/src/renderers/form/button.tsx index 417dd158f..5435eb897 100644 --- a/packages/renderer/src/renderers/form/button.tsx +++ b/packages/renderer/src/renderers/form/button.tsx @@ -27,6 +27,11 @@ ComponentRegistry.register('button', defaultValue: 'default' }, { name: 'className', type: 'string', label: 'CSS Class', advanced: true } - ] + ], + defaultProps: { + label: 'Button', + variant: 'default', + size: 'default' + } } ); diff --git a/packages/renderer/src/renderers/form/calendar.tsx b/packages/renderer/src/renderers/form/calendar.tsx index 985fabca6..1e9372c9e 100644 --- a/packages/renderer/src/renderers/form/calendar.tsx +++ b/packages/renderer/src/renderers/form/calendar.tsx @@ -15,6 +15,10 @@ ComponentRegistry.register('calendar', inputs: [ { name: 'mode', type: 'enum', enum: ['default', 'single', 'multiple', 'range'], defaultValue: 'single', label: 'Mode' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + mode: 'single', + className: 'rounded-md border' + } } ); diff --git a/packages/renderer/src/renderers/form/checkbox.tsx b/packages/renderer/src/renderers/form/checkbox.tsx index 4a96498f3..e4a0be946 100644 --- a/packages/renderer/src/renderers/form/checkbox.tsx +++ b/packages/renderer/src/renderers/form/checkbox.tsx @@ -16,6 +16,10 @@ ComponentRegistry.register('checkbox', { name: 'label', type: 'string', label: 'Label', required: true }, { name: 'id', type: 'string', label: 'ID', required: true }, { name: 'checked', type: 'boolean', label: 'Checked' } - ] + ], + defaultProps: { + label: 'Checkbox label', + id: 'checkbox-' + Math.random().toString(36).substr(2, 9) + } } ); diff --git a/packages/renderer/src/renderers/form/input-otp.tsx b/packages/renderer/src/renderers/form/input-otp.tsx index 0c0d5930c..fbfb2e48b 100644 --- a/packages/renderer/src/renderers/form/input-otp.tsx +++ b/packages/renderer/src/renderers/form/input-otp.tsx @@ -22,6 +22,9 @@ ComponentRegistry.register('input-otp', inputs: [ { name: 'maxLength', type: 'number', label: 'Max Length', defaultValue: 6 }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + maxLength: 6 + } } ); diff --git a/packages/renderer/src/renderers/form/input.tsx b/packages/renderer/src/renderers/form/input.tsx index 861c8c7e4..a176c4609 100644 --- a/packages/renderer/src/renderers/form/input.tsx +++ b/packages/renderer/src/renderers/form/input.tsx @@ -27,6 +27,12 @@ ComponentRegistry.register('input', defaultValue: 'text' }, { name: 'id', type: 'string', label: 'ID', required: true } - ] + ], + defaultProps: { + label: 'Label', + placeholder: 'Enter text...', + inputType: 'text', + id: 'input-' + Math.random().toString(36).substr(2, 9) + } } ); diff --git a/packages/renderer/src/renderers/form/radio-group.tsx b/packages/renderer/src/renderers/form/radio-group.tsx index 19b61fcaf..d78142c38 100644 --- a/packages/renderer/src/renderers/form/radio-group.tsx +++ b/packages/renderer/src/renderers/form/radio-group.tsx @@ -24,6 +24,14 @@ ComponentRegistry.register('radio-group', description: 'Array of {label, value} objects' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + id: 'radio-' + Math.random().toString(36).substr(2, 9), + items: [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + { label: 'Option 3', value: 'option3' } + ] + } } ); diff --git a/packages/renderer/src/renderers/form/select.tsx b/packages/renderer/src/renderers/form/select.tsx index 1eba9a22d..5b7b037db 100644 --- a/packages/renderer/src/renderers/form/select.tsx +++ b/packages/renderer/src/renderers/form/select.tsx @@ -36,6 +36,15 @@ ComponentRegistry.register('select', label: 'Options', description: 'Array of {label, value} objects' } - ] + ], + defaultProps: { + label: 'Select an option', + placeholder: 'Choose...', + options: [ + { label: 'Option 1', value: 'option1' }, + { label: 'Option 2', value: 'option2' }, + { label: 'Option 3', value: 'option3' } + ] + } } ); diff --git a/packages/renderer/src/renderers/form/slider.tsx b/packages/renderer/src/renderers/form/slider.tsx index a335b2a4b..2f93e5657 100644 --- a/packages/renderer/src/renderers/form/slider.tsx +++ b/packages/renderer/src/renderers/form/slider.tsx @@ -20,6 +20,13 @@ ComponentRegistry.register('slider', { name: 'min', type: 'number', label: 'Min', defaultValue: 0 }, { name: 'step', type: 'number', label: 'Step', defaultValue: 1 }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + defaultValue: [50], + max: 100, + min: 0, + step: 1, + className: 'w-full' + } } ); diff --git a/packages/renderer/src/renderers/form/switch.tsx b/packages/renderer/src/renderers/form/switch.tsx index e33710100..6c1c539b3 100644 --- a/packages/renderer/src/renderers/form/switch.tsx +++ b/packages/renderer/src/renderers/form/switch.tsx @@ -14,6 +14,10 @@ ComponentRegistry.register('switch', { name: 'label', type: 'string', label: 'Label', required: true }, { name: 'id', type: 'string', label: 'ID', required: true }, { name: 'checked', type: 'boolean', label: 'Checked' } - ] + ], + defaultProps: { + label: 'Switch label', + id: 'switch-' + Math.random().toString(36).substr(2, 9) + } } ); diff --git a/packages/renderer/src/renderers/form/textarea.tsx b/packages/renderer/src/renderers/form/textarea.tsx index 982b5ed85..6fb431e24 100644 --- a/packages/renderer/src/renderers/form/textarea.tsx +++ b/packages/renderer/src/renderers/form/textarea.tsx @@ -19,6 +19,11 @@ ComponentRegistry.register('textarea', { name: 'label', type: 'string', label: 'Label' }, { name: 'placeholder', type: 'string', label: 'Placeholder' }, { name: 'id', type: 'string', label: 'ID', required: true } - ] + ], + defaultProps: { + label: 'Textarea label', + placeholder: 'Enter text here...', + id: 'textarea-' + Math.random().toString(36).substr(2, 9) + } } ); diff --git a/packages/renderer/src/renderers/form/toggle.tsx b/packages/renderer/src/renderers/form/toggle.tsx index c54e8e349..6d0d963fe 100644 --- a/packages/renderer/src/renderers/form/toggle.tsx +++ b/packages/renderer/src/renderers/form/toggle.tsx @@ -22,7 +22,12 @@ ComponentRegistry.register('toggle', { name: 'variant', type: 'enum', enum: ['default', 'outline'], defaultValue: 'default', label: 'Variant' }, { name: 'size', type: 'enum', enum: ['default', 'sm', 'lg'], defaultValue: 'default', label: 'Size' }, { name: 'ariaLabel', type: 'string', label: 'Aria Label' } - ] + ], + defaultProps: { + label: 'Toggle', + variant: 'default', + size: 'default' + } } ); @@ -55,6 +60,16 @@ ComponentRegistry.register('toggle-group', description: 'Array of {label, value, icon?} objects' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + groupType: 'single', + variant: 'default', + size: 'default', + items: [ + { label: 'A', value: 'a' }, + { label: 'B', value: 'b' }, + { label: 'C', value: 'c' } + ] + } } ); diff --git a/packages/renderer/src/renderers/layout/card.tsx b/packages/renderer/src/renderers/layout/card.tsx index 1873d9fca..1ad11a454 100644 --- a/packages/renderer/src/renderers/layout/card.tsx +++ b/packages/renderer/src/renderers/layout/card.tsx @@ -28,6 +28,11 @@ ComponentRegistry.register('card', { name: 'title', type: 'string', label: 'Title' }, { name: 'description', type: 'string', label: 'Description' }, { name: 'className', type: 'string', label: 'CSS Class' } - ] + ], + defaultProps: { + title: 'Card Title', + description: 'Card description goes here', + className: 'w-full' + } } ); diff --git a/packages/renderer/src/renderers/layout/tabs.tsx b/packages/renderer/src/renderers/layout/tabs.tsx index 7d31d2e92..3e7d702a0 100644 --- a/packages/renderer/src/renderers/layout/tabs.tsx +++ b/packages/renderer/src/renderers/layout/tabs.tsx @@ -32,6 +32,15 @@ ComponentRegistry.register('tabs', type: 'array', label: 'Items' } - ] + ], + defaultProps: { + defaultValue: 'tab1', + items: [ + { label: 'Tab 1', value: 'tab1', body: [{ type: 'text', content: 'Content for Tab 1' }] }, + { label: 'Tab 2', value: 'tab2', body: [{ type: 'text', content: 'Content for Tab 2' }] }, + { label: 'Tab 3', value: 'tab3', body: [{ type: 'text', content: 'Content for Tab 3' }] } + ], + className: 'w-full' + } } ); diff --git a/packages/renderer/src/renderers/navigation/header-bar.tsx b/packages/renderer/src/renderers/navigation/header-bar.tsx index 0593e5be2..5a2a998af 100644 --- a/packages/renderer/src/renderers/navigation/header-bar.tsx +++ b/packages/renderer/src/renderers/navigation/header-bar.tsx @@ -38,6 +38,12 @@ ComponentRegistry.register('header-bar', label: 'Header Bar', inputs: [ { name: 'crumbs', type: 'array', label: 'Breadcrumbs' } - ] + ], + defaultProps: { + crumbs: [ + { label: 'Home', href: '#' }, + { label: 'Current Page' } + ] + } } ); diff --git a/packages/renderer/src/renderers/navigation/sidebar.tsx b/packages/renderer/src/renderers/navigation/sidebar.tsx index a0b88c212..8103a8512 100644 --- a/packages/renderer/src/renderers/navigation/sidebar.tsx +++ b/packages/renderer/src/renderers/navigation/sidebar.tsx @@ -24,6 +24,13 @@ ComponentRegistry.register('sidebar-provider', label: 'Sidebar Provider', inputs: [ { name: 'defaultOpen', type: 'boolean', label: 'Default Open', defaultValue: true } + ], + defaultProps: { + defaultOpen: true + }, + defaultChildren: [ + { type: 'sidebar' }, + { type: 'sidebar-inset' } ] } ); @@ -38,6 +45,16 @@ ComponentRegistry.register('sidebar', { name: 'collapsible', type: 'enum', enum: ['offcanvas', 'icon', 'none'], defaultValue: 'icon', label: 'Collapsible' }, { name: 'side', type: 'enum', enum: ['left', 'right'], defaultValue: 'left', label: 'Side' }, { name: 'variant', type: 'enum', enum: ['sidebar', 'floating', 'inset'], defaultValue: 'sidebar', label: 'Variant' } + ], + defaultProps: { + collapsible: 'icon', + side: 'left', + variant: 'sidebar' + }, + defaultChildren: [ + { type: 'sidebar-header' }, + { type: 'sidebar-content' }, + { type: 'sidebar-footer' } ] } ); @@ -46,14 +63,24 @@ ComponentRegistry.register('sidebar-header', ({ schema, ...props }) => ( {renderChildren(schema.body)} ), - { label: 'Sidebar Header' } + { + label: 'Sidebar Header', + defaultChildren: [ + { type: 'text', content: 'Sidebar Header' } + ] + } ); ComponentRegistry.register('sidebar-content', ({ schema, ...props }) => ( {renderChildren(schema.body)} ), - { label: 'Sidebar Content' } + { + label: 'Sidebar Content', + defaultChildren: [ + { type: 'sidebar-group' } + ] + } ); ComponentRegistry.register('sidebar-group', @@ -69,6 +96,12 @@ ComponentRegistry.register('sidebar-group', label: 'Sidebar Group', inputs: [ { name: 'label', type: 'string', label: 'Label' } + ], + defaultProps: { + label: 'Menu' + }, + defaultChildren: [ + { type: 'sidebar-menu' } ] } ); @@ -77,14 +110,25 @@ ComponentRegistry.register('sidebar-menu', ({ schema, ...props }) => ( {renderChildren(schema.body)} ), - { label: 'Sidebar Menu' } + { + label: 'Sidebar Menu', + defaultChildren: [ + { type: 'sidebar-menu-item' }, + { type: 'sidebar-menu-item' } + ] + } ); ComponentRegistry.register('sidebar-menu-item', ({ schema, ...props }) => ( {renderChildren(schema.body)} ), - { label: 'Sidebar Menu Item' } + { + label: 'Sidebar Menu Item', + defaultChildren: [ + { type: 'sidebar-menu-button' } + ] + } ); ComponentRegistry.register('sidebar-menu-button', @@ -99,6 +143,12 @@ ComponentRegistry.register('sidebar-menu-button', { name: 'active', type: 'boolean', label: 'Active', defaultValue: false }, { name: 'size', type: 'enum', enum: ['default', 'sm', 'lg'], defaultValue: 'default', label: 'Size' }, { name: 'tooltip', type: 'string', label: 'Tooltip' } + ], + defaultProps: { + size: 'default' + }, + defaultChildren: [ + { type: 'text', content: 'Menu Item' } ] } ); @@ -107,14 +157,24 @@ ComponentRegistry.register('sidebar-footer', ({ schema, ...props }) => ( {renderChildren(schema.body)} ), - { label: 'Sidebar Footer' } + { + label: 'Sidebar Footer', + defaultChildren: [ + { type: 'text', content: 'Footer' } + ] + } ); ComponentRegistry.register('sidebar-inset', ({ schema, ...props }) => ( {renderChildren(schema.body)} ), - { label: 'Sidebar Inset' } + { + label: 'Sidebar Inset', + defaultChildren: [ + { type: 'div', className: 'p-4', body: [{ type: 'text', content: 'Main content area' }] } + ] + } ); ComponentRegistry.register('sidebar-trigger', diff --git a/packages/renderer/src/renderers/overlay/alert-dialog.tsx b/packages/renderer/src/renderers/overlay/alert-dialog.tsx index 8bbce43d2..9f6347a85 100644 --- a/packages/renderer/src/renderers/overlay/alert-dialog.tsx +++ b/packages/renderer/src/renderers/overlay/alert-dialog.tsx @@ -50,6 +50,13 @@ ComponentRegistry.register('alert-dialog', label: 'Content/Body' }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + title: 'Are you sure?', + description: 'This action cannot be undone.', + cancelText: 'Cancel', + actionText: 'Continue', + trigger: [{ type: 'button', label: 'Open Alert', variant: 'destructive' }] + } } ); diff --git a/packages/renderer/src/renderers/overlay/context-menu.tsx b/packages/renderer/src/renderers/overlay/context-menu.tsx index 397f9bd89..17ce8572c 100644 --- a/packages/renderer/src/renderers/overlay/context-menu.tsx +++ b/packages/renderer/src/renderers/overlay/context-menu.tsx @@ -71,6 +71,15 @@ ComponentRegistry.register('context-menu', description: 'Recursive structure: { type?: "separator"|"label", label, shortcut, children }' }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + items: [ + { label: 'Action 1' }, + { label: 'Action 2' }, + { type: 'separator' }, + { label: 'Action 3' } + ], + trigger: [{ type: 'text', content: 'Right click here' }] + } } ); diff --git a/packages/renderer/src/renderers/overlay/dialog.tsx b/packages/renderer/src/renderers/overlay/dialog.tsx index 3d88105fe..0402f7833 100644 --- a/packages/renderer/src/renderers/overlay/dialog.tsx +++ b/packages/renderer/src/renderers/overlay/dialog.tsx @@ -55,6 +55,13 @@ ComponentRegistry.register('dialog', label: 'Footer' }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + title: 'Dialog Title', + description: 'Dialog description goes here', + modal: true, + trigger: [{ type: 'button', label: 'Open Dialog' }], + content: [{ type: 'text', content: 'Dialog content goes here' }] + } } ); diff --git a/packages/renderer/src/renderers/overlay/drawer.tsx b/packages/renderer/src/renderers/overlay/drawer.tsx index accc5d468..2fb28b724 100644 --- a/packages/renderer/src/renderers/overlay/drawer.tsx +++ b/packages/renderer/src/renderers/overlay/drawer.tsx @@ -56,6 +56,12 @@ ComponentRegistry.register('drawer', label: 'Footer' }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + title: 'Drawer Title', + description: 'Drawer description', + trigger: [{ type: 'button', label: 'Open Drawer' }], + content: [{ type: 'text', content: 'Drawer content goes here' }] + } } ); diff --git a/packages/renderer/src/renderers/overlay/dropdown-menu.tsx b/packages/renderer/src/renderers/overlay/dropdown-menu.tsx index b76787c57..26b28ae07 100644 --- a/packages/renderer/src/renderers/overlay/dropdown-menu.tsx +++ b/packages/renderer/src/renderers/overlay/dropdown-menu.tsx @@ -73,6 +73,17 @@ ComponentRegistry.register('dropdown-menu', description: 'Recursive structure: { type?: "separator"|"label", label, icon, shortcut, disabled, children: [] }' }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + trigger: [{ type: 'button', label: 'Menu', variant: 'outline' }], + items: [ + { label: 'Item 1' }, + { label: 'Item 2' }, + { type: 'separator' }, + { label: 'Item 3' } + ], + align: 'start', + side: 'bottom' + } } ); diff --git a/packages/renderer/src/renderers/overlay/hover-card.tsx b/packages/renderer/src/renderers/overlay/hover-card.tsx index b79cf5b87..fd227298f 100644 --- a/packages/renderer/src/renderers/overlay/hover-card.tsx +++ b/packages/renderer/src/renderers/overlay/hover-card.tsx @@ -35,6 +35,11 @@ ComponentRegistry.register('hover-card', label: 'Content' }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + trigger: [{ type: 'button', label: 'Hover me', variant: 'link' }], + content: [{ type: 'text', content: 'Hover card content appears on hover' }], + side: 'top' + } } ); diff --git a/packages/renderer/src/renderers/overlay/popover.tsx b/packages/renderer/src/renderers/overlay/popover.tsx index 3425150b9..0bf032ba7 100644 --- a/packages/renderer/src/renderers/overlay/popover.tsx +++ b/packages/renderer/src/renderers/overlay/popover.tsx @@ -35,6 +35,12 @@ ComponentRegistry.register('popover', label: 'Content', }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + trigger: [{ type: 'button', label: 'Open Popover', variant: 'outline' }], + content: [{ type: 'text', content: 'Popover content goes here' }], + align: 'center', + side: 'bottom' + } } ); diff --git a/packages/renderer/src/renderers/overlay/sheet.tsx b/packages/renderer/src/renderers/overlay/sheet.tsx index a534276fd..6aa11de6f 100644 --- a/packages/renderer/src/renderers/overlay/sheet.tsx +++ b/packages/renderer/src/renderers/overlay/sheet.tsx @@ -54,6 +54,14 @@ ComponentRegistry.register('sheet', label: 'Footer' }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + title: 'Sheet Title', + description: 'Sheet description', + side: 'right', + modal: true, + trigger: [{ type: 'button', label: 'Open Sheet' }], + content: [{ type: 'text', content: 'Sheet content goes here' }] + } } ); diff --git a/packages/renderer/src/renderers/overlay/tooltip.tsx b/packages/renderer/src/renderers/overlay/tooltip.tsx index 1308181ed..4d3dd9bf1 100644 --- a/packages/renderer/src/renderers/overlay/tooltip.tsx +++ b/packages/renderer/src/renderers/overlay/tooltip.tsx @@ -46,6 +46,12 @@ ComponentRegistry.register('tooltip', label: 'Rich Content' }, { name: 'className', type: 'string', label: 'Content CSS Class' } - ] + ], + defaultProps: { + trigger: [{ type: 'button', label: 'Hover me', variant: 'outline' }], + content: 'Tooltip content', + delayDuration: 700, + side: 'top' + } } ); From 2a2885bcc351ebc1cd3ea7b557a4f38eede51558 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 14:02:45 +0000 Subject: [PATCH 3/4] Add tests for component default props Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- .../src/__tests__/defaultProps.test.tsx | 128 ++++++++++++++++++ .../renderer/src/__tests__/registry.test.tsx | 41 ++++++ 2 files changed, 169 insertions(+) create mode 100644 packages/renderer/src/__tests__/defaultProps.test.tsx diff --git a/packages/renderer/src/__tests__/defaultProps.test.tsx b/packages/renderer/src/__tests__/defaultProps.test.tsx new file mode 100644 index 000000000..4bb03f5d8 --- /dev/null +++ b/packages/renderer/src/__tests__/defaultProps.test.tsx @@ -0,0 +1,128 @@ +import { describe, it, expect } from 'vitest'; +import { ComponentRegistry } from '../registry'; + +// Import all renderers to ensure they're registered +import '../renderers/basic/div'; +import '../renderers/basic/text'; +import '../renderers/basic/span'; +import '../renderers/basic/separator'; +import '../renderers/form/button'; +import '../renderers/form/input'; +import '../renderers/form/checkbox'; +import '../renderers/form/switch'; +import '../renderers/form/select'; +import '../renderers/form/textarea'; +import '../renderers/form/toggle'; +import '../renderers/form/slider'; +import '../renderers/form/radio-group'; +import '../renderers/form/calendar'; +import '../renderers/form/input-otp'; +import '../renderers/layout/card'; +import '../renderers/layout/tabs'; +import '../renderers/data-display/alert'; +import '../renderers/data-display/avatar'; +import '../renderers/data-display/badge'; +import '../renderers/overlay/dialog'; +import '../renderers/overlay/drawer'; +import '../renderers/overlay/popover'; +import '../renderers/overlay/sheet'; +import '../renderers/overlay/tooltip'; +import '../renderers/overlay/alert-dialog'; +import '../renderers/overlay/dropdown-menu'; +import '../renderers/overlay/context-menu'; +import '../renderers/overlay/hover-card'; +import '../renderers/disclosure/accordion'; +import '../renderers/disclosure/collapsible'; +import '../renderers/complex/table'; +import '../renderers/complex/carousel'; +import '../renderers/complex/resizable'; +import '../renderers/complex/scroll-area'; +import '../renderers/feedback/progress'; +import '../renderers/feedback/skeleton'; +import '../renderers/feedback/toaster'; + +describe('@object-ui/renderer - Default Props', () => { + const componentsRequiringDefaults = [ + 'div', 'text', 'span', 'separator', + 'button', 'input', 'checkbox', 'switch', 'select', 'textarea', + 'toggle', 'toggle-group', 'slider', 'radio-group', 'calendar', 'input-otp', + 'card', 'tabs', + 'alert', 'avatar', 'badge', + 'dialog', 'drawer', 'popover', 'sheet', 'tooltip', + 'alert-dialog', 'dropdown-menu', 'context-menu', 'hover-card', + 'accordion', 'collapsible', + 'table', 'carousel', 'resizable', 'scroll-area', + 'progress', 'skeleton', 'toaster' + ]; + + it('should have defaultProps defined for all components', () => { + const missingDefaults: string[] = []; + + componentsRequiringDefaults.forEach(type => { + const config = ComponentRegistry.getConfig(type); + if (!config) { + missingDefaults.push(`${type} (not registered)`); + } else if (!config.defaultProps && !config.defaultChildren) { + missingDefaults.push(`${type} (no defaultProps or defaultChildren)`); + } + }); + + if (missingDefaults.length > 0) { + console.error('Components missing default props:', missingDefaults); + } + + expect(missingDefaults).toHaveLength(0); + }); + + it('should have valid defaultProps for button component', () => { + const config = ComponentRegistry.getConfig('button'); + expect(config).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + expect(config?.defaultProps?.label).toBeDefined(); + expect(typeof config?.defaultProps?.label).toBe('string'); + }); + + it('should have valid defaultProps for input component', () => { + const config = ComponentRegistry.getConfig('input'); + expect(config).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + expect(config?.defaultProps?.label).toBeDefined(); + expect(config?.defaultProps?.placeholder).toBeDefined(); + }); + + it('should have valid defaultProps for card component', () => { + const config = ComponentRegistry.getConfig('card'); + expect(config).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + expect(config?.defaultProps?.title).toBeDefined(); + }); + + it('should have valid defaultProps for table component', () => { + const config = ComponentRegistry.getConfig('table'); + expect(config).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + expect(config?.defaultProps?.columns).toBeDefined(); + expect(Array.isArray(config?.defaultProps?.columns)).toBe(true); + expect(config?.defaultProps?.data).toBeDefined(); + expect(Array.isArray(config?.defaultProps?.data)).toBe(true); + }); + + it('should have valid defaultProps with initial dimensions for div component', () => { + const config = ComponentRegistry.getConfig('div'); + expect(config).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + expect(config?.defaultProps?.className).toBeDefined(); + // Should have some minimum dimensions to avoid collapsing + expect(config?.defaultProps?.className).toContain('min-h'); + }); + + it('should have defaultChildren defined for components that need them', () => { + const componentsWithChildren = ['span', 'accordion', 'tabs', 'collapsible']; + + componentsWithChildren.forEach(type => { + const config = ComponentRegistry.getConfig(type); + expect(config).toBeDefined(); + expect(config?.defaultChildren || config?.defaultProps).toBeDefined(); + }); + }); +}); diff --git a/packages/renderer/src/__tests__/registry.test.tsx b/packages/renderer/src/__tests__/registry.test.tsx index 68950b17c..0b23271b0 100644 --- a/packages/renderer/src/__tests__/registry.test.tsx +++ b/packages/renderer/src/__tests__/registry.test.tsx @@ -97,5 +97,46 @@ describe('@object-ui/renderer - Registry', () => { consoleWarnSpy.mockRestore(); }); + + it('should support defaultProps in component metadata', () => { + const TestComponent: ComponentRenderer = ({ schema }) => ( +
{schema.label}
+ ); + + ComponentRegistry.register('test-with-defaults', TestComponent, { + label: 'Test With Defaults', + defaultProps: { + label: 'Default Label', + variant: 'primary' + } + }); + + const config = ComponentRegistry.getConfig('test-with-defaults'); + expect(config).toBeDefined(); + expect(config?.defaultProps).toBeDefined(); + expect(config?.defaultProps?.label).toBe('Default Label'); + expect(config?.defaultProps?.variant).toBe('primary'); + }); + + it('should support defaultChildren in component metadata', () => { + const TestComponent: ComponentRenderer = ({ schema }) => ( +
{schema.type}
+ ); + + ComponentRegistry.register('test-with-children', TestComponent, { + label: 'Test With Children', + defaultChildren: [ + { type: 'text', content: 'Child 1' }, + { type: 'text', content: 'Child 2' } + ] + }); + + const config = ComponentRegistry.getConfig('test-with-children'); + expect(config).toBeDefined(); + expect(config?.defaultChildren).toBeDefined(); + expect(config?.defaultChildren).toHaveLength(2); + expect(config?.defaultChildren?.[0].type).toBe('text'); + expect(config?.defaultChildren?.[0].content).toBe('Child 1'); + }); }); }); From 76cf5a83de5a33dfb5200677b352e4493ed162a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 14:05:42 +0000 Subject: [PATCH 4/4] Replace Math.random() IDs with static IDs for form components Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com> --- packages/renderer/src/renderers/form/checkbox.tsx | 2 +- packages/renderer/src/renderers/form/input.tsx | 2 +- packages/renderer/src/renderers/form/radio-group.tsx | 2 +- packages/renderer/src/renderers/form/switch.tsx | 2 +- packages/renderer/src/renderers/form/textarea.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/renderer/src/renderers/form/checkbox.tsx b/packages/renderer/src/renderers/form/checkbox.tsx index e4a0be946..bf2dc6e74 100644 --- a/packages/renderer/src/renderers/form/checkbox.tsx +++ b/packages/renderer/src/renderers/form/checkbox.tsx @@ -19,7 +19,7 @@ ComponentRegistry.register('checkbox', ], defaultProps: { label: 'Checkbox label', - id: 'checkbox-' + Math.random().toString(36).substr(2, 9) + id: 'checkbox-field' // Will be made unique by designer's ensureNodeIds } } ); diff --git a/packages/renderer/src/renderers/form/input.tsx b/packages/renderer/src/renderers/form/input.tsx index a176c4609..3aae4b421 100644 --- a/packages/renderer/src/renderers/form/input.tsx +++ b/packages/renderer/src/renderers/form/input.tsx @@ -32,7 +32,7 @@ ComponentRegistry.register('input', label: 'Label', placeholder: 'Enter text...', inputType: 'text', - id: 'input-' + Math.random().toString(36).substr(2, 9) + id: 'input-field' // Will be made unique by designer's ensureNodeIds } } ); diff --git a/packages/renderer/src/renderers/form/radio-group.tsx b/packages/renderer/src/renderers/form/radio-group.tsx index d78142c38..ba4536f76 100644 --- a/packages/renderer/src/renderers/form/radio-group.tsx +++ b/packages/renderer/src/renderers/form/radio-group.tsx @@ -26,7 +26,7 @@ ComponentRegistry.register('radio-group', { name: 'className', type: 'string', label: 'CSS Class' } ], defaultProps: { - id: 'radio-' + Math.random().toString(36).substr(2, 9), + id: 'radio-group', // Will be made unique by designer's ensureNodeIds items: [ { label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }, diff --git a/packages/renderer/src/renderers/form/switch.tsx b/packages/renderer/src/renderers/form/switch.tsx index 6c1c539b3..28b073462 100644 --- a/packages/renderer/src/renderers/form/switch.tsx +++ b/packages/renderer/src/renderers/form/switch.tsx @@ -17,7 +17,7 @@ ComponentRegistry.register('switch', ], defaultProps: { label: 'Switch label', - id: 'switch-' + Math.random().toString(36).substr(2, 9) + id: 'switch-field' // Will be made unique by designer's ensureNodeIds } } ); diff --git a/packages/renderer/src/renderers/form/textarea.tsx b/packages/renderer/src/renderers/form/textarea.tsx index 6fb431e24..b5fe251e3 100644 --- a/packages/renderer/src/renderers/form/textarea.tsx +++ b/packages/renderer/src/renderers/form/textarea.tsx @@ -23,7 +23,7 @@ ComponentRegistry.register('textarea', defaultProps: { label: 'Textarea label', placeholder: 'Enter text here...', - id: 'textarea-' + Math.random().toString(36).substr(2, 9) + id: 'textarea-field' // Will be made unique by designer's ensureNodeIds } } );