@@ -10,6 +10,7 @@ import type { DrawlistBuilder } from "../../drawlist/index.js";
1010import type { ZrevEvent } from "../../events.js" ;
1111import { type VNode , defineWidget , ui } from "../../index.js" ;
1212import { DEFAULT_TERMINAL_CAPS } from "../../terminalCaps.js" ;
13+ import { createTestRenderer } from "../../testing/index.js" ;
1314import { defaultTheme } from "../../theme/defaultTheme.js" ;
1415import { TOAST_HEIGHT , getToastActionFocusId } from "../../widgets/toast.js" ;
1516import { createApp } from "../createApp.js" ;
@@ -1089,6 +1090,67 @@ describe("WidgetRenderer integration battery", () => {
10891090 assert . deepEqual ( events , [ ] ) ;
10901091 } ) ;
10911092
1093+ test ( "modal actions preserve declared order and click behavior" , ( ) => {
1094+ const backend = createNoopBackend ( ) ;
1095+ const renderer = new WidgetRenderer < void > ( {
1096+ backend,
1097+ requestRender : ( ) => { } ,
1098+ } ) ;
1099+
1100+ const calls : string [ ] = [ ] ;
1101+ const vnode = ui . modal ( {
1102+ id : "confirm-quit" ,
1103+ title : "Confirm quit" ,
1104+ content : ui . text ( "Are you sure?" ) ,
1105+ actions : [
1106+ ui . button ( {
1107+ id : "cancel" ,
1108+ label : "Cancel" ,
1109+ onPress : ( ) => calls . push ( "cancel" ) ,
1110+ } ) ,
1111+ ui . button ( {
1112+ id : "confirm" ,
1113+ label : "Quit" ,
1114+ onPress : ( ) => calls . push ( "confirm" ) ,
1115+ } ) ,
1116+ ] ,
1117+ } ) ;
1118+
1119+ const res = renderer . submitFrame (
1120+ ( ) => vnode ,
1121+ undefined ,
1122+ { cols : 80 , rows : 24 } ,
1123+ defaultTheme ,
1124+ noRenderHooks ( ) ,
1125+ ) ;
1126+ assert . ok ( res . ok ) ;
1127+ const textSnapshot = createTestRenderer ( { viewport : { cols : 80 , rows : 24 } } )
1128+ . render ( vnode )
1129+ . toText ( ) ;
1130+ assert . equal ( textSnapshot . includes ( "Cancel" ) , true ) ;
1131+
1132+ const rects = renderer . getRectByIdIndex ( ) ;
1133+ const cancelRect = rects . get ( "cancel" ) ;
1134+ const confirmRect = rects . get ( "confirm" ) ;
1135+ assert . ok ( cancelRect !== undefined , "cancel rect should exist" ) ;
1136+ assert . ok ( confirmRect !== undefined , "confirm rect should exist" ) ;
1137+ if ( ! cancelRect || ! confirmRect ) return ;
1138+
1139+ assert . equal ( cancelRect . x < confirmRect . x , true , "cancel should render left of confirm" ) ;
1140+
1141+ const cancelX = cancelRect . x + Math . max ( 0 , Math . floor ( ( cancelRect . w - 1 ) / 2 ) ) ;
1142+ const cancelY = cancelRect . y ;
1143+ renderer . routeEngineEvent ( mouseDownEvent ( cancelX , cancelY ) ) ;
1144+ renderer . routeEngineEvent ( mouseEvent ( cancelX , cancelY , 4 ) ) ;
1145+
1146+ const confirmX = confirmRect . x + Math . max ( 0 , Math . floor ( ( confirmRect . w - 1 ) / 2 ) ) ;
1147+ const confirmY = confirmRect . y ;
1148+ renderer . routeEngineEvent ( mouseDownEvent ( confirmX , confirmY ) ) ;
1149+ renderer . routeEngineEvent ( mouseEvent ( confirmX , confirmY , 4 ) ) ;
1150+
1151+ assert . deepEqual ( calls , [ "cancel" , "confirm" ] ) ;
1152+ } ) ;
1153+
10921154 test ( "splitPane double-click toggles collapse via onCollapse" , ( ) => {
10931155 const backend = createNoopBackend ( ) ;
10941156 const renderer = new WidgetRenderer < void > ( {
0 commit comments