@@ -941,3 +941,207 @@ describe('RecordPickerDialog — Column Resize', () => {
941941 expect ( handle . className ) . toContain ( 'cursor-col-resize' ) ;
942942 } ) ;
943943} ) ;
944+
945+ // ------------- RecordPickerDialog — renderFilterBar slot (FilterUI integration) ---
946+
947+ describe ( 'RecordPickerDialog — renderFilterBar slot' , ( ) => {
948+ const basePickerProps = {
949+ open : true ,
950+ onOpenChange : vi . fn ( ) ,
951+ dataSource : mockDataSource as any ,
952+ objectName : 'customers' ,
953+ onSelect : vi . fn ( ) ,
954+ } ;
955+
956+ it ( 'calls renderFilterBar with correct props when provided' , async ( ) => {
957+ mockDataSource . find . mockResolvedValue ( { data : [ ] , total : 0 } ) ;
958+
959+ const renderFilterBar = vi . fn ( ) . mockReturnValue (
960+ < div data-testid = "custom-filter-bar" > Custom FilterUI</ div > ,
961+ ) ;
962+
963+ render (
964+ < RecordPickerDialog
965+ { ...basePickerProps }
966+ filterColumns = { [
967+ { field : 'status' , label : 'Status' , type : 'select' , options : [ { label : 'Active' , value : 'active' } ] } ,
968+ ] }
969+ renderFilterBar = { renderFilterBar }
970+ /> ,
971+ ) ;
972+
973+ await waitFor ( ( ) => {
974+ expect ( screen . getByTestId ( 'custom-filter-bar' ) ) . toBeInTheDocument ( ) ;
975+ } ) ;
976+
977+ // renderFilterBar should have been called with FilterBarProps
978+ expect ( renderFilterBar ) . toHaveBeenCalledWith (
979+ expect . objectContaining ( {
980+ filterColumns : expect . arrayContaining ( [
981+ expect . objectContaining ( { field : 'status' , type : 'select' } ) ,
982+ ] ) ,
983+ values : { } ,
984+ onChange : expect . any ( Function ) ,
985+ onClear : expect . any ( Function ) ,
986+ activeCount : 0 ,
987+ } ) ,
988+ ) ;
989+ } ) ;
990+
991+ it ( 'hides built-in filter bar when renderFilterBar is provided' , async ( ) => {
992+ mockDataSource . find . mockResolvedValue ( { data : [ ] , total : 0 } ) ;
993+
994+ render (
995+ < RecordPickerDialog
996+ { ...basePickerProps }
997+ filterColumns = { [ { field : 'name' , label : 'Name' , type : 'text' } ] }
998+ renderFilterBar = { ( ) => < div data-testid = "external-filter" > External</ div > }
999+ /> ,
1000+ ) ;
1001+
1002+ await waitFor ( ( ) => {
1003+ expect ( screen . getByTestId ( 'external-filter' ) ) . toBeInTheDocument ( ) ;
1004+ } ) ;
1005+
1006+ // Built-in filter panel toggle button should NOT be present
1007+ expect ( screen . queryByTestId ( 'record-picker-filter-panel' ) ) . not . toBeInTheDocument ( ) ;
1008+ } ) ;
1009+ } ) ;
1010+
1011+ // ------------- RecordPickerDialog — renderGrid slot (ObjectGrid reuse) ---
1012+
1013+ describe ( 'RecordPickerDialog — renderGrid slot' , ( ) => {
1014+ const basePickerProps = {
1015+ open : true ,
1016+ onOpenChange : vi . fn ( ) ,
1017+ dataSource : mockDataSource as any ,
1018+ objectName : 'customers' ,
1019+ onSelect : vi . fn ( ) ,
1020+ } ;
1021+
1022+ it ( 'renders external grid component via renderGrid slot' , async ( ) => {
1023+ mockDataSource . find . mockResolvedValue ( {
1024+ data : [ { id : '1' , name : 'Acme Corp' } ] ,
1025+ total : 1 ,
1026+ } ) ;
1027+
1028+ const renderGrid = vi . fn ( ) . mockReturnValue (
1029+ < div data-testid = "custom-grid" > Custom ObjectGrid</ div > ,
1030+ ) ;
1031+
1032+ render (
1033+ < RecordPickerDialog
1034+ { ...basePickerProps }
1035+ columns = { [ 'name' ] }
1036+ renderGrid = { renderGrid }
1037+ /> ,
1038+ ) ;
1039+
1040+ await waitFor ( ( ) => {
1041+ expect ( screen . getByTestId ( 'record-picker-grid-slot' ) ) . toBeInTheDocument ( ) ;
1042+ expect ( screen . getByTestId ( 'custom-grid' ) ) . toBeInTheDocument ( ) ;
1043+ } ) ;
1044+
1045+ // renderGrid should have been called with grid slot props
1046+ expect ( renderGrid ) . toHaveBeenCalledWith (
1047+ expect . objectContaining ( {
1048+ columns : expect . arrayContaining ( [
1049+ expect . objectContaining ( { field : 'name' } ) ,
1050+ ] ) ,
1051+ records : expect . arrayContaining ( [
1052+ expect . objectContaining ( { id : '1' , name : 'Acme Corp' } ) ,
1053+ ] ) ,
1054+ loading : false ,
1055+ totalCount : 1 ,
1056+ currentPage : 1 ,
1057+ pageSize : 10 ,
1058+ sortField : null ,
1059+ sortDirection : 'asc' ,
1060+ onSort : expect . any ( Function ) ,
1061+ onPageChange : expect . any ( Function ) ,
1062+ onRowClick : expect . any ( Function ) ,
1063+ isSelected : expect . any ( Function ) ,
1064+ multiple : false ,
1065+ idField : 'id' ,
1066+ } ) ,
1067+ ) ;
1068+ } ) ;
1069+
1070+ it ( 'hides built-in table when renderGrid is provided' , async ( ) => {
1071+ mockDataSource . find . mockResolvedValue ( {
1072+ data : [ { id : '1' , name : 'Acme Corp' } ] ,
1073+ total : 1 ,
1074+ } ) ;
1075+
1076+ render (
1077+ < RecordPickerDialog
1078+ { ...basePickerProps }
1079+ columns = { [ 'name' ] }
1080+ renderGrid = { ( ) => < div > Custom Grid</ div > }
1081+ /> ,
1082+ ) ;
1083+
1084+ await waitFor ( ( ) => {
1085+ expect ( screen . getByText ( 'Custom Grid' ) ) . toBeInTheDocument ( ) ;
1086+ } ) ;
1087+
1088+ // Built-in table should NOT be present
1089+ expect ( screen . queryByRole ( 'grid' ) ) . not . toBeInTheDocument ( ) ;
1090+ // Built-in pagination should NOT be present
1091+ expect ( screen . queryByTestId ( 'record-picker-pagination' ) ) . not . toBeInTheDocument ( ) ;
1092+ } ) ;
1093+ } ) ;
1094+
1095+ // ------------- RecordPickerDialog — Auto-generated filterColumns from lookupFilters ---
1096+
1097+ describe ( 'RecordPickerDialog — Auto-generated filter bar from lookupFilters' , ( ) => {
1098+ const basePickerProps = {
1099+ open : true ,
1100+ onOpenChange : vi . fn ( ) ,
1101+ dataSource : mockDataSource as any ,
1102+ objectName : 'customers' ,
1103+ onSelect : vi . fn ( ) ,
1104+ } ;
1105+
1106+ it ( 'auto-generates filter bar from lookupFilters when no filterColumns given' , async ( ) => {
1107+ mockDataSource . find . mockResolvedValue ( { data : [ ] , total : 0 } ) ;
1108+
1109+ render (
1110+ < RecordPickerDialog
1111+ { ...basePickerProps }
1112+ lookupFilters = { [
1113+ { field : 'status' , operator : 'eq' , value : 'active' } ,
1114+ { field : 'amount' , operator : 'gte' , value : 100 } ,
1115+ ] }
1116+ /> ,
1117+ ) ;
1118+
1119+ await waitFor ( ( ) => {
1120+ // Filter bar should appear because lookupFilters auto-generate filterColumns
1121+ expect ( screen . getByTestId ( 'record-picker-filter-bar' ) ) . toBeInTheDocument ( ) ;
1122+ } ) ;
1123+ } ) ;
1124+
1125+ it ( 'prefers explicit filterColumns over auto-generated ones' , async ( ) => {
1126+ mockDataSource . find . mockResolvedValue ( { data : [ ] , total : 0 } ) ;
1127+
1128+ const renderFilterBar = vi . fn ( ) . mockReturnValue ( < div > Filters</ div > ) ;
1129+
1130+ render (
1131+ < RecordPickerDialog
1132+ { ...basePickerProps }
1133+ lookupFilters = { [ { field : 'status' , operator : 'eq' , value : 'active' } ] }
1134+ filterColumns = { [ { field : 'custom_field' , label : 'Custom' , type : 'text' } ] }
1135+ renderFilterBar = { renderFilterBar }
1136+ /> ,
1137+ ) ;
1138+
1139+ await waitFor ( ( ) => {
1140+ expect ( renderFilterBar ) . toHaveBeenCalled ( ) ;
1141+ } ) ;
1142+
1143+ // Should use the explicit filterColumns, not auto-generated ones
1144+ const calledProps = renderFilterBar . mock . calls [ 0 ] [ 0 ] ;
1145+ expect ( calledProps . filterColumns [ 0 ] . field ) . toBe ( 'custom_field' ) ;
1146+ } ) ;
1147+ } ) ;
0 commit comments