@@ -454,6 +454,67 @@ describe('layoutTableBlock', () => {
454454 expect ( rowBoundaries ! [ 0 ] . resizable ) . toBe ( false ) ;
455455 expect ( rowBoundaries ! [ 1 ] . resizable ) . toBe ( true ) ;
456456 } ) ;
457+
458+ it ( 'marks row boundaries as non-resizable when a rowspan from a prior fragment crosses them' , ( ) => {
459+ // 5 rows, 2 columns. First cell in row 0 has rowSpan=4, covering rows 0-3.
460+ // When the table splits so a continuation fragment renders rows 2-4,
461+ // the boundary after row 2 must be blocked because the span from row 0
462+ // still extends through it (the span covers rows 0,1,2,3).
463+ // The boundary after row 3 (end of span) and row 4 should be resizable.
464+ const block = createMockTableBlock ( 5 ) ;
465+ const measure = createMockTableMeasure ( [ 100 , 100 ] , [ 30 , 30 , 30 , 30 , 30 ] ) ;
466+
467+ // Inject rowSpan=4 on the first cell of row 0
468+ ( measure . rows [ 0 ] . cells [ 0 ] as any ) . rowSpan = 4 ;
469+
470+ const fragments : TableFragment [ ] = [ ] ;
471+ let cursorY = 0 ;
472+ let contentBottom = 65 ; // Fits rows 0-1 (30+30=60 < 65), forces split before row 2
473+
474+ layoutTableBlock ( {
475+ block,
476+ measure,
477+ columnWidth : 200 ,
478+ ensurePage : ( ) => ( {
479+ page : { fragments } ,
480+ columnIndex : 0 ,
481+ cursorY,
482+ contentBottom,
483+ } ) ,
484+ advanceColumn : ( ) => {
485+ cursorY = 0 ;
486+ contentBottom = 200 ; // continuation page has room for remaining rows
487+ return {
488+ page : { fragments } ,
489+ columnIndex : 0 ,
490+ cursorY,
491+ contentBottom,
492+ } ;
493+ } ,
494+ columnX : ( ) => 0 ,
495+ } ) ;
496+
497+ // Collect all row boundaries across continuation fragments (fromRow >= 2)
498+ const continuationFragments = fragments . filter ( ( f ) => f . fromRow >= 2 ) ;
499+ expect ( continuationFragments . length ) . toBeGreaterThan ( 0 ) ;
500+
501+ const allRowBoundaries = continuationFragments . flatMap ( ( f ) => f . metadata ?. rowBoundaries ?? [ ] ) ;
502+
503+ // Row 2 boundary should be blocked (rowSpan from row 0 extends through row 3)
504+ const row2 = allRowBoundaries . find ( ( rb ) => rb . index === 2 ) ;
505+ expect ( row2 ) . toBeDefined ( ) ;
506+ expect ( row2 ! . resizable ) . toBe ( false ) ;
507+
508+ // Row 3 is the last row of the span — its bottom boundary is NOT blocked
509+ const row3 = allRowBoundaries . find ( ( rb ) => rb . index === 3 ) ;
510+ expect ( row3 ) . toBeDefined ( ) ;
511+ expect ( row3 ! . resizable ) . toBe ( true ) ;
512+
513+ // Row 4 is entirely outside the span (may be in a later fragment)
514+ const row4 = allRowBoundaries . find ( ( rb ) => rb . index === 4 ) ;
515+ expect ( row4 ) . toBeDefined ( ) ;
516+ expect ( row4 ! . resizable ) . toBe ( true ) ;
517+ } ) ;
457518 } ) ;
458519
459520 describe ( 'cellSpacing' , ( ) => {
0 commit comments