Skip to content

Commit 65987eb

Browse files
committed
Refactor ObjectView to integrate ListView and update plugin registrations; add tests for ListView rendering
1 parent 5c24e30 commit 65987eb

File tree

4 files changed

+164
-89
lines changed

4 files changed

+164
-89
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { describe, it, expect, vi } from 'vitest';
2+
import { render, screen, fireEvent } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
4+
import { ObjectView } from '../components/ObjectView';
5+
import { MemoryRouter, Route, Routes } from 'react-router-dom';
6+
7+
// Mock child plugins to focus on ObjectView logic
8+
vi.mock('@object-ui/plugin-grid', () => ({
9+
ObjectGrid: (props: any) => <div data-testid="object-grid">Grid View: {props.schema.objectName} (Filter: {JSON.stringify(props.schema.filter)})</div>
10+
}));
11+
12+
vi.mock('@object-ui/plugin-kanban', () => ({
13+
ObjectKanban: () => <div data-testid="object-kanban">Kanban View</div>
14+
}));
15+
16+
vi.mock('@object-ui/plugin-calendar', () => ({
17+
ObjectCalendar: () => <div data-testid="object-calendar">Calendar View</div>
18+
}));
19+
20+
vi.mock('@object-ui/plugin-list', () => ({
21+
ListView: (props: any) => (
22+
<div data-testid="list-view">
23+
ListView for {props.schema.objectName}
24+
<button onClick={() => console.log('Filter clicked')}>Filter</button>
25+
</div>
26+
)
27+
}));
28+
29+
// Mock UI Components
30+
vi.mock('@object-ui/components', async () => {
31+
return {
32+
Button: ({ children, onClick, className }: any) => (
33+
<button onClick={onClick} className={className} data-testid="ui-button">
34+
{children}
35+
</button>
36+
),
37+
Empty: ({ children }: any) => <div>{children}</div>,
38+
EmptyTitle: ({ children }: any) => <div>{children}</div>,
39+
EmptyDescription: ({ children }: any) => <div>{children}</div>
40+
};
41+
});
42+
43+
// Mock Icons
44+
vi.mock('lucide-react', () => ({
45+
Plus: () => <span>+</span>,
46+
Calendar: () => <span>Cal</span>,
47+
Kanban: () => <span>Kan</span>,
48+
Table: () => <span>Tab</span>,
49+
AlignLeft: () => <span>Gantt</span>,
50+
Filter: () => <span>FilterIcon</span>,
51+
X: () => <span>X</span>
52+
}));
53+
54+
describe('ObjectView Console Features', () => {
55+
56+
const mockObjects = [
57+
{
58+
name: 'todo_task',
59+
label: 'Todo Task',
60+
fields: { name: { type: 'text' }, status: { type: 'select' } },
61+
list_views: {
62+
all: { label: 'All Tasks', type: 'grid' }
63+
}
64+
}
65+
];
66+
67+
const mockDataSource = {
68+
find: vi.fn(),
69+
findOne: vi.fn(),
70+
create: vi.fn(),
71+
update: vi.fn(),
72+
delete: vi.fn()
73+
};
74+
75+
const renderObjectView = (objectName = 'todo_task') => {
76+
return render(
77+
<MemoryRouter initialEntries={[`/${objectName}`]}>
78+
<Routes>
79+
<Route path="/:objectName" element={
80+
<ObjectView
81+
dataSource={mockDataSource}
82+
objects={mockObjects}
83+
onEdit={vi.fn()}
84+
/>
85+
} />
86+
</Routes>
87+
</MemoryRouter>
88+
);
89+
};
90+
91+
it('renders ListView', () => {
92+
renderObjectView();
93+
94+
expect(screen.getByText('Todo Task')).toBeInTheDocument();
95+
// Verify ListView is rendered instead of direct Filter button
96+
expect(screen.getByTestId('list-view')).toBeInTheDocument();
97+
expect(screen.getByText('ListView for todo_task')).toBeInTheDocument();
98+
});
99+
100+
// Removed Filter button test as functionality moved to ListView plugin
101+
});

