diff --git a/src/platform/plugins/shared/controls/tsconfig.json b/src/platform/plugins/shared/controls/tsconfig.json index 467a0d5adadf2..568222535902c 100644 --- a/src/platform/plugins/shared/controls/tsconfig.json +++ b/src/platform/plugins/shared/controls/tsconfig.json @@ -40,7 +40,7 @@ "@kbn/controls-schemas", "@kbn/presentation-util", "@kbn/control-group-renderer", - "@kbn/controls-renderer" + "@kbn/controls-renderer", ], "exclude": ["target/**/*"] } diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/esql_variables_manager.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/esql_variables_manager.ts index a91ea35aa1155..3ac67a27c60b3 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/esql_variables_manager.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/esql_variables_manager.ts @@ -7,30 +7,88 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { BehaviorSubject } from 'rxjs'; -import type { PublishesESQLVariable } from '@kbn/esql-types'; +import type { Observable } from 'rxjs'; +import { BehaviorSubject, combineLatest, map, switchMap } from 'rxjs'; import { apiPublishesESQLVariable, type ESQLControlVariable } from '@kbn/esql-types'; -import { combineCompatibleChildrenApis } from '@kbn/presentation-containers'; import type { DefaultEmbeddableApi } from '@kbn/embeddable-plugin/public'; import type { PublishingSubject } from '@kbn/presentation-publishing'; +import type { AggregateQuery } from '@kbn/es-query'; +import { getESQLQueryVariables } from '@kbn/esql-utils'; export const initializeESQLVariablesManager = ( children$: PublishingSubject<{ [key: string]: DefaultEmbeddableApi }> ) => { const esqlVariables$ = new BehaviorSubject([]); - const childrenESQLVariablesSubscription = combineCompatibleChildrenApis< - PublishesESQLVariable, - ESQLControlVariable[] - >({ children$ }, 'esqlVariable$', apiPublishesESQLVariable, []).subscribe((newESQLVariables) => { - esqlVariables$.next(newESQLVariables); - }); + const esqlRelatedPanels$ = new BehaviorSubject>(new Map()); + + const childrenESQLVariablesSubscription = children$ + .pipe( + switchMap((children) => { + const esqlVariableChildren: Array< + Observable<{ uuid: string; variable: ESQLControlVariable }> + > = []; + const esqlQueryChildren: Array> = []; + + for (const child of Object.values(children)) { + if (apiPublishesESQLQuery(child)) { + esqlQueryChildren.push( + child.query$.pipe(map(({ esql }) => ({ esql, uuid: child.uuid }))) + ); + } else if (apiPublishesESQLVariable(child)) { + esqlVariableChildren.push( + child.esqlVariable$.pipe(map((variable) => ({ variable, uuid: child.uuid }))) + ); + } + } + + return combineLatest([ + combineLatest(esqlVariableChildren), + combineLatest(esqlQueryChildren), + ]); + }) + ) + .subscribe(([esqlVariablesWithUUIDs, esqlQueries]) => { + esqlVariables$.next(esqlVariablesWithUUIDs.map(({ variable }) => variable)); + + const nextESQLRelatedPanels: Map = new Map(); + for (const { esql, uuid } of esqlQueries) { + const variables = getESQLQueryVariables(esql); + if (variables.length > 0) { + const relatedPanelUUIDs = variables + .map( + (variableName) => + esqlVariablesWithUUIDs.find(({ variable: { key } }) => key === variableName)?.uuid + ) + .filter(Boolean) as string[]; + nextESQLRelatedPanels.set(uuid, relatedPanelUUIDs); + + for (const relatedUUID of relatedPanelUUIDs) { + nextESQLRelatedPanels.set(relatedUUID, [ + ...(nextESQLRelatedPanels.get(relatedUUID) ?? []), + uuid, + ]); + } + } + } + esqlRelatedPanels$.next(nextESQLRelatedPanels); + }); return { api: { esqlVariables$, }, + internalApi: { + esqlRelatedPanels$, + }, cleanup: () => { childrenESQLVariablesSubscription.unsubscribe(); }, }; }; + +interface PublishesESQLQuery { + query$: PublishingSubject; +} +const apiPublishesESQLQuery = (api: unknown): api is PublishesESQLQuery => + Boolean((api as PublishesESQLQuery).query$) && + 'esql' in (api as PublishesESQLQuery).query$?.value; diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts index a02661cd159ea..ed066a32374b1 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/get_dashboard_api.ts @@ -9,7 +9,7 @@ import type { Reference } from '@kbn/content-management-utils'; import type { EmbeddablePackageState } from '@kbn/embeddable-plugin/public'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, map } from 'rxjs'; import { v4 } from 'uuid'; import { getReferencesForPanelId } from '../../common'; @@ -137,6 +137,27 @@ export function getDashboardApi({ const trackOverlayApi = initializeTrackOverlay(trackPanel.setFocusedPanelId); + const arePanelsRelated$ = new BehaviorSubject<(a: string, b: string) => boolean>(() => false); + + const relatedPanelSubscription = esqlVariablesManager.internalApi.esqlRelatedPanels$ + .pipe( + map((esqlRelatedPanels) => (uuid: string) => { + const relatedPanelUUIDs = []; + + const esqlRelatedPanelUUIDs = esqlRelatedPanels.get(uuid); + if (esqlRelatedPanelUUIDs) { + for (const relatedUUID of esqlRelatedPanelUUIDs) { + relatedPanelUUIDs.push(relatedUUID); + } + } + + return relatedPanelUUIDs; + }) + ) + .subscribe((getRelatedPanels) => + arePanelsRelated$.next((a: string, b: string) => getRelatedPanels(a).includes(b)) + ); + const isFetchPaused$ = new BehaviorSubject(false); // TODO: Make this work and probably move it to another file const dashboardApi = { @@ -243,6 +264,7 @@ export function getDashboardApi({ ...unifiedSearchManager.internalApi, dashboardContainerRef$, setDashboardContainerRef: (ref: HTMLElement | null) => dashboardContainerRef$.next(ref), + arePanelsRelated$, // serializeControls: () => controlGroupManager.internalApi.serializeControlGroup(), // untilControlsInitialized: controlGroupManager.internalApi.untilControlsInitialized, }; @@ -269,6 +291,7 @@ export function getDashboardApi({ layoutManager.cleanup(); esqlVariablesManager.cleanup(); timesliceManager.cleanup(); + relatedPanelSubscription.unsubscribe(); }, }; } diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_api/types.ts b/src/platform/plugins/shared/dashboard/public/dashboard_api/types.ts index fd2ce9133c6ae..cdece28039cac 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_api/types.ts +++ b/src/platform/plugins/shared/dashboard/public/dashboard_api/types.ts @@ -182,4 +182,5 @@ export interface DashboardInternalApi { controlGroupInput: ControlsGroupState | undefined; controlGroupReferences: Reference[]; }; + arePanelsRelated$: BehaviorSubject<(a: string, b: string) => boolean>; } diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_renderer/grid/dashboard_grid_item.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_renderer/grid/dashboard_grid_item.tsx index 558ad242e6554..83abe7bab37f1 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_renderer/grid/dashboard_grid_item.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_renderer/grid/dashboard_grid_item.tsx @@ -62,6 +62,7 @@ export const Item = React.forwardRef( useMargins, viewMode, dashboardContainerRef, + arePanelsRelated, ] = useBatchedPublishingSubjects( dashboardApi.highlightPanelId$, dashboardApi.scrollToPanelId$, @@ -69,13 +70,17 @@ export const Item = React.forwardRef( dashboardApi.focusedPanelId$, dashboardApi.settings.useMargins$, dashboardApi.viewMode$, - dashboardInternalApi.dashboardContainerRef$ + dashboardInternalApi.dashboardContainerRef$, + dashboardInternalApi.arePanelsRelated$ ); const expandPanel = expandedPanelId !== undefined && expandedPanelId === id; const hidePanel = expandedPanelId !== undefined && expandedPanelId !== id; const focusPanel = focusedPanelId !== undefined && focusedPanelId === id; - const blurPanel = focusedPanelId !== undefined && focusedPanelId !== id; + const blurPanel = + focusedPanelId !== undefined && + focusedPanelId !== id && + !arePanelsRelated(id, focusedPanelId); const classes = classNames('dshDashboardGrid__item', { 'dshDashboardGrid__item--expanded': expandPanel, 'dshDashboardGrid__item--hidden': hidePanel,