@@ -478,4 +478,70 @@ describe('Grid adopt-root-styles property', () => {
478478 expect ( computedStyle . fontWeight ) . to . equal ( '700' ) ;
479479 } ) ;
480480 } ) ;
481+
482+ describe ( 'Virtualizer cell caching' , ( ) => {
483+ beforeEach ( async ( ) => {
484+ adoptRootStylesTDD . columnConfig = [
485+ {
486+ field : 'name' ,
487+ sortable : true ,
488+ cellTemplate : ( ctx : IgcCellContext < TestData > ) =>
489+ litHtml `<div class="custom-cell-class">${ ctx . value } </div>` ,
490+ } ,
491+ { field : 'id' , sortable : true } ,
492+ ] ;
493+ await adoptRootStylesTDD . setUp ( ) ;
494+ } ) ;
495+
496+ afterEach ( ( ) => adoptRootStylesTDD . tearDown ( ) ) ;
497+
498+ it ( 'should preserve adopted styles when a cell is disconnected and reconnected by the virtualizer' , async ( ) => {
499+ const cell = adoptRootStylesTDD . rows . first . cells . get ( 'name' ) ;
500+ const cellElement = cell . element as IgcGridLiteCell < TestData > ;
501+
502+ // Verify initial adopted state
503+ // @ts -expect-error - Accessing private controller for testing
504+ expect ( cellElement . _adoptedStylesController . hasAdoptedStyles ) . to . be . true ;
505+
506+ // Simulate virtualizer caching: disconnect the cell and reconnect it with
507+ // properties already set (no Lit property-change cycle will fire in update)
508+ const parentNode = cellElement . parentNode ! ;
509+ parentNode . removeChild ( cellElement ) ;
510+ await nextFrame ( ) ;
511+ parentNode . appendChild ( cellElement ) ;
512+ await cellElement . updateComplete ;
513+
514+ // connectedCallback must re-apply shouldAdoptStyles regardless of whether
515+ // adoptRootStyles changed, because the property did not change so the
516+ // update() guard (props.has('adoptRootStyles')) never fires
517+ // @ts -expect-error - Accessing private controller for testing
518+ expect ( cellElement . _adoptedStylesController . hasAdoptedStyles ) . to . be . true ;
519+
520+ const customDiv = cellElement . shadowRoot ! . querySelector ( '.custom-cell-class' ) ;
521+ expect ( customDiv ) . to . exist ;
522+ const computedStyle = window . getComputedStyle ( customDiv ! ) ;
523+ expect ( computedStyle . color ) . to . equal ( 'rgb(255, 0, 0)' ) ;
524+ } ) ;
525+
526+ it ( 'should preserve adopted styles after sorting already-sorted data across multiple columns' , async ( ) => {
527+ // The test data IDs are 1–8 in ascending order, so sorting ascending by id
528+ // produces no reordering. The virtualizer therefore recycles cells in-place
529+ // without a full connected/disconnected lifecycle, meaning connectedCallback
530+ // is never re-invoked and update()'s props.has('adoptRootStyles') guard
531+ // does not fire — this is the exact scenario the connectedCallback fix covers.
532+ await adoptRootStylesTDD . sortHeader ( 'id' ) ;
533+
534+ // A second sort column keeps the order stable (all ids are unique, so the
535+ // secondary key never kicks in), producing another no-op reorder pass.
536+ await adoptRootStylesTDD . sortHeader ( 'name' ) ;
537+
538+ for ( let i = 0 ; i < adoptRootStylesTDD . grid . rows . length ; i ++ ) {
539+ const cellElement = adoptRootStylesTDD . rows . get ( i ) . cells . get ( 'name' )
540+ . element as IgcGridLiteCell < TestData > ;
541+
542+ // @ts -expect-error - Accessing private controller for testing
543+ expect ( cellElement . _adoptedStylesController . hasAdoptedStyles ) . to . be . true ;
544+ }
545+ } ) ;
546+ } ) ;
481547} ) ;
0 commit comments