diff --git a/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx b/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx index e619c1911d365a..91edfcc5fc3b22 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/empty_screen/dashboard_empty_screen.tsx @@ -52,13 +52,6 @@ export function DashboardEmptyScreen() { const isEditMode = useMemo(() => { return viewMode === 'edit'; }, [viewMode]); - const { originatingPath, originatingApp } = useMemo(() => { - const appContext = dashboardApi.getAppContext(); - return { - originatingApp: appContext?.currentAppId, - originatingPath: appContext?.getCurrentPath?.() ?? '', - }; - }, [dashboardApi]); const goToLens = useCallback(() => { if (!lensAlias || !lensAlias.alias) return; @@ -70,22 +63,16 @@ export function DashboardEmptyScreen() { if (trackUiMetric) { trackUiMetric(METRIC_TYPE.CLICK, `${lensAlias.name}:create`); } + const appContext = dashboardApi.getAppContext(); getStateTransfer().navigateToEditor(lensAlias.alias.app, { path: lensAlias.alias.path, state: { - originatingApp, - originatingPath, + originatingApp: appContext?.currentAppId, + originatingPath: appContext?.getCurrentPath?.() ?? '', searchSessionId: search.session.getSessionId(), }, }); - }, [ - getStateTransfer, - lensAlias, - originatingApp, - originatingPath, - search.session, - usageCollection, - ]); + }, [getStateTransfer, lensAlias, dashboardApi, search.session, usageCollection]); // TODO replace these SVGs with versions from EuiIllustration as soon as it becomes available. const imageUrl = basePath.prepend( diff --git a/src/plugins/visualizations/public/embeddable/initialize_edit_api.ts b/src/plugins/visualizations/public/embeddable/initialize_edit_api.ts new file mode 100644 index 00000000000000..818847a6e92d63 --- /dev/null +++ b/src/plugins/visualizations/public/embeddable/initialize_edit_api.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { i18n } from '@kbn/i18n'; +import { + PublishingSubject, + apiHasAppContext, + apiPublishesTimeRange, +} from '@kbn/presentation-publishing'; +import { TimeRange } from '@kbn/es-query'; +import type { Vis } from '../vis'; +import { urlFor } from '../utils/saved_visualize_utils'; +import { getCapabilities, getEmbeddable } from '../services'; + +export function initializeEditApi({ + customTimeRange$, + description$, + parentApi, + savedObjectId$, + searchSessionId$, + title$, + vis$, + uuid, +}: { + customTimeRange$: PublishingSubject; + description$: PublishingSubject; + parentApi?: unknown; + savedObjectId$: PublishingSubject; + searchSessionId$: PublishingSubject; + title$: PublishingSubject; + vis$: PublishingSubject; + uuid: string; +}) { + return !parentApi || !apiHasAppContext(parentApi) + ? {} + : { + getTypeDisplayName: () => + i18n.translate('visualizations.displayName', { + defaultMessage: 'visualization', + }), + onEdit: async () => { + const stateTransferService = getEmbeddable().getStateTransfer(); + const visId = savedObjectId$.getValue(); + const editPath = visId ? urlFor(visId) : '#/edit_by_value'; + const parentTimeRange = apiPublishesTimeRange(parentApi) + ? parentApi.timeRange$.getValue() + : {}; + const customTimeRange = customTimeRange$.getValue(); + const parentApiContext = parentApi.getAppContext(); + + await stateTransferService.navigateToEditor('visualize', { + path: editPath, + state: { + embeddableId: uuid, + valueInput: { + savedVis: vis$.getValue().serialize(), + title: title$.getValue(), + description: description$.getValue(), + timeRange: customTimeRange ?? parentTimeRange, + }, + originatingApp: parentApiContext?.currentAppId, + searchSessionId: searchSessionId$.getValue() || undefined, + originatingPath: parentApiContext?.getCurrentPath?.(), + }, + }); + }, + isEditingEnabled: () => { + const readOnly = Boolean(vis$.getValue().type.disableEdit); + if (readOnly) return false; + const capabilities = getCapabilities(); + const isByValue = !savedObjectId$.getValue(); + if (isByValue) + return Boolean( + capabilities.dashboard?.showWriteControls && capabilities.visualize?.show + ); + else return Boolean(capabilities.visualize?.save); + }, + }; +} diff --git a/src/plugins/visualizations/public/embeddable/types.ts b/src/plugins/visualizations/public/embeddable/types.ts index 2536b478debb40..f4215d923e1d5c 100644 --- a/src/plugins/visualizations/public/embeddable/types.ts +++ b/src/plugins/visualizations/public/embeddable/types.ts @@ -89,7 +89,7 @@ export const isVisualizeRuntimeState = (state: unknown): state is VisualizeRunti ); }; -export type VisualizeApi = HasEditCapabilities & +export type VisualizeApi = Partial & PublishesDataViews & PublishesDataLoading & HasVisualizeConfig & diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx index 8e1861af15a984..e779282fa147a6 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx @@ -22,14 +22,12 @@ import { i18n } from '@kbn/i18n'; import { dispatchRenderComplete } from '@kbn/kibana-utils-plugin/public'; import { apiPublishesSettings } from '@kbn/presentation-containers'; import { - apiHasAppContext, apiHasDisableTriggers, apiHasExecutionContext, apiIsOfType, apiPublishesTimeRange, apiPublishesTimeslice, apiPublishesUnifiedSearch, - apiPublishesViewMode, fetch$, getUnchangingComparator, initializeTimeRange, @@ -42,9 +40,8 @@ import React, { useEffect, useRef } from 'react'; import { BehaviorSubject, switchMap } from 'rxjs'; import { VISUALIZE_APP_NAME, VISUALIZE_EMBEDDABLE_TYPE } from '../../common/constants'; import { VIS_EVENT_TO_TRIGGER } from './events'; -import { getCapabilities, getInspector, getUiActions, getUsageCollection } from '../services'; +import { getInspector, getUiActions, getUsageCollection } from '../services'; import { ACTION_CONVERT_TO_LENS } from '../triggers'; -import { urlFor } from '../utils/saved_visualize_utils'; import type { SerializedVis, Vis } from '../vis'; import { createVisInstance } from './create_vis_instance'; import { getExpressionRendererProps } from './get_expression_renderer_props'; @@ -58,6 +55,7 @@ import { VisualizeSerializedState, isVisualizeSavedObjectState, } from './types'; +import { initializeEditApi } from './initialize_edit_api'; export const getVisualizeEmbeddableFactory: (deps: { embeddableStart: EmbeddableStart; @@ -148,10 +146,6 @@ export const getVisualizeEmbeddableFactory: (deps: { const searchSessionId$ = new BehaviorSubject(''); - const viewMode$ = apiPublishesViewMode(parentApi) - ? parentApi.viewMode - : new BehaviorSubject('view'); - const executionContext = apiHasExecutionContext(parentApi) ? parentApi.executionContext : undefined; @@ -160,8 +154,6 @@ export const getVisualizeEmbeddableFactory: (deps: { ? parentApi.disableTriggers : undefined; - const parentApiContext = apiHasAppContext(parentApi) ? parentApi.getAppContext() : undefined; - const inspectorAdapters$ = new BehaviorSubject>({}); // Track data views @@ -209,47 +201,16 @@ export const getVisualizeEmbeddableFactory: (deps: { }, getVis: () => vis$.getValue(), getInspectorAdapters: () => inspectorAdapters$.getValue(), - getTypeDisplayName: () => - i18n.translate('visualizations.displayName', { - defaultMessage: 'visualization', - }), - onEdit: async () => { - const stateTransferService = embeddableStart.getStateTransfer(); - const visId = savedObjectId$.getValue(); - const editPath = visId ? urlFor(visId) : '#/edit_by_value'; - const parentTimeRange = apiPublishesTimeRange(parentApi) - ? parentApi.timeRange$.getValue() - : {}; - const customTimeRange = customTimeRangeApi.timeRange$.getValue(); - - await stateTransferService.navigateToEditor('visualize', { - path: editPath, - state: { - embeddableId: uuid, - valueInput: { - savedVis: vis$.getValue().serialize(), - title: api.panelTitle?.getValue(), - description: api.panelDescription?.getValue(), - timeRange: customTimeRange ?? parentTimeRange, - }, - originatingApp: parentApiContext?.currentAppId ?? '', - searchSessionId: searchSessionId$.getValue() || undefined, - originatingPath: parentApiContext?.getCurrentPath?.(), - }, - }); - }, - isEditingEnabled: () => { - if (viewMode$.getValue() !== 'edit') return false; - const readOnly = Boolean(vis$.getValue().type.disableEdit); - if (readOnly) return false; - const capabilities = getCapabilities(); - const isByValue = !savedObjectId$.getValue(); - if (isByValue) - return Boolean( - capabilities.dashboard?.showWriteControls && capabilities.visualize?.show - ); - else return Boolean(capabilities.visualize?.save); - }, + ...initializeEditApi({ + customTimeRange$: customTimeRangeApi.timeRange$, + description$: titlesApi.panelDescription, + parentApi, + savedObjectId$, + searchSessionId$, + title$: titlesApi.panelTitle, + vis$, + uuid, + }), updateVis: async (visUpdates) => { const currentSerializedVis = vis$.getValue().serialize(); serializedVis$.next({