Skip to content

Commit 6c3a801

Browse files
Copilothotlong
andcommitted
fix(fields): support ObjectStack 'reference' convention in LookupField
The ObjectStack backend uses `reference` (not `reference_to`) as the property name for lookup field metadata. The entire codebase uses the pattern `fieldDef.reference_to || fieldDef.reference` to handle both conventions, but LookupField only checked `reference_to`. This caused `referenceTo` to always be `undefined` when using the ObjectStack backend, so `hasDataSource` was `false` and `dataSource.find()` was never called — the dialog stayed empty. Fixes: - fieldMeta unwrapping now detects both `reference_to` and `reference` in the nested field object - referenceTo falls back: `fieldMeta.reference_to || fieldMeta.reference` - Added 2 new tests for the `reference` convention (nested + flat) Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 8230707 commit 6c3a801

2 files changed

Lines changed: 88 additions & 2 deletions

File tree

packages/fields/src/complex-widgets.test.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,89 @@ describe('Complex & Relationship Widgets', () => {
399399
expect(screen.getByText('Order 002')).toBeInTheDocument();
400400
});
401401
});
402+
403+
it('supports ObjectStack "reference" convention (not just "reference_to")', async () => {
404+
// ObjectStack backend uses `reference` instead of `reference_to`
405+
const onChange = vi.fn();
406+
mockDataSource.find.mockResolvedValue({
407+
data: [
408+
{ _id: 'a1', name: 'Acme Corp' },
409+
{ _id: 'a2', name: 'Beta Inc' },
410+
],
411+
total: 2,
412+
});
413+
414+
const wrappedField = {
415+
name: 'account',
416+
label: 'Account',
417+
field: {
418+
name: 'account',
419+
type: 'lookup',
420+
reference: 'account', // ObjectStack convention
421+
},
422+
dataSource: mockDataSource,
423+
} as any;
424+
425+
render(
426+
<LookupField
427+
value={null}
428+
onChange={onChange}
429+
field={wrappedField}
430+
readonly={false}
431+
/>
432+
);
433+
434+
await act(async () => {
435+
fireEvent.click(screen.getByRole('button', { name: /Select/i }));
436+
});
437+
438+
await waitFor(() => {
439+
expect(mockDataSource.find).toHaveBeenCalledWith('account', { $top: 50 });
440+
});
441+
442+
await waitFor(() => {
443+
expect(screen.getByText('Acme Corp')).toBeInTheDocument();
444+
expect(screen.getByText('Beta Inc')).toBeInTheDocument();
445+
});
446+
});
447+
448+
it('supports flat "reference" field without wrapper nesting', async () => {
449+
// When field metadata is flat (no field.field nesting)
450+
const onChange = vi.fn();
451+
mockDataSource.find.mockResolvedValue({
452+
data: [
453+
{ _id: 'p1', name: 'Product A' },
454+
],
455+
total: 1,
456+
});
457+
458+
render(
459+
<LookupField
460+
value={null}
461+
onChange={onChange}
462+
field={{
463+
name: 'product',
464+
label: 'Product',
465+
type: 'lookup',
466+
reference: 'products', // ObjectStack convention, flat field
467+
} as any}
468+
readonly={false}
469+
dataSource={mockDataSource}
470+
/>
471+
);
472+
473+
await act(async () => {
474+
fireEvent.click(screen.getByRole('button', { name: /Select/i }));
475+
});
476+
477+
await waitFor(() => {
478+
expect(mockDataSource.find).toHaveBeenCalledWith('products', { $top: 50 });
479+
});
480+
481+
await waitFor(() => {
482+
expect(screen.getByText('Product A')).toBeInTheDocument();
483+
});
484+
});
402485
});
403486

404487
describe('MasterDetailField', () => {

packages/fields/src/widgets/LookupField.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,10 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
8484
// When rendered via createFieldRenderer wrapper the actual objectSchema field
8585
// metadata (reference_to, display_field, etc.) lives at lookupField.field.
8686
// Unwrap it so lookup-specific properties resolve correctly.
87+
// ObjectStack convention uses `reference` while the types use `reference_to`,
88+
// so we check for both property names.
8789
const innerField = lookupField?.field;
88-
const fieldMeta = (innerField && typeof innerField === 'object' && ('reference_to' in innerField || 'type' in innerField))
90+
const fieldMeta = (innerField && typeof innerField === 'object' && ('reference_to' in innerField || 'reference' in innerField || 'type' in innerField))
8991
? innerField
9092
: lookupField;
9193

@@ -94,7 +96,8 @@ export function LookupField({ value, onChange, field, readonly, ...props }: Fiel
9496
const displayField = fieldMeta?.display_field || fieldMeta?.reference_field || 'name';
9597
const descriptionField: string | undefined = fieldMeta?.description_field;
9698
const idField = fieldMeta?.id_field || '_id';
97-
const referenceTo: string | undefined = fieldMeta?.reference_to;
99+
// ObjectStack convention uses `reference`; types define `reference_to` — support both
100+
const referenceTo: string | undefined = fieldMeta?.reference_to || fieldMeta?.reference;
98101

99102
// Resolve DataSource: explicit prop > field-level > wrapper field > SchemaRendererContext > none
100103
const ctx = useContext(SchemaRendererContext);

0 commit comments

Comments
 (0)