@@ -16,7 +16,7 @@ import {
1616 isLanding ,
1717 paintLanding ,
1818} from "./shell"
19- import { openOperatorOverlay } from "./overlays"
19+ import { makeOperatorQuestion , openOperatorOverlay } from "./overlays"
2020import {
2121 LANDING_HINTS ,
2222 LANDING_SUGGESTIONS ,
@@ -328,7 +328,7 @@ describe("landing screen", () => {
328328 )
329329 } )
330330
331- test ( "an overlay covers the landing instead of moving it " , async ( ) => {
331+ test ( "an overlay covers the landing, sliding it only as far as its content needs " , async ( ) => {
332332 await withTestRenderer (
333333 async ( h ) => {
334334 const shell = createAppShell ( h . renderer , {
@@ -345,15 +345,25 @@ describe("landing screen", () => {
345345 before . findIndex ( ( row ) => row . includes ( text ) ) ,
346346 )
347347 expect ( was . every ( ( index ) => index > 0 ) ) . toBe ( true )
348+ // The anchors are listed top to bottom, so their positions climb
349+ // together before the overlay opens.
350+ expect ( was ) . toEqual ( [ ...was ] . sort ( ( a , b ) => a - b ) )
348351
349352 openOperatorOverlay ( shell )
350353 await settle ( h )
351354 const after = rows ( h )
352- // Every landing anchor is on the row it was on: the overlay covers
353- // the composition, it does not push it around.
354- expect (
355- anchors . map ( ( text ) => after . findIndex ( ( row ) => row . includes ( text ) ) ) ,
356- ) . toEqual ( was )
355+ // Every landing anchor is still on screen and in the same relative
356+ // order: the overlay is not letting the composition it covers spill
357+ // off the viewport, overlap itself, or reshuffle. It may still
358+ // slide the composition (up or down a little, as the mark re-grids
359+ // for its new tier) when its own content needs more room than the
360+ // even top/bottom split would otherwise leave it.
361+ const nowAt = anchors . map ( ( text ) =>
362+ after . findIndex ( ( row ) => row . includes ( text ) ) ,
363+ )
364+ expect ( nowAt . every ( ( index ) => index > 0 ) ) . toBe ( true )
365+ expect ( nowAt ) . toEqual ( [ ...nowAt ] . sort ( ( a , b ) => a - b ) )
366+ expect ( new Set ( nowAt ) . size ) . toBe ( nowAt . length )
357367 expect ( h . captureCharFrame ( ) ) . toContain ( "operator" )
358368 } finally {
359369 shell . dispose ( )
@@ -363,6 +373,35 @@ describe("landing screen", () => {
363373 )
364374 } )
365375
376+ // A question with more choices than the even top/bottom split would leave
377+ // room for used to get its list starved down to whatever that split
378+ // happened to allow — as little as one or two choices — because the float
379+ // only asked the split for one choice row of headroom. It now asks for the
380+ // overlay's real, already fraction-capped content height, so a terminal
381+ // tall enough for that content shows every choice without scrolling.
382+ test ( "a landing overlay with many choices shows them all when there is room" , async ( ) => {
383+ await withTestRenderer (
384+ async ( h ) => {
385+ const shell = createAppShell ( h . renderer , {
386+ terminal : { columns : 100 , rows : 36 } ,
387+ wireKeys : false ,
388+ run : "idle" ,
389+ } )
390+ try {
391+ openOperatorOverlay ( shell )
392+ await settle ( h )
393+ const frame = h . captureCharFrame ( )
394+ for ( const choice of makeOperatorQuestion ( ) . choices ) {
395+ expect ( frame ) . toContain ( choice )
396+ }
397+ } finally {
398+ shell . dispose ( )
399+ }
400+ } ,
401+ { width : 100 , height : 36 } ,
402+ )
403+ } )
404+
366405 test ( "a short or narrow terminal shrinks the mark, never the prompt box" , async ( ) => {
367406 for ( const size of [
368407 { width : 100 , height : 30 } ,
0 commit comments