@@ -1284,4 +1284,180 @@ describe('ViewConfigPanel', () => {
12841284 fireEvent . click ( screen . getByText ( 'console.objectView.sortBy' ) ) ;
12851285 expect ( screen . queryByTestId ( 'inline-sort-builder' ) ) . not . toBeInTheDocument ( ) ;
12861286 } ) ;
1287+
1288+ // ── Column selector sub-panel tests ──
1289+
1290+ it ( 'shows selected columns in order with move up/down buttons' , ( ) => {
1291+ render (
1292+ < ViewConfigPanel
1293+ open = { true }
1294+ onClose = { vi . fn ( ) }
1295+ activeView = { mockActiveView }
1296+ objectDef = { mockObjectDef }
1297+ />
1298+ ) ;
1299+
1300+ // Expand the Fields sub-section
1301+ fireEvent . click ( screen . getByText ( 'console.objectView.fields' ) ) ;
1302+
1303+ // Selected columns section should appear
1304+ expect ( screen . getByTestId ( 'selected-columns' ) ) . toBeInTheDocument ( ) ;
1305+
1306+ // Move buttons should exist for selected columns
1307+ expect ( screen . getByTestId ( 'col-move-up-name' ) ) . toBeInTheDocument ( ) ;
1308+ expect ( screen . getByTestId ( 'col-move-down-name' ) ) . toBeInTheDocument ( ) ;
1309+ expect ( screen . getByTestId ( 'col-move-up-stage' ) ) . toBeInTheDocument ( ) ;
1310+ expect ( screen . getByTestId ( 'col-move-down-stage' ) ) . toBeInTheDocument ( ) ;
1311+ expect ( screen . getByTestId ( 'col-move-up-amount' ) ) . toBeInTheDocument ( ) ;
1312+ expect ( screen . getByTestId ( 'col-move-down-amount' ) ) . toBeInTheDocument ( ) ;
1313+ } ) ;
1314+
1315+ it ( 'disables move-up for first column and move-down for last column' , ( ) => {
1316+ render (
1317+ < ViewConfigPanel
1318+ open = { true }
1319+ onClose = { vi . fn ( ) }
1320+ activeView = { mockActiveView }
1321+ objectDef = { mockObjectDef }
1322+ />
1323+ ) ;
1324+
1325+ fireEvent . click ( screen . getByText ( 'console.objectView.fields' ) ) ;
1326+
1327+ // First column (name) — move up disabled
1328+ expect ( screen . getByTestId ( 'col-move-up-name' ) ) . toBeDisabled ( ) ;
1329+ expect ( screen . getByTestId ( 'col-move-down-name' ) ) . not . toBeDisabled ( ) ;
1330+
1331+ // Last column (amount) — move down disabled
1332+ expect ( screen . getByTestId ( 'col-move-up-amount' ) ) . not . toBeDisabled ( ) ;
1333+ expect ( screen . getByTestId ( 'col-move-down-amount' ) ) . toBeDisabled ( ) ;
1334+ } ) ;
1335+
1336+ it ( 'moves column down and updates draft.columns order' , ( ) => {
1337+ const onViewUpdate = vi . fn ( ) ;
1338+ render (
1339+ < ViewConfigPanel
1340+ open = { true }
1341+ onClose = { vi . fn ( ) }
1342+ activeView = { mockActiveView }
1343+ objectDef = { mockObjectDef }
1344+ onViewUpdate = { onViewUpdate }
1345+ />
1346+ ) ;
1347+
1348+ fireEvent . click ( screen . getByText ( 'console.objectView.fields' ) ) ;
1349+
1350+ // Move "name" down — should swap with "stage"
1351+ fireEvent . click ( screen . getByTestId ( 'col-move-down-name' ) ) ;
1352+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'columns' , [ 'stage' , 'name' , 'amount' ] ) ;
1353+ } ) ;
1354+
1355+ it ( 'moves column up and updates draft.columns order' , ( ) => {
1356+ const onViewUpdate = vi . fn ( ) ;
1357+ render (
1358+ < ViewConfigPanel
1359+ open = { true }
1360+ onClose = { vi . fn ( ) }
1361+ activeView = { mockActiveView }
1362+ objectDef = { mockObjectDef }
1363+ onViewUpdate = { onViewUpdate }
1364+ />
1365+ ) ;
1366+
1367+ fireEvent . click ( screen . getByText ( 'console.objectView.fields' ) ) ;
1368+
1369+ // Move "amount" up — should swap with "stage"
1370+ fireEvent . click ( screen . getByTestId ( 'col-move-up-amount' ) ) ;
1371+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'columns' , [ 'name' , 'amount' , 'stage' ] ) ;
1372+ } ) ;
1373+
1374+ it ( 'saves reordered columns via onSave' , ( ) => {
1375+ const onSave = vi . fn ( ) ;
1376+ render (
1377+ < ViewConfigPanel
1378+ open = { true }
1379+ onClose = { vi . fn ( ) }
1380+ activeView = { mockActiveView }
1381+ objectDef = { mockObjectDef }
1382+ onSave = { onSave }
1383+ />
1384+ ) ;
1385+
1386+ fireEvent . click ( screen . getByText ( 'console.objectView.fields' ) ) ;
1387+
1388+ // Move "name" down
1389+ fireEvent . click ( screen . getByTestId ( 'col-move-down-name' ) ) ;
1390+
1391+ // Footer should appear
1392+ expect ( screen . getByTestId ( 'view-config-footer' ) ) . toBeInTheDocument ( ) ;
1393+
1394+ // Save
1395+ fireEvent . click ( screen . getByTestId ( 'view-config-save' ) ) ;
1396+ expect ( onSave ) . toHaveBeenCalledOnce ( ) ;
1397+ expect ( onSave . mock . calls [ 0 ] [ 0 ] . columns ) . toEqual ( [ 'stage' , 'name' , 'amount' ] ) ;
1398+ } ) ;
1399+
1400+ it ( 'shows unselected fields below selected columns' , ( ) => {
1401+ // Only 'name' and 'stage' are selected, 'amount' is not
1402+ render (
1403+ < ViewConfigPanel
1404+ open = { true }
1405+ onClose = { vi . fn ( ) }
1406+ activeView = { { ...mockActiveView , columns : [ 'name' , 'stage' ] } }
1407+ objectDef = { mockObjectDef }
1408+ />
1409+ ) ;
1410+
1411+ fireEvent . click ( screen . getByText ( 'console.objectView.fields' ) ) ;
1412+
1413+ // 'amount' should have a checkbox but no move buttons
1414+ expect ( screen . getByTestId ( 'col-checkbox-amount' ) ) . toBeInTheDocument ( ) ;
1415+ expect ( screen . getByTestId ( 'col-checkbox-amount' ) ) . not . toBeChecked ( ) ;
1416+ expect ( screen . queryByTestId ( 'col-move-up-amount' ) ) . not . toBeInTheDocument ( ) ;
1417+ expect ( screen . queryByTestId ( 'col-move-down-amount' ) ) . not . toBeInTheDocument ( ) ;
1418+ } ) ;
1419+
1420+ it ( 'adding unselected field appends to columns and shows move buttons' , ( ) => {
1421+ const onViewUpdate = vi . fn ( ) ;
1422+ render (
1423+ < ViewConfigPanel
1424+ open = { true }
1425+ onClose = { vi . fn ( ) }
1426+ activeView = { { ...mockActiveView , columns : [ 'name' , 'stage' ] } }
1427+ objectDef = { mockObjectDef }
1428+ onViewUpdate = { onViewUpdate }
1429+ />
1430+ ) ;
1431+
1432+ fireEvent . click ( screen . getByText ( 'console.objectView.fields' ) ) ;
1433+
1434+ // Click checkbox for 'amount' to add it
1435+ fireEvent . click ( screen . getByTestId ( 'col-checkbox-amount' ) ) ;
1436+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'columns' , [ 'name' , 'stage' , 'amount' ] ) ;
1437+ } ) ;
1438+
1439+ it ( 'reorder triggers onViewUpdate for real-time preview' , ( ) => {
1440+ const onViewUpdate = vi . fn ( ) ;
1441+ render (
1442+ < ViewConfigPanel
1443+ open = { true }
1444+ onClose = { vi . fn ( ) }
1445+ activeView = { mockActiveView }
1446+ objectDef = { mockObjectDef }
1447+ onViewUpdate = { onViewUpdate }
1448+ />
1449+ ) ;
1450+
1451+ fireEvent . click ( screen . getByText ( 'console.objectView.fields' ) ) ;
1452+
1453+ // Move "stage" up
1454+ fireEvent . click ( screen . getByTestId ( 'col-move-up-stage' ) ) ;
1455+ expect ( onViewUpdate ) . toHaveBeenCalledWith ( 'columns' , [ 'stage' , 'name' , 'amount' ] ) ;
1456+
1457+ // Move "stage" down (now at index 0)
1458+ fireEvent . click ( screen . getByTestId ( 'col-move-down-stage' ) ) ;
1459+ // After the first move, state has stage at index 0
1460+ // The second move should operate on the updated state
1461+ expect ( onViewUpdate ) . toHaveBeenCalledTimes ( 2 ) ;
1462+ } ) ;
12871463} ) ;
0 commit comments