@@ -12,6 +12,7 @@ import {
1212 getColumnValues ,
1313 getRows ,
1414 getColumnHeaderCell ,
15+ raf ,
1516} from 'test/utils/helperFn' ;
1617import {
1718 useGridApiRef ,
@@ -653,6 +654,103 @@ describe('<DataGridPro /> - Rows', () => {
653654 } ) ;
654655 } ) ;
655656
657+ describe ( 'controlled layout' , ( ) => {
658+ const controlledLayout = { virtualizerLayoutMode : 'controlled' as const } ;
659+
660+ // Lets the browser fire the scroll event and the grid handle it, like a real scroll.
661+ const scrollTo = ( position : { top ?: number ; left ?: number } ) =>
662+ act ( async ( ) => {
663+ grid ( 'virtualScroller' ) ! . scrollTo ( position ) ;
664+ await raf ( ) ;
665+ } ) ;
666+
667+ function expectViewportCovered ( ) {
668+ const { viewportInnerSize } = apiRef . current ! . state . dimensions ;
669+ const viewportTop = grid ( 'columnHeaders' ) ! . getBoundingClientRect ( ) . bottom ;
670+ const viewportLeft = grid ( 'virtualScroller' ) ! . getBoundingClientRect ( ) . left ;
671+
672+ const rows = $$ ( '[role="row"][data-rowindex]' ) ;
673+ const rowRects = rows . map ( ( row ) => row . getBoundingClientRect ( ) ) ;
674+ expect ( Math . min ( ...rowRects . map ( ( rect ) => rect . top ) ) ) . to . be . at . most ( viewportTop + 0.5 ) ;
675+ expect ( Math . max ( ...rowRects . map ( ( rect ) => rect . bottom ) ) ) . to . be . at . least (
676+ viewportTop + viewportInnerSize . height - 0.5 ,
677+ ) ;
678+
679+ const cellRects = $$ ( rows [ 0 ] , '[role="gridcell"]' ) . map ( ( cell ) =>
680+ cell . getBoundingClientRect ( ) ,
681+ ) ;
682+ expect ( Math . min ( ...cellRects . map ( ( rect ) => rect . left ) ) ) . to . be . at . most ( viewportLeft + 0.5 ) ;
683+ expect ( Math . max ( ...cellRects . map ( ( rect ) => rect . right ) ) ) . to . be . at . least (
684+ viewportLeft + viewportInnerSize . width - 0.5 ,
685+ ) ;
686+ }
687+
688+ it ( 'should render the rows in the viewport when scrolling up by less than a row' , async ( ) => {
689+ const rowHeight = 50 ;
690+ render (
691+ < TestCaseVirtualization rowHeight = { rowHeight } experimentalFeatures = { controlledLayout } /> ,
692+ ) ;
693+
694+ // Start in the middle of a row, so the steps below reach the previous row before
695+ // they add up to a full row height.
696+ const initialTop = 20 * rowHeight + rowHeight / 2 ;
697+ await scrollTo ( { top : initialTop } ) ;
698+ for ( let top = initialTop - 10 ; top >= 18 * rowHeight ; top -= 10 ) {
699+ // eslint-disable-next-line no-await-in-loop
700+ await scrollTo ( { top } ) ;
701+ expectViewportCovered ( ) ;
702+ }
703+ } ) ;
704+
705+ it ( 'should render the rows in the viewport when scrolling down past rows shorter than the average' , async ( ) => {
706+ // Every 10th row is tall, so the other rows are shorter than the average row height
707+ // and a scroll smaller than the average can bring several of them into view.
708+ render (
709+ < TestCaseVirtualization
710+ getRowHeight = { ( { id } ) => ( Number ( id ) % 10 === 0 ? 300 : 25 ) }
711+ experimentalFeatures = { controlledLayout }
712+ /> ,
713+ ) ;
714+
715+ for ( let top = 0 ; top <= 600 ; top += 40 ) {
716+ // eslint-disable-next-line no-await-in-loop
717+ await scrollTo ( { top } ) ;
718+ expectViewportCovered ( ) ;
719+ }
720+ } ) ;
721+
722+ it ( 'should render the columns in the viewport when scrolling left by less than a column' , async ( ) => {
723+ const columnWidth = 100 ;
724+ render ( < TestCaseVirtualization nbCols = { 20 } experimentalFeatures = { controlledLayout } /> ) ;
725+
726+ // Start in the middle of a column and step by less than the column threshold.
727+ const initialLeft = 10 * columnWidth + columnWidth / 2 ;
728+ await scrollTo ( { left : initialLeft } ) ;
729+ for ( let left = initialLeft - 10 ; left >= 8 * columnWidth ; left -= 10 ) {
730+ // eslint-disable-next-line no-await-in-loop
731+ await scrollTo ( { left } ) ;
732+ expectViewportCovered ( ) ;
733+ }
734+ } ) ;
735+
736+ it ( 'should render the columns in the viewport when scrolling right past columns narrower than the threshold' , async ( ) => {
737+ const columns = Array . from ( { length : 60 } , ( _ , index ) => ( {
738+ field : `column${ index } ` ,
739+ width : 30 ,
740+ minWidth : 30 ,
741+ } ) ) ;
742+ render (
743+ < TestCaseVirtualization columns = { columns } experimentalFeatures = { controlledLayout } /> ,
744+ ) ;
745+
746+ for ( let left = 0 ; left <= 400 ; left += 10 ) {
747+ // eslint-disable-next-line no-await-in-loop
748+ await scrollTo ( { left } ) ;
749+ expectViewportCovered ( ) ;
750+ }
751+ } ) ;
752+ } ) ;
753+
656754 it ( 'should render new columns when scrolling past the threshold value' , async ( ) => {
657755 const columnWidth = 100 ;
658756 const columnThresholdPx = 1 * columnWidth ;
0 commit comments