@@ -352,3 +352,215 @@ describe('ListColumn[] with DataSource objectDef merge', () => {
352352 expect ( wonBadge ) . toHaveClass ( 'bg-blue-100' ) ;
353353 } ) ;
354354} ) ;
355+
356+ // =========================================================================
357+ // Inline data + DataSource: schema should be fetched for type-aware rendering
358+ // (Regression test for: inline data skipping objectSchema load)
359+ // =========================================================================
360+ describe ( 'Inline data with DataSource schema fetch' , ( ) => {
361+ it ( 'should fetch objectSchema even when data is inline (provider: value)' , async ( ) => {
362+ const mockDataSource = createMockDataSource ( opportunitySchema , [ ] ) ;
363+
364+ const schema : any = {
365+ type : 'object-grid' as const ,
366+ objectName : 'opportunity' ,
367+ data : { provider : 'value' , items : opportunityData } ,
368+ columns : [ 'name' , 'amount' , 'stage' , 'close_date' , 'probability' ] ,
369+ } ;
370+
371+ render (
372+ < ActionProvider >
373+ < ObjectGrid schema = { schema } dataSource = { mockDataSource } />
374+ </ ActionProvider >
375+ ) ;
376+
377+ // Schema should be fetched even with inline data
378+ await waitFor ( ( ) => {
379+ expect ( mockDataSource . getObjectSchema ) . toHaveBeenCalledWith ( 'opportunity' ) ;
380+ } ) ;
381+
382+ // Data should render
383+ await waitFor ( ( ) => {
384+ expect ( screen . getByText ( 'Enterprise License' ) ) . toBeInTheDocument ( ) ;
385+ } ) ;
386+
387+ // Amount should be formatted as currency using objectSchema field type
388+ await waitFor ( ( ) => {
389+ expect ( screen . getByText ( '$150,000.00' ) ) . toBeInTheDocument ( ) ;
390+ } ) ;
391+
392+ // Stage should render with labels from objectSchema options
393+ await waitFor ( ( ) => {
394+ expect ( screen . getByText ( 'Closed Won' ) ) . toBeInTheDocument ( ) ;
395+ } ) ;
396+
397+ // Stage badge should have color from objectSchema
398+ const closedWonBadge = screen . getByText ( 'Closed Won' ) ;
399+ expect ( closedWonBadge ) . toHaveClass ( 'bg-green-100' ) ;
400+
401+ // find() should NOT have been called (data is inline)
402+ expect ( mockDataSource . find ) . not . toHaveBeenCalled ( ) ;
403+ } ) ;
404+
405+ it ( 'should use objectSchema labels for column headers with inline data' , async ( ) => {
406+ const mockDataSource = createMockDataSource ( opportunitySchema , [ ] ) ;
407+
408+ const schema : any = {
409+ type : 'object-grid' as const ,
410+ objectName : 'opportunity' ,
411+ data : { provider : 'value' , items : opportunityData } ,
412+ columns : [ 'name' , 'amount' , 'close_date' ] ,
413+ } ;
414+
415+ render (
416+ < ActionProvider >
417+ < ObjectGrid schema = { schema } dataSource = { mockDataSource } />
418+ </ ActionProvider >
419+ ) ;
420+
421+ await waitFor ( ( ) => {
422+ expect ( mockDataSource . getObjectSchema ) . toHaveBeenCalledWith ( 'opportunity' ) ;
423+ } ) ;
424+
425+ // Headers should use objectSchema labels (not raw field names)
426+ await waitFor ( ( ) => {
427+ expect ( screen . getByText ( 'Opportunity Name' ) ) . toBeInTheDocument ( ) ;
428+ } ) ;
429+ expect ( screen . getByText ( 'Amount' ) ) . toBeInTheDocument ( ) ;
430+ expect ( screen . getByText ( 'Close Date' ) ) . toBeInTheDocument ( ) ;
431+ } ) ;
432+
433+ it ( 'should render lookup/select fields with CellRenderers when inline data + objectSchema' , async ( ) => {
434+ const lookupSchema = {
435+ name : 'order' ,
436+ fields : {
437+ name : { name : 'name' , type : 'text' , label : 'Order' } ,
438+ status : {
439+ name : 'status' , type : 'select' , label : 'Status' ,
440+ options : [
441+ { value : 'pending' , label : 'Pending' , color : 'yellow' } ,
442+ { value : 'shipped' , label : 'Shipped' , color : 'blue' } ,
443+ { value : 'delivered' , label : 'Delivered' , color : 'green' } ,
444+ ] ,
445+ } ,
446+ assigned_to : { name : 'assigned_to' , type : 'lookup' , label : 'Assigned To' } ,
447+ } ,
448+ } ;
449+
450+ const inlineOrderData = [
451+ { _id : 'o1' , name : 'Order 001' , status : 'pending' , assigned_to : 'Alice' } ,
452+ { _id : 'o2' , name : 'Order 002' , status : 'shipped' , assigned_to : 'Bob' } ,
453+ { _id : 'o3' , name : 'Order 003' , status : 'delivered' , assigned_to : 'Charlie' } ,
454+ ] ;
455+
456+ const mockDataSource = createMockDataSource ( lookupSchema , [ ] ) ;
457+
458+ const schema : any = {
459+ type : 'object-grid' as const ,
460+ objectName : 'order' ,
461+ data : { provider : 'value' , items : inlineOrderData } ,
462+ columns : [ 'name' , 'status' , 'assigned_to' ] ,
463+ } ;
464+
465+ render (
466+ < ActionProvider >
467+ < ObjectGrid schema = { schema } dataSource = { mockDataSource } />
468+ </ ActionProvider >
469+ ) ;
470+
471+ // Schema should be fetched
472+ await waitFor ( ( ) => {
473+ expect ( mockDataSource . getObjectSchema ) . toHaveBeenCalledWith ( 'order' ) ;
474+ } ) ;
475+
476+ // Status should render with select renderer (colored badges)
477+ await waitFor ( ( ) => {
478+ expect ( screen . getByText ( 'Pending' ) ) . toBeInTheDocument ( ) ;
479+ } ) ;
480+
481+ const pendingBadge = screen . getByText ( 'Pending' ) ;
482+ expect ( pendingBadge ) . toHaveClass ( 'bg-yellow-100' ) ;
483+
484+ const shippedBadge = screen . getByText ( 'Shipped' ) ;
485+ expect ( shippedBadge ) . toHaveClass ( 'bg-blue-100' ) ;
486+
487+ // find() should NOT be called
488+ expect ( mockDataSource . find ) . not . toHaveBeenCalled ( ) ;
489+ } ) ;
490+
491+ it ( 'should enrich legacy inline data fallback (no columns) with objectSchema' , async ( ) => {
492+ const mockDataSource = createMockDataSource ( opportunitySchema , [ ] ) ;
493+
494+ const schema : any = {
495+ type : 'object-grid' as const ,
496+ objectName : 'opportunity' ,
497+ data : { provider : 'value' , items : opportunityData } ,
498+ // No columns specified — should auto-derive from data keys + objectSchema
499+ } ;
500+
501+ render (
502+ < ActionProvider >
503+ < ObjectGrid schema = { schema } dataSource = { mockDataSource } />
504+ </ ActionProvider >
505+ ) ;
506+
507+ // Schema should still be fetched
508+ await waitFor ( ( ) => {
509+ expect ( mockDataSource . getObjectSchema ) . toHaveBeenCalledWith ( 'opportunity' ) ;
510+ } ) ;
511+
512+ // Data should render with objectSchema-enriched columns
513+ await waitFor ( ( ) => {
514+ expect ( screen . getByText ( 'Enterprise License' ) ) . toBeInTheDocument ( ) ;
515+ } ) ;
516+
517+ // Amount should be formatted (objectSchema type: currency)
518+ await waitFor ( ( ) => {
519+ expect ( screen . getByText ( '$150,000.00' ) ) . toBeInTheDocument ( ) ;
520+ } ) ;
521+
522+ // find() should NOT be called
523+ expect ( mockDataSource . find ) . not . toHaveBeenCalled ( ) ;
524+ } ) ;
525+
526+ it ( 'should enrich ListColumn[] with objectSchema types when inline data + dataSource' , async ( ) => {
527+ const mockDataSource = createMockDataSource ( opportunitySchema , [ ] ) ;
528+
529+ const schema : any = {
530+ type : 'object-grid' as const ,
531+ objectName : 'opportunity' ,
532+ data : { provider : 'value' , items : opportunityData } ,
533+ // ListColumn[] without explicit type — objectSchema should provide types
534+ columns : [
535+ { field : 'name' , label : 'Name' } ,
536+ { field : 'stage' , label : 'Stage' } ,
537+ { field : 'amount' , label : 'Amount' } ,
538+ ] ,
539+ } ;
540+
541+ render (
542+ < ActionProvider >
543+ < ObjectGrid schema = { schema } dataSource = { mockDataSource } />
544+ </ ActionProvider >
545+ ) ;
546+
547+ // Schema should be fetched
548+ await waitFor ( ( ) => {
549+ expect ( mockDataSource . getObjectSchema ) . toHaveBeenCalledWith ( 'opportunity' ) ;
550+ } ) ;
551+
552+ // Stage should render with colored badge from objectSchema select options
553+ await waitFor ( ( ) => {
554+ const closedWonBadge = screen . getByText ( 'Closed Won' ) ;
555+ expect ( closedWonBadge ) . toHaveClass ( 'bg-green-100' ) ;
556+ } ) ;
557+
558+ // Amount should be formatted as currency from objectSchema type
559+ await waitFor ( ( ) => {
560+ expect ( screen . getByText ( '$150,000.00' ) ) . toBeInTheDocument ( ) ;
561+ } ) ;
562+
563+ // find() should NOT be called
564+ expect ( mockDataSource . find ) . not . toHaveBeenCalled ( ) ;
565+ } ) ;
566+ } ) ;
0 commit comments