@@ -417,6 +417,63 @@ describe('intersection splitter drag', () => {
417417 }
418418 } ) ;
419419
420+ it ( 'creates a handle for a perpendicular splitter nested deeper than one level' , async ( ) => {
421+ const restoreMocks = setupDimensionMocks ( ) ;
422+
423+ try {
424+ // The bottom row holds a left column whose first child is itself a row.
425+ // That inner row's vertical splitter is two levels below the root column's
426+ // top/bottom bar yet its top still touches it, so the bar owner (the root
427+ // column) must create a crossing handle for it.
428+ layout = await createLayout ( {
429+ content : [
430+ {
431+ type : 'column' ,
432+ content : [
433+ { type : 'component' , componentName : 'testComponent' } ,
434+ {
435+ type : 'row' ,
436+ content : [
437+ {
438+ type : 'column' ,
439+ content : [
440+ {
441+ type : 'row' ,
442+ content : [
443+ { type : 'component' , componentName : 'testComponent' } ,
444+ { type : 'component' , componentName : 'testComponent' } ,
445+ ] ,
446+ } ,
447+ { type : 'component' , componentName : 'testComponent' } ,
448+ ] ,
449+ } ,
450+ { type : 'component' , componentName : 'testComponent' } ,
451+ ] ,
452+ } ,
453+ ] ,
454+ } ,
455+ ] ,
456+ } ) ;
457+
458+ const rootColumn = verifyPath ( 'column' , layout ) as any ;
459+ expect ( rootColumn ) . toBeDefined ( ) ;
460+
461+ await new Promise < void > ( resolve => {
462+ window . requestAnimationFrame ( ( ) => resolve ( ) ) ;
463+ } ) ;
464+
465+ // Handles owned by the root column are appended directly into its element.
466+ // It owns one for the bottom row's own vertical splitter (left column |
467+ // right component) and one for the deeply nested inner-row splitter.
468+ const rootOwnedHandles = rootColumn . element . children (
469+ '.lm_intersection_splitter'
470+ ) ;
471+ expect ( rootOwnedHandles . length ) . toBe ( 2 ) ;
472+ } finally {
473+ restoreMocks ( ) ;
474+ }
475+ } ) ;
476+
420477 it ( 'preserves all intersection handles after normal vertical and horizontal splitter drags' , async ( ) => {
421478 const restoreMocks = setupDimensionMocks ( ) ;
422479
@@ -600,4 +657,75 @@ describe('intersection splitter drag', () => {
600657 restoreMocks ( ) ;
601658 }
602659 } ) ;
660+
661+ it ( 'stretches the stem line with a transform (not box size) and clears it on stop' , async ( ) => {
662+ const restoreMocks = setupDimensionMocks ( ) ;
663+
664+ try {
665+ layout = await createLayout ( {
666+ content : [
667+ {
668+ type : 'column' ,
669+ content : [
670+ { type : 'component' , componentName : 'testComponent' } ,
671+ {
672+ type : 'row' ,
673+ content : [
674+ {
675+ type : 'column' ,
676+ content : [
677+ { type : 'component' , componentName : 'testComponent' } ,
678+ { type : 'component' , componentName : 'testComponent' } ,
679+ ] ,
680+ } ,
681+ { type : 'component' , componentName : 'testComponent' } ,
682+ ] ,
683+ } ,
684+ ] ,
685+ } ,
686+ ] ,
687+ } ) ;
688+
689+ const bottomRow = verifyPath ( 'column.1.row' , layout ) as any ;
690+ expect ( bottomRow ) . toBeDefined ( ) ;
691+
692+ const intersectionHandle = bottomRow . element
693+ . find ( '.lm_intersection_splitter' )
694+ . first ( ) ;
695+ expect ( intersectionHandle . length ) . toBe ( 1 ) ;
696+
697+ // The stem line is the vertical splitter inside the bottom row.
698+ const stemLine = bottomRow . element . find ( '.lm_splitter.lm_vertical' ) ;
699+ expect ( stemLine . length ) . toBe ( 1 ) ;
700+ const stemEl = stemLine [ 0 ] as HTMLElement ;
701+
702+ const startX = 100 ;
703+ const startY = 100 ;
704+ const mousedown = $ . Event ( 'mousedown' ) as JQuery . TriggeredEvent ;
705+ mousedown . pageX = startX ;
706+ mousedown . pageY = startY ;
707+ mousedown . button = 0 ;
708+ intersectionHandle . trigger ( mousedown ) ;
709+
710+ const mousemove = $ . Event ( 'mousemove' ) as JQuery . TriggeredEvent ;
711+ mousemove . pageX = startX - 40 ;
712+ mousemove . pageY = startY - 40 ;
713+ $ ( document ) . trigger ( mousemove ) ;
714+
715+ // While dragging, the stem is stretched via a scale transform - never by
716+ // mutating its box size, which would reflow sibling panes and headers.
717+ expect ( stemEl . style . transform ) . toContain ( 'scale' ) ;
718+ expect ( stemEl . style . width ) . toBe ( '' ) ;
719+
720+ $ ( document ) . trigger ( 'mouseup' ) ;
721+ await new Promise < void > ( resolve => {
722+ window . requestAnimationFrame ( ( ) => resolve ( ) ) ;
723+ } ) ;
724+
725+ // The transform is cleared once the drag stops.
726+ expect ( stemEl . style . transform ) . toBe ( '' ) ;
727+ } finally {
728+ restoreMocks ( ) ;
729+ }
730+ } ) ;
603731} ) ;
0 commit comments