apps/console/src/components/ObjectView.tsx

Lines changed: 58 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { useState, useMemo } from 'react';
22
import { useParams, useSearchParams } from 'react-router-dom';
3-
import { ObjectGrid } from '@object-ui/plugin-grid';
4-
import { ObjectKanban } from '@object-ui/plugin-kanban';
5-
import { ObjectCalendar } from '@object-ui/plugin-calendar';
63
import { ObjectGantt } from '@object-ui/plugin-gantt';
4+
import { ListView } from '@object-ui/plugin-list';
5+
// Import plugins for side-effects (registration)
6+
import '@object-ui/plugin-grid';
7+
import '@object-ui/plugin-kanban';
8+
import '@object-ui/plugin-calendar';
79
import { Button, Empty, EmptyTitle, EmptyDescription } from '@object-ui/components';
810
import { Plus, Calendar as CalendarIcon, Kanban as KanbanIcon, Table as TableIcon, AlignLeft } from 'lucide-react';
11+
import type { ListViewSchema } from '@object-ui/types';
912

1013
export function ObjectView({ dataSource, objects, onEdit }: any) {
1114
const { objectName } = useParams();
@@ -85,85 +88,59 @@ export function ObjectView({ dataSource, objects, onEdit }: any) {
8588
onRowClick: (record: any) => onEdit(record), // Default to edit on click
8689
};
8790

88-
switch (activeView.type) {
89-
case 'kanban':
90-
return (
91-
<ObjectKanban
92-
key={key}
93-
{...commonProps}
94-
schema={{
95-
type: 'kanban',
96-
objectName: objectDef.name,
97-
groupBy: activeView.groupBy || 'status',
98-
columns: activeView.columns,
99-
cardTitle: objectDef.titleField || 'name', // Default title field
100-
cardFields: activeView.columns
101-
}}
102-
{...interactionProps}
103-
/>
104-
);
105-
case 'calendar':
106-
return (
107-
<ObjectCalendar
108-
key={key}
109-
{...commonProps}
110-
schema={{
111-
type: 'calendar',
112-
objectName: objectDef.name,
113-
dateField: activeView.dateField || 'due_date',
114-
endField: activeView.endField,
91+
// Gantt is not yet supported by ListView, handle separately
92+
if (activeView.type === 'gantt') {
93+
return (
94+
<ObjectGantt
95+
key={key}
96+
{...commonProps}
97+
schema={{
98+
type: 'object-grid',
99+
objectName: objectDef.name,
100+
gantt: {
101+
startDateField: activeView.startDateField || 'start_date',
102+
endDateField: activeView.endDateField || 'end_date',
115103
titleField: activeView.titleField || 'name',
104+
progressField: activeView.progressField || 'progress',
105+
dependenciesField: activeView.dependenciesField,
116106
colorField: activeView.colorField,
117-
}}
118-
{...interactionProps}
119-
/>
120-
);
121-
case 'gantt':
122-
return (
123-
<ObjectGantt
124-
key={key}
125-
{...commonProps}
126-
schema={{
127-
type: 'object-grid',
128-
objectName: objectDef.name,
129-
// Gantt config is read by ObjectGantt via getGanttConfig helper
130-
// TypeScript workaround: gantt property not in ObjectGridSchema but supported by implementation
131-
gantt: {
132-
startDateField: activeView.startDateField || 'start_date',
133-
endDateField: activeView.endDateField || 'end_date',
134-
titleField: activeView.titleField || 'name',
135-
progressField: activeView.progressField || 'progress',
136-
dependenciesField: activeView.dependenciesField,
137-
colorField: activeView.colorField,
138-
}
139-
} as any}
140-
{...interactionProps}
141-
/>
142-
);
143-
case 'grid':
144-
default:
145-
return (
146-
<ObjectGrid
147-
key={key}
148-
{...commonProps}
149-
schema={{
150-
type: 'object-grid',
151-
objectName: objectDef.name,
152-
filterable: true,
153-
columns: getGridColumns(activeView),
154-
filter: activeView.filter,
155-
sort: activeView.sort
156-
}}
157-
{...interactionProps}
158-
onDelete={async (record: any) => {
159-
if (confirm(`Delete record?`)) {
160-
await dataSource.delete(objectName, record.id || record._id);
161-
setRefreshKey(k => k + 1);
162-
}
163-
}}
164-
/>
165-
);
107+
}
108+
} as any}
109+
{...interactionProps}
110+
/>
111+
);
166112
}
113+
114+
// Use standard ListView for supported types
115+
const listViewSchema: ListViewSchema = {
116+
type: 'list-view',
117+
objectName: objectDef.name,
118+
viewType: activeView.type,
119+
fields: activeView.columns,
120+
filters: activeView.filter,
121+
sort: activeView.sort,
122+
options: {
123+
kanban: {
124+
groupField: activeView.groupBy || 'status',
125+
titleField: objectDef.titleField || 'name',
126+
cardFields: activeView.columns
127+
},
128+
calendar: {
129+
startDateField: activeView.dateField || 'due_date',
130+
endDateField: activeView.endField,
131+
titleField: activeView.titleField || 'name',
132+
}
133+
}
134+
};
135+
136+
return (
137+
<ListView
138+
key={key}
139+
schema={listViewSchema}
140+
className="h-full"
141+
{...interactionProps}
142+
/>
143+
);
167144
};
168145

169146
return (
@@ -217,12 +194,7 @@ export function ObjectView({ dataSource, objects, onEdit }: any) {
217194

218195
{/* Right: View Options (Placeholder for now) */}
219196
<div className="flex items-center gap-2 hidden md:flex">
220-
<Button variant="ghost" size="sm" className="h-8 text-muted-foreground">
221-
<span className="text-xs">Filter</span>
222-
</Button>
223-
<Button variant="ghost" size="sm" className="h-8 text-muted-foreground">
224-
<span className="text-xs">Sort</span>
225-
</Button>
197+
{/* Filter/Sort are handled by ListView now */}
226198
</div>
227199
</div>
228200

@@ -234,4 +206,4 @@ export function ObjectView({ dataSource, objects, onEdit }: any) {
234206
</div>
235207
</div>
236208
);
237-
}
209+
}

packages/plugin-grid/src/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ export type { ObjectGridProps } from './ObjectGrid';
1717
export type { VirtualGridProps, VirtualGridColumn } from './VirtualGrid';
1818

1919
// Register object-grid component
20-
const ObjectGridRenderer: React.FC<{ schema: any }> = ({ schema }) => {
20+
const ObjectGridRenderer: React.FC<{ schema: any; [key: string]: any }> = ({ schema, ...props }) => {
2121
const { dataSource } = useSchemaContext() || {};
22-
return <ObjectGrid schema={schema} dataSource={dataSource} />;
22+
return <ObjectGrid schema={schema} dataSource={dataSource} {...props} />;
2323
};
2424

2525
ComponentRegistry.register('object-grid', ObjectGridRenderer, {

packages/plugin-list/src/ListView.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface ListViewProps {
2020
onFilterChange?: (filters: any) => void;
2121
onSortChange?: (sort: any) => void;
2222
onSearchChange?: (search: string) => void;
23+
[key: string]: any;
2324
}
2425

2526
export const ListView: React.FC<ListViewProps> = ({
@@ -29,6 +30,7 @@ export const ListView: React.FC<ListViewProps> = ({
2930
onFilterChange: _onFilterChange,
3031
onSortChange,
3132
onSearchChange,
33+
...props
3234
}) => {
3335
const [currentView, setCurrentView] = React.useState<ViewType>(
3436
(schema.viewType as ViewType) || 'grid'
@@ -209,7 +211,7 @@ export const ListView: React.FC<ListViewProps> = ({
209211

210212
{/* View Content */}
211213
<div className="flex-1">
212-
<SchemaRenderer schema={viewComponentSchema} />
214+
<SchemaRenderer schema={viewComponentSchema} {...props} />
213215
</div>
214216
</div>
215217
);

0 commit comments

Comments
 (0)