@@ -3,8 +3,14 @@ import { createRef } from "react";
33import { describe , expect , test , vi } from "vitest" ;
44import { setDefaultElementBounds } from "../../utils/test/mockBoundingClientRect" ;
55import { Panel } from "../panel/Panel" ;
6+ import { getStorageKey } from "./auto-save/getStorageKey" ;
67import { Group } from "./Group" ;
7- import { type GroupImperativeHandle , type LayoutStorage } from "./types" ;
8+ import type { LegacyLayout } from "./readLegacyLayout" ;
9+ import {
10+ type GroupImperativeHandle ,
11+ type Layout ,
12+ type LayoutStorage
13+ } from "./types" ;
814import { useDefaultLayout } from "./useDefaultLayout" ;
915
1016describe ( "useDefaultLayout" , ( ) => {
@@ -678,4 +684,250 @@ describe("useDefaultLayout", () => {
678684
679685 expect ( storage . setItem ) . toHaveBeenCalledTimes ( 1 ) ;
680686 } ) ;
687+
688+ describe ( "legacy layout fallback" , ( ) => {
689+ function mockLayoutStorage ( {
690+ groupId,
691+ legacyLayout = {
692+ "left,right" : {
693+ expandToSizes : { } ,
694+ layout : [ 50 , 50 ]
695+ } ,
696+ "center,left,right" : {
697+ expandToSizes : { } ,
698+ layout : [ 33 , 33 , 34 ]
699+ }
700+ } ,
701+ matchV3,
702+ matchV4,
703+ modernLayout = {
704+ left : 25 ,
705+ center : 35 ,
706+ right : 40
707+ } ,
708+ panelIds
709+ } : {
710+ groupId : string ;
711+ legacyLayout ?: LegacyLayout ;
712+ matchV3 ?: boolean ;
713+ matchV4 ?: boolean ;
714+ modernLayout ?: Layout ;
715+ panelIds ?: string [ ] | undefined ;
716+ } ) {
717+ const keyV3 = getStorageKey ( groupId , [ ] ) ;
718+ const keyV4 = getStorageKey ( groupId , panelIds ?? [ ] ) ;
719+
720+ const storage : LayoutStorage = {
721+ getItem : vi . fn ( ( key ) => {
722+ if ( matchV4 && key === keyV4 ) {
723+ return JSON . stringify ( modernLayout ) ;
724+ } else if ( matchV3 && key === keyV3 ) {
725+ return JSON . stringify ( legacyLayout ) ;
726+ } else {
727+ return null ;
728+ }
729+ } ) ,
730+ setItem : vi . fn ( )
731+ } ;
732+
733+ return {
734+ keyV3,
735+ keyV4,
736+ storage
737+ } ;
738+ }
739+
740+ describe ( "with panel ids prop" , ( ) => {
741+ test ( "skips mismatched legacy layout" , ( ) => {
742+ const { keyV3, keyV4, storage } = mockLayoutStorage ( {
743+ groupId : "test-group-id" ,
744+ matchV3 : true ,
745+ panelIds : [ "left" , "center" ]
746+ } ) ;
747+
748+ const { result } = renderHook ( ( ) =>
749+ useDefaultLayout ( {
750+ id : "test-group-id" ,
751+ panelIds : [ "left" , "center" ] ,
752+ storage
753+ } )
754+ ) ;
755+
756+ expect ( result . current . defaultLayout ) . toMatchInlineSnapshot ( `undefined` ) ;
757+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV4 ) ;
758+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV3 ) ;
759+ expect ( storage . setItem ) . not . toHaveBeenCalled ( ) ;
760+ } ) ;
761+
762+ test ( "returns matching legacy layout" , ( ) => {
763+ const { keyV3, keyV4, storage } = mockLayoutStorage ( {
764+ groupId : "test-group-id" ,
765+ matchV3 : true ,
766+ panelIds : [ "left" , "right" ]
767+ } ) ;
768+
769+ const { result } = renderHook ( ( ) =>
770+ useDefaultLayout ( {
771+ id : "test-group-id" ,
772+ panelIds : [ "left" , "right" ] ,
773+ storage
774+ } )
775+ ) ;
776+
777+ expect ( result . current . defaultLayout ) . toMatchInlineSnapshot ( `
778+ {
779+ "left": 50,
780+ "right": 50,
781+ }
782+ ` ) ;
783+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV4 ) ;
784+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV3 ) ;
785+ expect ( storage . setItem ) . not . toHaveBeenCalled ( ) ;
786+ } ) ;
787+
788+ test ( "prefers matching modern layouts if both are present" , ( ) => {
789+ const { keyV3, keyV4, storage } = mockLayoutStorage ( {
790+ groupId : "test-group-id" ,
791+ matchV3 : true ,
792+ matchV4 : true ,
793+ panelIds : [ "left" , "center" , "right" ]
794+ } ) ;
795+
796+ const { result } = renderHook ( ( ) =>
797+ useDefaultLayout ( {
798+ id : "test-group-id" ,
799+ panelIds : [ "left" , "center" , "right" ] ,
800+ storage
801+ } )
802+ ) ;
803+
804+ expect ( result . current . defaultLayout ) . toMatchInlineSnapshot ( `
805+ {
806+ "center": 35,
807+ "left": 25,
808+ "right": 40,
809+ }
810+ ` ) ;
811+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV4 ) ;
812+ expect ( storage . getItem ) . not . toHaveBeenCalledWith ( keyV3 ) ;
813+ expect ( storage . setItem ) . not . toHaveBeenCalled ( ) ;
814+ } ) ;
815+ } ) ;
816+
817+ describe ( "without panel ids prop" , ( ) => {
818+ test ( "skips mismatched legacy layout" , ( ) => {
819+ const { keyV3, keyV4, storage } = mockLayoutStorage ( {
820+ groupId : "test-group-id" ,
821+ matchV3 : true
822+ } ) ;
823+
824+ const { result } = renderHook ( ( ) =>
825+ useDefaultLayout ( {
826+ id : "test-group-id" ,
827+ storage
828+ } )
829+ ) ;
830+
831+ expect ( result . current . defaultLayout ) . toMatchInlineSnapshot ( `undefined` ) ;
832+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV4 ) ;
833+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV3 ) ;
834+ expect ( storage . setItem ) . not . toHaveBeenCalled ( ) ;
835+ } ) ;
836+
837+ test ( "returns matching legacy layout" , ( ) => {
838+ const { keyV3, keyV4, storage } = mockLayoutStorage ( {
839+ groupId : "test-group-id" ,
840+ legacyLayout : {
841+ "center,right" : {
842+ expandToSizes : { } ,
843+ layout : [ 50 , 50 ]
844+ }
845+ } ,
846+ matchV3 : true
847+ } ) ;
848+
849+ const { result } = renderHook ( ( ) =>
850+ useDefaultLayout ( {
851+ id : "test-group-id" ,
852+ storage
853+ } )
854+ ) ;
855+
856+ expect ( result . current . defaultLayout ) . toMatchInlineSnapshot ( `
857+ {
858+ "center": 50,
859+ "right": 50,
860+ }
861+ ` ) ;
862+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV4 ) ;
863+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV3 ) ;
864+ expect ( storage . setItem ) . not . toHaveBeenCalled ( ) ;
865+ } ) ;
866+
867+ test ( "prefers matching modern layouts if both are present" , ( ) => {
868+ const { keyV4, storage } = mockLayoutStorage ( {
869+ groupId : "test-group-id" ,
870+ matchV3 : true ,
871+ matchV4 : true
872+ } ) ;
873+
874+ const { result } = renderHook ( ( ) =>
875+ useDefaultLayout ( {
876+ id : "test-group-id" ,
877+ storage
878+ } )
879+ ) ;
880+
881+ expect ( result . current . defaultLayout ) . toMatchInlineSnapshot ( `
882+ {
883+ "center": 35,
884+ "left": 25,
885+ "right": 40,
886+ }
887+ ` ) ;
888+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV4 ) ;
889+ expect ( storage . setItem ) . not . toHaveBeenCalled ( ) ;
890+ } ) ;
891+ } ) ;
892+
893+ test ( "updates persisted modern layout" , ( ) => {
894+ const { keyV3, keyV4, storage } = mockLayoutStorage ( {
895+ groupId : "test-group-id" ,
896+ matchV3 : true ,
897+ panelIds : [ "left" , "right" ]
898+ } ) ;
899+
900+ const { result } = renderHook ( ( ) =>
901+ useDefaultLayout ( {
902+ id : "test-group-id" ,
903+ panelIds : [ "left" , "right" ] ,
904+ storage
905+ } )
906+ ) ;
907+
908+ expect ( result . current . defaultLayout ) . toMatchInlineSnapshot ( `
909+ {
910+ "left": 50,
911+ "right": 50,
912+ }
913+ ` ) ;
914+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV4 ) ;
915+ expect ( storage . getItem ) . toHaveBeenCalledWith ( keyV3 ) ;
916+ expect ( storage . setItem ) . not . toHaveBeenCalled ( ) ;
917+
918+ result . current . onLayoutChanged ( {
919+ left : 35 ,
920+ right : 65
921+ } ) ;
922+
923+ expect ( storage . setItem ) . toHaveBeenCalledTimes ( 1 ) ;
924+ expect ( storage . setItem ) . toHaveBeenCalledWith (
925+ keyV4 ,
926+ JSON . stringify ( {
927+ left : 35 ,
928+ right : 65
929+ } )
930+ ) ;
931+ } ) ;
932+ } ) ;
681933} ) ;
0 commit comments