Skip to content

Commit 97ae174

Browse files
Copilothotlong
andcommitted
fix: Address code review feedback - optimize performance, improve refresh logic, and add comprehensive test coverage
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 00d17dd commit 97ae174

3 files changed

Lines changed: 414 additions & 25 deletions

File tree

packages/plugin-aggrid/src/ObjectAgGridImpl.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,6 @@ function applyFieldTypeFormatting(colDef: ColDef, field: FieldMetadata): void {
453453
} else {
454454
// Fallback to simple rendering for unsupported types
455455
switch (field.type) {
456-
case 'lookup':
457456
case 'master_detail':
458457
colDef.valueFormatter = (params: any) => {
459458
if (!params.value) return '';
Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
/**
2+
* ObjectUI
3+
* Copyright (c) 2024-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
10+
import type { ICellRendererParams, ICellEditorParams } from 'ag-grid-community';
11+
import type { FieldMetadata } from '@object-ui/types';
12+
import {
13+
FieldWidgetCellRenderer,
14+
FieldWidgetCellEditor,
15+
createFieldCellRenderer,
16+
createFieldCellEditor
17+
} from './field-renderers';
18+
19+
describe('field-renderers', () => {
20+
describe('FieldWidgetCellRenderer', () => {
21+
let renderer: FieldWidgetCellRenderer;
22+
23+
beforeEach(() => {
24+
renderer = new FieldWidgetCellRenderer();
25+
});
26+
27+
afterEach(() => {
28+
renderer.destroy();
29+
});
30+
31+
it('should initialize with a field widget for supported types', () => {
32+
const field: FieldMetadata = {
33+
name: 'testField',
34+
label: 'Test Field',
35+
type: 'text',
36+
};
37+
38+
const params = {
39+
value: 'test value',
40+
field,
41+
} as ICellRendererParams & { field: FieldMetadata };
42+
43+
renderer.init(params);
44+
45+
expect(renderer.eGui).toBeDefined();
46+
expect(renderer.eGui.className).toBe('field-widget-cell');
47+
expect(renderer.root).toBeDefined();
48+
});
49+
50+
it('should initialize with fallback for unsupported types', () => {
51+
const field = {
52+
name: 'testField',
53+
label: 'Test Field',
54+
type: 'unsupported_type',
55+
} as unknown as FieldMetadata;
56+
57+
const params = {
58+
value: 'fallback value',
59+
field,
60+
} as ICellRendererParams & { field: FieldMetadata };
61+
62+
renderer.init(params);
63+
64+
expect(renderer.eGui).toBeDefined();
65+
expect(renderer.eGui.textContent).toBe('fallback value');
66+
expect(renderer.root).toBeNull();
67+
});
68+
69+
it('should handle null values in fallback mode', () => {
70+
const field = {
71+
name: 'testField',
72+
label: 'Test Field',
73+
type: 'unsupported_type',
74+
} as unknown as FieldMetadata;
75+
76+
const params = {
77+
value: null,
78+
field,
79+
} as ICellRendererParams & { field: FieldMetadata };
80+
81+
renderer.init(params);
82+
83+
expect(renderer.eGui.textContent).toBe('');
84+
});
85+
86+
it('should return the GUI element', () => {
87+
const field: FieldMetadata = {
88+
name: 'testField',
89+
label: 'Test Field',
90+
type: 'text',
91+
};
92+
93+
const params = {
94+
value: 'test',
95+
field,
96+
} as ICellRendererParams & { field: FieldMetadata };
97+
98+
renderer.init(params);
99+
const gui = renderer.getGui();
100+
101+
expect(gui).toBe(renderer.eGui);
102+
});
103+
104+
it('should refresh with new value for supported field type', () => {
105+
const field: FieldMetadata = {
106+
name: 'testField',
107+
label: 'Test Field',
108+
type: 'text',
109+
};
110+
111+
const initParams = {
112+
value: 'initial value',
113+
field,
114+
} as ICellRendererParams & { field: FieldMetadata };
115+
116+
renderer.init(initParams);
117+
118+
const refreshParams = {
119+
value: 'updated value',
120+
field,
121+
} as ICellRendererParams & { field: FieldMetadata };
122+
123+
const result = renderer.refresh(refreshParams);
124+
125+
expect(result).toBe(true);
126+
});
127+
128+
it('should refresh with new value for unsupported field type', () => {
129+
const field = {
130+
name: 'testField',
131+
label: 'Test Field',
132+
type: 'unsupported_type',
133+
} as unknown as FieldMetadata;
134+
135+
const initParams = {
136+
value: 'initial value',
137+
field,
138+
} as ICellRendererParams & { field: FieldMetadata };
139+
140+
renderer.init(initParams);
141+
142+
const refreshParams = {
143+
value: 'updated value',
144+
field,
145+
} as ICellRendererParams & { field: FieldMetadata };
146+
147+
const result = renderer.refresh(refreshParams);
148+
149+
expect(result).toBe(true);
150+
expect(renderer.eGui.textContent).toBe('updated value');
151+
});
152+
153+
it('should clean up root on destroy', () => {
154+
const field: FieldMetadata = {
155+
name: 'testField',
156+
label: 'Test Field',
157+
type: 'text',
158+
};
159+
160+
const params = {
161+
value: 'test',
162+
field,
163+
} as ICellRendererParams & { field: FieldMetadata };
164+
165+
renderer.init(params);
166+
167+
const unmountSpy = vi.spyOn(renderer.root!, 'unmount');
168+
renderer.destroy();
169+
170+
expect(unmountSpy).toHaveBeenCalled();
171+
});
172+
173+
it('should handle destroy when root is null', () => {
174+
const field = {
175+
name: 'testField',
176+
label: 'Test Field',
177+
type: 'unsupported_type',
178+
} as unknown as FieldMetadata;
179+
180+
const params = {
181+
value: 'test',
182+
field,
183+
} as ICellRendererParams & { field: FieldMetadata };
184+
185+
renderer.init(params);
186+
187+
// Should not throw
188+
expect(() => renderer.destroy()).not.toThrow();
189+
});
190+
});
191+
192+
describe('FieldWidgetCellEditor', () => {
193+
let editor: FieldWidgetCellEditor;
194+
195+
beforeEach(() => {
196+
editor = new FieldWidgetCellEditor();
197+
});
198+
199+
afterEach(() => {
200+
editor.destroy();
201+
});
202+
203+
it('should initialize with a field widget for supported types', () => {
204+
const field: FieldMetadata = {
205+
name: 'testField',
206+
label: 'Test Field',
207+
type: 'text',
208+
};
209+
210+
const params = {
211+
value: 'initial value',
212+
field,
213+
} as ICellEditorParams & { field: FieldMetadata };
214+
215+
editor.init(params);
216+
217+
expect(editor.eGui).toBeDefined();
218+
expect(editor.eGui.className).toBe('field-widget-editor');
219+
expect(editor.root).toBeDefined();
220+
expect(editor.currentValue).toBe('initial value');
221+
});
222+
223+
it('should initialize with fallback input for unsupported types', () => {
224+
const field = {
225+
name: 'testField',
226+
label: 'Test Field',
227+
type: 'unsupported_type',
228+
} as unknown as FieldMetadata;
229+
230+
const params = {
231+
value: 'fallback value',
232+
field,
233+
} as ICellEditorParams & { field: FieldMetadata };
234+
235+
editor.init(params);
236+
237+
expect(editor.eGui).toBeDefined();
238+
expect(editor.root).toBeNull();
239+
const input = editor.eGui.querySelector('input');
240+
expect(input).toBeDefined();
241+
expect(input?.value).toBe('fallback value');
242+
});
243+
244+
it('should return current value', () => {
245+
const field: FieldMetadata = {
246+
name: 'testField',
247+
label: 'Test Field',
248+
type: 'text',
249+
};
250+
251+
const params = {
252+
value: 'test value',
253+
field,
254+
} as ICellEditorParams & { field: FieldMetadata };
255+
256+
editor.init(params);
257+
258+
expect(editor.getValue()).toBe('test value');
259+
});
260+
261+
it('should return the GUI element', () => {
262+
const field: FieldMetadata = {
263+
name: 'testField',
264+
label: 'Test Field',
265+
type: 'text',
266+
};
267+
268+
const params = {
269+
value: 'test',
270+
field,
271+
} as ICellEditorParams & { field: FieldMetadata };
272+
273+
editor.init(params);
274+
const gui = editor.getGui();
275+
276+
expect(gui).toBe(editor.eGui);
277+
});
278+
279+
it('should return true for popup editors for specific field types', () => {
280+
const popupTypes = ['date', 'datetime', 'select', 'lookup', 'color'] as const;
281+
282+
popupTypes.forEach(type => {
283+
const field: FieldMetadata = {
284+
name: 'testField',
285+
label: 'Test Field',
286+
type,
287+
};
288+
289+
const params = {
290+
value: 'test',
291+
field,
292+
} as ICellEditorParams & { field: FieldMetadata };
293+
294+
const testEditor = new FieldWidgetCellEditor();
295+
testEditor.init(params);
296+
297+
expect(testEditor.isPopup()).toBe(true);
298+
testEditor.destroy();
299+
});
300+
});
301+
302+
it('should return false for popup editors for non-popup field types', () => {
303+
const field: FieldMetadata = {
304+
name: 'testField',
305+
label: 'Test Field',
306+
type: 'text',
307+
};
308+
309+
const params = {
310+
value: 'test',
311+
field,
312+
} as ICellEditorParams & { field: FieldMetadata };
313+
314+
editor.init(params);
315+
316+
expect(editor.isPopup()).toBe(false);
317+
});
318+
319+
it('should clean up root on destroy', () => {
320+
const field: FieldMetadata = {
321+
name: 'testField',
322+
label: 'Test Field',
323+
type: 'text',
324+
};
325+
326+
const params = {
327+
value: 'test',
328+
field,
329+
} as ICellEditorParams & { field: FieldMetadata };
330+
331+
editor.init(params);
332+
333+
const unmountSpy = vi.spyOn(editor.root!, 'unmount');
334+
editor.destroy();
335+
336+
expect(unmountSpy).toHaveBeenCalled();
337+
});
338+
});
339+
340+
describe('createFieldCellRenderer', () => {
341+
it('should create a renderer class with field metadata', () => {
342+
const field: FieldMetadata = {
343+
name: 'testField',
344+
label: 'Test Field',
345+
type: 'text',
346+
};
347+
348+
const RendererClass = createFieldCellRenderer(field);
349+
const renderer = new RendererClass();
350+
351+
const params = {
352+
value: 'test value',
353+
} as ICellRendererParams;
354+
355+
renderer.init(params);
356+
357+
expect(renderer.eGui).toBeDefined();
358+
renderer.destroy();
359+
});
360+
});
361+
362+
describe('createFieldCellEditor', () => {
363+
it('should create an editor class with field metadata', () => {
364+
const field: FieldMetadata = {
365+
name: 'testField',
366+
label: 'Test Field',
367+
type: 'text',
368+
};
369+
370+
const EditorClass = createFieldCellEditor(field);
371+
const editor = new EditorClass();
372+
373+
const params = {
374+
value: 'test value',
375+
} as ICellEditorParams;
376+
377+
editor.init(params);
378+
379+
expect(editor.eGui).toBeDefined();
380+
editor.destroy();
381+
});
382+
});
383+
});

0 commit comments

Comments
 (0)