@@ -198,7 +198,7 @@ describe('DashboardDatasourceBehaviour', () => {
198198
199199 expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
200200 // since there is no previous request ID on dashboard load, the behaviour should not re-run queries
201- expect ( behaviour [ 'prevRequestId' ] ) . toBeUndefined ( ) ;
201+ expect ( behaviour [ 'prevRequestIds' ] . size ) . toBe ( 0 ) ;
202202 } ) ;
203203
204204 it ( 'Should not re-run queries in behaviour on scene load' , async ( ) => {
@@ -242,7 +242,7 @@ describe('DashboardDatasourceBehaviour', () => {
242242
243243 expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
244244 // since there is no previous request ID on dashboard load, the behaviour should not re-run queries
245- expect ( behaviour [ 'prevRequestId' ] ) . toBeUndefined ( ) ;
245+ expect ( behaviour [ 'prevRequestIds' ] . size ) . toBe ( 0 ) ;
246246 } ) ;
247247
248248 it ( 'Should exit behaviour early if not in a dashboard scene' , async ( ) => {
@@ -593,6 +593,363 @@ describe('DashboardDatasourceBehaviour', () => {
593593
594594 expect ( spy ) . toHaveBeenCalled ( ) ;
595595 } ) ;
596+
597+ it ( 'Should re-run query when ANY source panel changes with multiple dashboardDS queries' , async ( ) => {
598+ jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
599+
600+ // Create two source panels
601+ const sourcePanel1 = new VizPanel ( {
602+ title : 'Panel A' ,
603+ pluginId : 'table' ,
604+ key : 'panel-1' ,
605+ $data : new SceneDataTransformer ( {
606+ transformations : [ ] ,
607+ $data : new SceneQueryRunner ( {
608+ datasource : { uid : 'grafana' } ,
609+ queries : [ { refId : 'A' , queryType : 'randomWalk' } ] ,
610+ } ) ,
611+ } ) ,
612+ } ) ;
613+
614+ const sourcePanel2 = new VizPanel ( {
615+ title : 'Panel B' ,
616+ pluginId : 'table' ,
617+ key : 'panel-2' ,
618+ $data : new SceneDataTransformer ( {
619+ transformations : [ ] ,
620+ $data : new SceneQueryRunner ( {
621+ datasource : { uid : 'grafana' } ,
622+ queries : [ { refId : 'A' , queryType : 'randomWalk' } ] ,
623+ } ) ,
624+ } ) ,
625+ } ) ;
626+
627+ // Create a mixed DS panel that references BOTH source panels
628+ const mixedDSPanel = new VizPanel ( {
629+ title : 'Panel C - Mixed' ,
630+ pluginId : 'table' ,
631+ key : 'panel-3' ,
632+ $data : new SceneDataTransformer ( {
633+ transformations : [ ] ,
634+ $data : new SceneQueryRunner ( {
635+ datasource : { uid : MIXED_DATASOURCE_NAME } ,
636+ queries : [
637+ {
638+ datasource : { uid : SHARED_DASHBOARD_QUERY } ,
639+ refId : 'A' ,
640+ panelId : 1 ,
641+ } ,
642+ {
643+ datasource : { uid : SHARED_DASHBOARD_QUERY } ,
644+ refId : 'B' ,
645+ panelId : 2 ,
646+ } ,
647+ ] ,
648+ $behaviors : [ new DashboardDatasourceBehaviour ( { } ) ] ,
649+ } ) ,
650+ } ) ,
651+ } ) ;
652+
653+ const scene = new DashboardScene ( {
654+ title : 'hello' ,
655+ uid : 'dash-1' ,
656+ meta : {
657+ canEdit : true ,
658+ } ,
659+ body : DefaultGridLayoutManager . fromVizPanels ( [ sourcePanel1 , sourcePanel2 , mixedDSPanel ] ) ,
660+ } ) ;
661+
662+ const sceneDeactivate = activateFullSceneTree ( scene ) ;
663+
664+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
665+
666+ const spy = jest
667+ . spyOn ( mixedDSPanel . state . $data ! . state . $data as SceneQueryRunner , 'runQueries' )
668+ . mockImplementation ( ) ;
669+
670+ // deactivate scene
671+ sceneDeactivate ( ) ;
672+
673+ // Only change the SECOND source panel
674+ ( sourcePanel2 . state . $data ! . state . $data as SceneQueryRunner ) . runQueries ( ) ;
675+
676+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
677+
678+ // activate scene again
679+ activateFullSceneTree ( scene ) ;
680+
681+ // Should re-run because the second panel changed
682+ expect ( spy ) . toHaveBeenCalled ( ) ;
683+ } ) ;
684+
685+ it ( 'Should track multiple dashboardDS queries independently' , async ( ) => {
686+ jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
687+
688+ const sourcePanel1 = new VizPanel ( {
689+ title : 'Panel A' ,
690+ pluginId : 'table' ,
691+ key : 'panel-1' ,
692+ $data : new SceneDataTransformer ( {
693+ transformations : [ ] ,
694+ $data : new SceneQueryRunner ( {
695+ datasource : { uid : 'grafana' } ,
696+ queries : [ { refId : 'A' , queryType : 'randomWalk' } ] ,
697+ } ) ,
698+ } ) ,
699+ } ) ;
700+
701+ const sourcePanel2 = new VizPanel ( {
702+ title : 'Panel B' ,
703+ pluginId : 'table' ,
704+ key : 'panel-2' ,
705+ $data : new SceneDataTransformer ( {
706+ transformations : [ ] ,
707+ $data : new SceneQueryRunner ( {
708+ datasource : { uid : 'grafana' } ,
709+ queries : [ { refId : 'A' , queryType : 'randomWalk' } ] ,
710+ } ) ,
711+ } ) ,
712+ } ) ;
713+
714+ const mixedDSPanel = new VizPanel ( {
715+ title : 'Panel C - Mixed' ,
716+ pluginId : 'table' ,
717+ key : 'panel-3' ,
718+ $data : new SceneDataTransformer ( {
719+ transformations : [ ] ,
720+ $data : new SceneQueryRunner ( {
721+ datasource : { uid : MIXED_DATASOURCE_NAME } ,
722+ queries : [
723+ {
724+ datasource : { uid : SHARED_DASHBOARD_QUERY } ,
725+ refId : 'A' ,
726+ panelId : 1 ,
727+ } ,
728+ {
729+ datasource : { uid : SHARED_DASHBOARD_QUERY } ,
730+ refId : 'B' ,
731+ panelId : 2 ,
732+ } ,
733+ ] ,
734+ $behaviors : [ new DashboardDatasourceBehaviour ( { } ) ] ,
735+ } ) ,
736+ } ) ,
737+ } ) ;
738+
739+ const scene = new DashboardScene ( {
740+ title : 'hello' ,
741+ uid : 'dash-1' ,
742+ meta : {
743+ canEdit : true ,
744+ } ,
745+ body : DefaultGridLayoutManager . fromVizPanels ( [ sourcePanel1 , sourcePanel2 , mixedDSPanel ] ) ,
746+ } ) ;
747+
748+ const sceneDeactivate = activateFullSceneTree ( scene ) ;
749+
750+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
751+
752+ const spy = jest
753+ . spyOn ( mixedDSPanel . state . $data ! . state . $data as SceneQueryRunner , 'runQueries' )
754+ . mockImplementation ( ) ;
755+
756+ // First cycle: change panel 1
757+ sceneDeactivate ( ) ;
758+ ( sourcePanel1 . state . $data ! . state . $data as SceneQueryRunner ) . runQueries ( ) ;
759+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
760+ const deactivate2 = activateFullSceneTree ( scene ) ;
761+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
762+
763+ // Second cycle: change panel 2
764+ deactivate2 ( ) ;
765+ ( sourcePanel2 . state . $data ! . state . $data as SceneQueryRunner ) . runQueries ( ) ;
766+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
767+ activateFullSceneTree ( scene ) ;
768+
769+ // Should have been called again for panel 2
770+ expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ;
771+ } ) ;
772+
773+ it ( 'Should handle multiple dashboardDS queries with library panels' , async ( ) => {
774+ jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
775+
776+ const libPanelBehavior1 = new LibraryPanelBehavior ( {
777+ isLoaded : false ,
778+ uid : 'lib-panel-1' ,
779+ name : 'Library Panel 1' ,
780+ _loadedPanel : undefined ,
781+ } ) ;
782+
783+ const libPanelBehavior2 = new LibraryPanelBehavior ( {
784+ isLoaded : false ,
785+ uid : 'lib-panel-2' ,
786+ name : 'Library Panel 2' ,
787+ _loadedPanel : undefined ,
788+ } ) ;
789+
790+ const sourcePanel1 = new VizPanel ( {
791+ title : 'Panel A' ,
792+ pluginId : 'table' ,
793+ key : 'panel-1' ,
794+ $behaviors : [ libPanelBehavior1 ] ,
795+ $data : new SceneQueryRunner ( {
796+ datasource : { uid : 'grafana' } ,
797+ queries : [ { refId : 'A' , queryType : 'randomWalk' } ] ,
798+ } ) ,
799+ } ) ;
800+
801+ const sourcePanel2 = new VizPanel ( {
802+ title : 'Panel B' ,
803+ pluginId : 'table' ,
804+ key : 'panel-2' ,
805+ $behaviors : [ libPanelBehavior2 ] ,
806+ $data : new SceneQueryRunner ( {
807+ datasource : { uid : 'grafana' } ,
808+ queries : [ { refId : 'A' , queryType : 'randomWalk' } ] ,
809+ } ) ,
810+ } ) ;
811+
812+ const mixedDSPanel = new VizPanel ( {
813+ title : 'Panel C - Mixed' ,
814+ pluginId : 'table' ,
815+ key : 'panel-3' ,
816+ $data : new SceneQueryRunner ( {
817+ datasource : { uid : MIXED_DATASOURCE_NAME } ,
818+ queries : [
819+ {
820+ datasource : { uid : SHARED_DASHBOARD_QUERY } ,
821+ refId : 'A' ,
822+ panelId : 1 ,
823+ } ,
824+ {
825+ datasource : { uid : SHARED_DASHBOARD_QUERY } ,
826+ refId : 'B' ,
827+ panelId : 2 ,
828+ } ,
829+ ] ,
830+ $behaviors : [ new DashboardDatasourceBehaviour ( { } ) ] ,
831+ } ) ,
832+ } ) ;
833+
834+ const scene = new DashboardScene ( {
835+ title : 'hello' ,
836+ uid : 'dash-1' ,
837+ meta : {
838+ canEdit : true ,
839+ } ,
840+ body : DefaultGridLayoutManager . fromVizPanels ( [ sourcePanel1 , sourcePanel2 , mixedDSPanel ] ) ,
841+ } ) ;
842+
843+ activateFullSceneTree ( scene ) ;
844+
845+ const spy = jest . spyOn ( mixedDSPanel . state . $data as SceneQueryRunner , 'runQueries' ) ;
846+
847+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
848+
849+ // Should not run queries until library panels are loaded
850+ expect ( spy ) . not . toHaveBeenCalled ( ) ;
851+
852+ // Load first library panel
853+ libPanelBehavior1 . setState ( {
854+ isLoaded : true ,
855+ uid : 'lib-panel-1' ,
856+ name : 'Library Panel 1' ,
857+ _loadedPanel : undefined ,
858+ } ) ;
859+
860+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
861+
862+ // Load second library panel
863+ libPanelBehavior2 . setState ( {
864+ isLoaded : true ,
865+ uid : 'lib-panel-2' ,
866+ name : 'Library Panel 2' ,
867+ _loadedPanel : undefined ,
868+ } ) ;
869+
870+ expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ;
871+ } ) ;
872+
873+ it ( 'Should handle multiple queries with transformers on all source panels' , async ( ) => {
874+ jest . spyOn ( console , 'error' ) . mockImplementation ( ) ;
875+
876+ const sourcePanel1 = new VizPanel ( {
877+ title : 'Panel A' ,
878+ pluginId : 'table' ,
879+ key : 'panel-1' ,
880+ $data : new SceneDataTransformer ( {
881+ transformations : [ { id : 'transformA' , options : { } } ] ,
882+ $data : new SceneQueryRunner ( {
883+ datasource : { uid : 'grafana' } ,
884+ queries : [ { refId : 'A' , queryType : 'randomWalk' } ] ,
885+ } ) ,
886+ } ) ,
887+ } ) ;
888+
889+ const sourcePanel2 = new VizPanel ( {
890+ title : 'Panel B' ,
891+ pluginId : 'table' ,
892+ key : 'panel-2' ,
893+ $data : new SceneDataTransformer ( {
894+ transformations : [ { id : 'transformB' , options : { } } ] ,
895+ $data : new SceneQueryRunner ( {
896+ datasource : { uid : 'grafana' } ,
897+ queries : [ { refId : 'A' , queryType : 'randomWalk' } ] ,
898+ } ) ,
899+ } ) ,
900+ } ) ;
901+
902+ const mixedDSPanel = new VizPanel ( {
903+ title : 'Panel C - Mixed' ,
904+ pluginId : 'table' ,
905+ key : 'panel-3' ,
906+ $data : new SceneQueryRunner ( {
907+ datasource : { uid : MIXED_DATASOURCE_NAME } ,
908+ queries : [
909+ {
910+ datasource : { uid : SHARED_DASHBOARD_QUERY } ,
911+ refId : 'A' ,
912+ panelId : 1 ,
913+ } ,
914+ {
915+ datasource : { uid : SHARED_DASHBOARD_QUERY } ,
916+ refId : 'B' ,
917+ panelId : 2 ,
918+ } ,
919+ ] ,
920+ $behaviors : [ new DashboardDatasourceBehaviour ( { } ) ] ,
921+ } ) ,
922+ } ) ;
923+
924+ const scene = new DashboardScene ( {
925+ title : 'hello' ,
926+ uid : 'dash-1' ,
927+ meta : {
928+ canEdit : true ,
929+ } ,
930+ body : DefaultGridLayoutManager . fromVizPanels ( [ sourcePanel1 , sourcePanel2 , mixedDSPanel ] ) ,
931+ } ) ;
932+
933+ activateFullSceneTree ( scene ) ;
934+
935+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
936+
937+ const spy = jest . spyOn ( mixedDSPanel . state . $data as SceneQueryRunner , 'runQueries' ) . mockImplementation ( ) ;
938+
939+ // Trigger transformer reprocessing on panel 1
940+ ( sourcePanel1 . state . $data as SceneDataTransformer ) . setState ( {
941+ data : { state : LoadingState . Done , series : [ ] , timeRange : getDefaultTimeRange ( ) } ,
942+ } ) ;
943+
944+ expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
945+
946+ // Trigger transformer reprocessing on panel 2
947+ ( sourcePanel2 . state . $data as SceneDataTransformer ) . setState ( {
948+ data : { state : LoadingState . Done , series : [ ] , timeRange : getDefaultTimeRange ( ) } ,
949+ } ) ;
950+
951+ expect ( spy ) . toHaveBeenCalledTimes ( 2 ) ;
952+ } ) ;
596953 } ) ;
597954
598955 it ( 'Should re-run query after transformations reprocess' , async ( ) => {
0 commit comments