Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions packages/renderer/src/__tests__/defaultProps.test.tsx
Original file line number Diff line number Diff line change
@@ -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();
});
});
});
41 changes: 41 additions & 0 deletions packages/renderer/src/__tests__/registry.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,46 @@ describe('@object-ui/renderer - Registry', () => {

consoleWarnSpy.mockRestore();
});

it('should support defaultProps in component metadata', () => {
const TestComponent: ComponentRenderer = ({ schema }) => (
<div>{schema.label}</div>
);

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 }) => (
<div>{schema.type}</div>
);

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');
});
});
});
5 changes: 4 additions & 1 deletion packages/renderer/src/renderers/basic/div.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]'
}
}
);
6 changes: 5 additions & 1 deletion packages/renderer/src/renderers/basic/separator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ ComponentRegistry.register('separator',
label: 'Orientation'
},
{ name: 'className', type: 'string', label: 'CSS Class' }
]
],
defaultProps: {
orientation: 'horizontal',
className: 'my-4'
}
}
);
6 changes: 6 additions & 0 deletions packages/renderer/src/renderers/basic/span.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
]
}
);
5 changes: 4 additions & 1 deletion packages/renderer/src/renderers/basic/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ ComponentRegistry.register('text',
label: 'Text',
inputs: [
{ name: 'content', type: 'string', label: 'Content', required: true }
]
],
defaultProps: {
content: 'Text content'
}
}
);
12 changes: 11 additions & 1 deletion packages/renderer/src/renderers/complex/carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
);
12 changes: 11 additions & 1 deletion packages/renderer/src/renderers/complex/resizable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
);
11 changes: 10 additions & 1 deletion packages/renderer/src/renderers/complex/scroll-area.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
);
15 changes: 14 additions & 1 deletion packages/renderer/src/renderers/complex/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
]
}
}
);
7 changes: 6 additions & 1 deletion packages/renderer/src/renderers/data-display/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
);
6 changes: 5 additions & 1 deletion packages/renderer/src/renderers/data-display/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
);
6 changes: 5 additions & 1 deletion packages/renderer/src/renderers/data-display/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ ComponentRegistry.register('badge',
label: 'Variant'
},
{ name: 'className', type: 'string', label: 'CSS Class' }
]
],
defaultProps: {
label: 'Badge',
variant: 'default'
}
}
);
24 changes: 23 additions & 1 deletion packages/renderer/src/renderers/disclosure/accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
);
7 changes: 6 additions & 1 deletion packages/renderer/src/renderers/disclosure/collapsible.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
);
6 changes: 5 additions & 1 deletion packages/renderer/src/renderers/feedback/progress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
}
);
Loading