[Embeddables Rebuild] Move legacy viusalize embeddable to legacy/embeddable (#192002)

## Summary

In visualize, renames the new `react_embeddable` folder to just
`embeddable`, and moves the previous `embeddable` folder to
`legacy/embeddable`.

Keeps a few constants and interfaces that are reused in the new
embeddable in the `embeddable` folder, and imports them into
`legacy/embeddable` where needed.

---------

Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>
This commit is contained in:
Zacqary Adam Xeper 2024-09-12 02:29:45 -05:00 committed by GitHub
parent f7dc0570bb
commit cd970c6c33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1283 additions and 1268 deletions

View file

@ -23,7 +23,7 @@ import {
VisualizeEmbeddable,
VisualizeInput,
VisualizeOutput,
} from '@kbn/visualizations-plugin/public/embeddable/visualize_embeddable';
} from '@kbn/visualizations-plugin/public/legacy/embeddable/visualize_embeddable';
const INPUT_KEY = 'portableDashboard:saveExample:input';

View file

@ -26,7 +26,7 @@ import {
import { Action } from '@kbn/ui-actions-plugin/public';
import React from 'react';
import { take } from 'rxjs';
import { apiHasVisualizeConfig, HasVisualizeConfig } from '../embeddable';
import { apiHasVisualizeConfig, HasVisualizeConfig } from '../legacy/embeddable';
import {
apiHasExpressionVariables,
HasExpressionVariables,

View file

@ -10,7 +10,7 @@
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query';
import { ExpressionRendererEvent, ExpressionRendererParams } from '@kbn/expressions-plugin/public';
import { toExpressionAst } from '../embeddable/to_ast';
import { toExpressionAst } from './to_ast';
import { getExecutionContext, getTimeFilter } from '../services';
import type { VisParams } from '../types';
import type { Vis } from '../vis';

View file

@ -7,4 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export { getVisualizeEmbeddableFactory } from './visualize_embeddable';
export const getVisualizeEmbeddableFactoryLazy = async () => {
const { getVisualizeEmbeddableFactory } = await import('./visualize_embeddable');
return getVisualizeEmbeddableFactory;
};

View file

@ -7,11 +7,5 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export { VisualizeEmbeddableFactory } from './visualize_embeddable_factory';
export { VISUALIZE_EMBEDDABLE_TYPE, COMMON_VISUALIZATION_GROUPING } from './constants';
export { getVisualizeEmbeddableFactoryLazy } from './get_visualize_embeddable_factory_lazy';
export { VIS_EVENT_TO_TRIGGER } from './events';
export { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
export type { VisualizeEmbeddable, VisualizeInput } from './visualize_embeddable';
export { type HasVisualizeConfig, apiHasVisualizeConfig } from './interfaces/has_visualize_config';

View file

@ -22,7 +22,7 @@ import {
SerializedTitles,
} from '@kbn/presentation-publishing';
import { DeepPartial } from '@kbn/utility-types';
import { HasVisualizeConfig } from '../embeddable';
import { HasVisualizeConfig } from '../legacy/embeddable';
import type { Vis, VisParams, VisSavedObject } from '../types';
import type { SerializedVis } from '../vis';

View file

@ -10,7 +10,7 @@
import { PublicContract } from '@kbn/utility-types';
import { PluginInitializerContext } from '@kbn/core/public';
import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin';
import type { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable';
import type { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './legacy/embeddable';
export function plugin(initializerContext: PluginInitializerContext) {
return new VisualizationsPlugin(initializerContext);
@ -18,11 +18,8 @@ export function plugin(initializerContext: PluginInitializerContext) {
/** @public static code */
export { TypesService } from './vis_types/types_service';
export {
apiHasVisualizeConfig,
VIS_EVENT_TO_TRIGGER,
COMMON_VISUALIZATION_GROUPING,
} from './embeddable';
export { VIS_EVENT_TO_TRIGGER } from './embeddable';
export { apiHasVisualizeConfig, COMMON_VISUALIZATION_GROUPING } from './legacy/embeddable';
export { VisualizationContainer } from './components';
export { getVisSchemas } from './vis_schemas';
@ -38,13 +35,13 @@ export type {
VisualizationClient,
SerializableAttributes,
} from './vis_types';
export type { VisualizeEditorInput } from './react_embeddable/types';
export type { VisualizeEditorInput } from './embeddable/types';
export type { Vis, SerializedVis, SerializedVisData, VisData } from './vis';
export type VisualizeEmbeddableFactoryContract = PublicContract<VisualizeEmbeddableFactory>;
export type VisualizeEmbeddableContract = PublicContract<VisualizeEmbeddable>;
export type { SchemaConfig } from '../common/types';
export { updateOldState } from './legacy/vis_update_state';
export type { VisualizeInput, VisualizeEmbeddable, HasVisualizeConfig } from './embeddable';
export type { VisualizeInput, VisualizeEmbeddable, HasVisualizeConfig } from './legacy/embeddable';
export type { PersistedState } from './persisted_state';
export type {
ISavedVis,

View file

@ -9,7 +9,7 @@
import { i18n } from '@kbn/i18n';
export { VISUALIZE_EMBEDDABLE_TYPE } from '../../common/constants';
export { VISUALIZE_EMBEDDABLE_TYPE } from '../../../common/constants';
export const COMMON_VISUALIZATION_GROUPING = [
{

View file

@ -9,7 +9,7 @@
import { IContainer, ErrorEmbeddable, AttributeService } from '@kbn/embeddable-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import { Vis } from '../types';
import { Vis } from '../../types';
import type {
VisualizeInput,
VisualizeEmbeddable,
@ -17,8 +17,8 @@ import type {
VisualizeByReferenceInput,
VisualizeSavedObjectAttributes,
} from './visualize_embeddable';
import { getHttp, getTimeFilter, getCapabilities } from '../services';
import { urlFor } from '../utils/saved_visualize_utils';
import { getHttp, getTimeFilter, getCapabilities } from '../../services';
import { urlFor } from '../../utils/saved_visualize_utils';
import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
import { createVisualizeEmbeddableAsync } from './visualize_embeddable_async';

View file

@ -0,0 +1,18 @@
/*
* 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".
*/
export { VisualizeEmbeddableFactory } from './visualize_embeddable_factory';
export { VISUALIZE_EMBEDDABLE_TYPE, COMMON_VISUALIZATION_GROUPING } from './constants';
export { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
export type { VisualizeEmbeddable, VisualizeInput } from './visualize_embeddable';
export {
type HasVisualizeConfig,
apiHasVisualizeConfig,
} from '../../embeddable/interfaces/has_visualize_config';

View file

@ -0,0 +1,717 @@
/*
* 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 _, { get } from 'lodash';
import { Subscription, ReplaySubject, mergeMap } from 'rxjs';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { render } from 'react-dom';
import { EuiLoadingChart } from '@elastic/eui';
import { Filter, onlyDisabledFiltersChanged, Query, TimeRange } from '@kbn/es-query';
import type { KibanaExecutionContext, SavedObjectAttributes } from '@kbn/core/public';
import type { ErrorLike } from '@kbn/expressions-plugin/common';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { TimefilterContract } from '@kbn/data-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import { Warnings } from '@kbn/charts-plugin/public';
import { hasUnsupportedDownsampledAggregationFailure } from '@kbn/search-response-warnings';
import {
Adapters,
AttributeService,
Embeddable,
EmbeddableInput,
EmbeddableOutput,
FilterableEmbeddable,
IContainer,
ReferenceOrValueEmbeddable,
SavedObjectEmbeddableInput,
} from '@kbn/embeddable-plugin/public';
import {
ExpressionAstExpression,
ExpressionLoader,
ExpressionRenderError,
IExpressionLoaderParams,
} from '@kbn/expressions-plugin/public';
import type { RenderMode } from '@kbn/expressions-plugin/common';
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/public';
import { mapAndFlattenFilters } from '@kbn/data-plugin/public';
import { isChartSizeEvent } from '@kbn/chart-expressions-common';
import { isFallbackDataView } from '../../visualize_app/utils';
import { VisualizationMissedSavedObjectError } from '../../components/visualization_missed_saved_object_error';
import VisualizationError from '../../components/visualization_error';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
import { SerializedVis, Vis } from '../../vis';
import { getApplication, getExecutionContext, getExpressions, getUiActions } from '../../services';
import { VIS_EVENT_TO_TRIGGER } from '../../embeddable/events';
import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
import { getSavedVisualization } from '../../utils/saved_visualize_utils';
import { VisSavedObject } from '../../types';
import { toExpressionAst } from '../../embeddable/to_ast';
export interface VisualizeEmbeddableConfiguration {
vis: Vis;
indexPatterns?: DataView[];
editPath: string;
editUrl: string;
capabilities: { visualizeSave: boolean; dashboardSave: boolean; visualizeOpen: boolean };
deps: VisualizeEmbeddableFactoryDeps;
}
export interface VisualizeInput extends EmbeddableInput {
vis?: {
colors?: { [key: string]: string };
};
savedVis?: SerializedVis;
renderMode?: RenderMode;
table?: unknown;
query?: Query;
filters?: Filter[];
timeRange?: TimeRange;
timeslice?: [number, number];
}
export interface VisualizeOutput extends EmbeddableOutput {
editPath: string;
editApp: string;
editUrl: string;
indexPatterns?: DataView[];
visTypeName: string;
}
export type VisualizeSavedObjectAttributes = SavedObjectAttributes & {
title: string;
vis?: Vis;
savedVis?: VisSavedObject;
};
export type VisualizeByValueInput = { attributes: VisualizeSavedObjectAttributes } & VisualizeInput;
export type VisualizeByReferenceInput = SavedObjectEmbeddableInput & VisualizeInput;
/** @deprecated
* VisualizeEmbeddable is no longer registered with the legacy embeddable system and is only
* used within the visualize editor.
*/
export class VisualizeEmbeddable
extends Embeddable<VisualizeInput, VisualizeOutput>
implements
ReferenceOrValueEmbeddable<VisualizeByValueInput, VisualizeByReferenceInput>,
FilterableEmbeddable
{
private handler?: ExpressionLoader;
private timefilter: TimefilterContract;
private timeRange?: TimeRange;
private query?: Query;
private filters?: Filter[];
private searchSessionId?: string;
private syncColors?: boolean;
private syncTooltips?: boolean;
private syncCursor?: boolean;
private embeddableTitle?: string;
private visCustomizations?: Pick<VisualizeInput, 'vis' | 'table'>;
private subscriptions: Subscription[] = [];
private expression?: ExpressionAstExpression;
private vis: Vis;
private domNode: any;
private warningDomNode: any;
public readonly type = VISUALIZE_EMBEDDABLE_TYPE;
private abortController?: AbortController;
private readonly deps: VisualizeEmbeddableFactoryDeps;
private readonly inspectorAdapters?: Adapters;
private attributeService?: AttributeService<
VisualizeSavedObjectAttributes,
VisualizeByValueInput,
VisualizeByReferenceInput
>;
private expressionVariables: Record<string, unknown> | undefined;
private readonly expressionVariablesSubject = new ReplaySubject<
Record<string, unknown> | undefined
>(1);
constructor(
timefilter: TimefilterContract,
{ vis, editPath, editUrl, indexPatterns, deps, capabilities }: VisualizeEmbeddableConfiguration,
initialInput: VisualizeInput,
attributeService?: AttributeService<
VisualizeSavedObjectAttributes,
VisualizeByValueInput,
VisualizeByReferenceInput
>,
parent?: IContainer
) {
super(
initialInput,
{
defaultTitle: vis.title,
defaultDescription: vis.description,
editPath,
editApp: 'visualize',
editUrl,
indexPatterns,
visTypeName: vis.type.name,
},
parent
);
this.deps = deps;
this.timefilter = timefilter;
this.syncColors = this.input.syncColors;
this.syncTooltips = this.input.syncTooltips;
this.syncCursor = this.input.syncCursor;
this.searchSessionId = this.input.searchSessionId;
this.query = this.input.query;
this.embeddableTitle = this.getTitle();
this.vis = vis;
this.vis.uiState.on('change', this.uiStateChangeHandler);
this.vis.uiState.on('reload', this.reload);
this.attributeService = attributeService;
if (this.attributeService) {
const readOnly = Boolean(vis.type.disableEdit);
const isByValue = !this.inputIsRefType(initialInput);
const editable = readOnly
? false
: capabilities.visualizeSave ||
(isByValue && capabilities.dashboardSave && capabilities.visualizeOpen);
this.updateOutput({ ...this.getOutput(), editable });
}
this.subscriptions.push(
this.getInput$().subscribe(() => {
const isDirty = this.handleChanges();
if (isDirty && this.handler) {
this.updateHandler();
}
})
);
const inspectorAdapters = this.vis.type.inspectorAdapters;
if (inspectorAdapters) {
this.inspectorAdapters =
typeof inspectorAdapters === 'function' ? inspectorAdapters() : inspectorAdapters;
}
}
public reportsEmbeddableLoad() {
return true;
}
public getVis() {
return this.vis;
}
/**
* Gets the Visualize embeddable's local filters
* @returns Local/panel-level array of filters for Visualize embeddable
*/
public getFilters() {
const filters = this.vis.serialize().data.searchSource?.filter ?? [];
// must clone the filters so that it's not read only, because mapAndFlattenFilters modifies the array
return mapAndFlattenFilters(_.cloneDeep(filters));
}
/**
* Gets the Visualize embeddable's local query
* @returns Local/panel-level query for Visualize embeddable
*/
public getQuery() {
return this.vis.serialize().data.searchSource.query;
}
public getInspectorAdapters = () => {
if (!this.handler || (this.inspectorAdapters && !Object.keys(this.inspectorAdapters).length)) {
return undefined;
}
return this.handler.inspect();
};
public openInspector = () => {
if (!this.handler) return;
const adapters = this.handler.inspect();
if (!adapters) return;
return this.deps.start().plugins.inspector.open(adapters, {
title:
this.getTitle() ||
i18n.translate('visualizations.embeddable.inspectorTitle', {
defaultMessage: 'Inspector',
}),
});
};
/**
* Transfers all changes in the containerState.customization into
* the uiState of this visualization.
*/
public transferCustomizationsToUiState() {
// Check for changes that need to be forwarded to the uiState
// Since the vis has an own listener on the uiState we don't need to
// pass anything from here to the handler.update method
const visCustomizations = { vis: this.input.vis, table: this.input.table };
if (visCustomizations.vis || visCustomizations.table) {
if (!_.isEqual(visCustomizations, this.visCustomizations)) {
this.visCustomizations = visCustomizations;
// Turn this off or the uiStateChangeHandler will fire for every modification.
this.vis.uiState.off('change', this.uiStateChangeHandler);
this.vis.uiState.clearAllKeys();
Object.entries(visCustomizations).forEach(([key, value]) => {
if (value) {
this.vis.uiState.set(key, value);
}
});
this.vis.uiState.on('change', this.uiStateChangeHandler);
}
} else if (this.parent) {
this.vis.uiState.clearAllKeys();
}
}
private handleChanges(): boolean {
this.transferCustomizationsToUiState();
let dirty = false;
// Check if timerange has changed
const nextTimeRange =
this.input.timeslice !== undefined
? {
from: new Date(this.input.timeslice[0]).toISOString(),
to: new Date(this.input.timeslice[1]).toISOString(),
mode: 'absolute' as 'absolute',
}
: this.input.timeRange;
if (!_.isEqual(nextTimeRange, this.timeRange)) {
this.timeRange = _.cloneDeep(nextTimeRange);
dirty = true;
}
// Check if filters has changed
if (!onlyDisabledFiltersChanged(this.input.filters, this.filters)) {
this.filters = this.input.filters;
dirty = true;
}
// Check if query has changed
if (!_.isEqual(this.input.query, this.query)) {
this.query = this.input.query;
dirty = true;
}
if (this.searchSessionId !== this.input.searchSessionId) {
this.searchSessionId = this.input.searchSessionId;
dirty = true;
}
if (this.syncColors !== this.input.syncColors) {
this.syncColors = this.input.syncColors;
dirty = true;
}
if (this.syncTooltips !== this.input.syncTooltips) {
this.syncTooltips = this.input.syncTooltips;
dirty = true;
}
if (this.syncCursor !== this.input.syncCursor) {
this.syncCursor = this.input.syncCursor;
dirty = true;
}
if (this.embeddableTitle !== this.getTitle()) {
this.embeddableTitle = this.getTitle();
dirty = true;
}
if (this.vis.description && this.domNode) {
this.domNode.setAttribute('data-description', this.vis.description);
}
return dirty;
}
private handleWarnings() {
const warnings: React.ReactNode[] = [];
if (this.getInspectorAdapters()?.requests) {
this.deps
.start()
.plugins.data.search.showWarnings(this.getInspectorAdapters()!.requests!, (warning) => {
if (hasUnsupportedDownsampledAggregationFailure(warning)) {
warnings.push(
i18n.translate('visualizations.embeddable.tsdbRollupWarning', {
defaultMessage:
'Visualization uses a function that is unsupported by rolled up data. Select a different function or change the time range.',
})
);
return true;
}
if (this.vis.type.suppressWarnings?.()) {
// if the vis type wishes to supress all warnings, return true so the default logic won't pick it up
return true;
}
});
}
if (this.warningDomNode) {
const { core } = this.deps.start();
render(
<KibanaRenderContextProvider {...core}>
<Warnings warnings={warnings || []} />
</KibanaRenderContextProvider>,
this.warningDomNode
);
}
}
// this is a hack to make editor still work, will be removed once we clean up editor
// @ts-ignore
hasInspector = () => Boolean(this.getInspectorAdapters());
onContainerLoading = () => {
this.renderComplete.dispatchInProgress();
this.updateOutput({
...this.getOutput(),
loading: true,
rendered: false,
error: undefined,
});
};
onContainerData = () => {
this.handleWarnings();
this.updateOutput({
...this.getOutput(),
loading: false,
});
};
onContainerRender = () => {
this.renderComplete.dispatchComplete();
this.updateOutput({
...this.getOutput(),
rendered: true,
});
};
onContainerError = (error: ExpressionRenderError) => {
if (this.abortController) {
this.abortController.abort();
}
this.renderComplete.dispatchError();
if (isFallbackDataView(this.vis.data.indexPattern)) {
error = new Error(
i18n.translate('visualizations.missedDataView.errorMessage', {
defaultMessage: `Could not find the {type}: {id}`,
values: {
id: this.vis.data.indexPattern.id ?? '-',
type: this.vis.data.savedSearchId
? i18n.translate('visualizations.noSearch.label', {
defaultMessage: 'search',
})
: i18n.translate('visualizations.noDataView.label', {
defaultMessage: 'data view',
}),
},
})
);
}
this.updateOutput({
...this.getOutput(),
rendered: true,
error,
});
};
/**
*
* @param {Element} domNode
*/
public async render(domNode: HTMLElement) {
this.timeRange = _.cloneDeep(this.input.timeRange);
this.transferCustomizationsToUiState();
const div = document.createElement('div');
div.className = `visualize panel-content panel-content--fullWidth`;
domNode.appendChild(div);
const warningDiv = document.createElement('div');
warningDiv.className = 'visPanel__warnings';
domNode.appendChild(warningDiv);
this.warningDomNode = warningDiv;
this.domNode = div;
super.render(this.domNode);
const { core } = this.deps.start();
render(
<KibanaRenderContextProvider {...core}>
<div className="visChart__spinner">
<EuiLoadingChart mono size="l" />
</div>
</KibanaRenderContextProvider>,
this.domNode
);
const expressions = getExpressions();
this.handler = await expressions.loader(this.domNode, undefined, {
renderMode: this.input.renderMode || 'view',
onRenderError: (element: HTMLElement, error: ExpressionRenderError) => {
this.onContainerError(error);
},
executionContext: this.getExecutionContext(),
});
this.subscriptions.push(
this.handler.events$
.pipe(
mergeMap(async (event) => {
// Visualize doesn't respond to sizing events, so ignore.
if (isChartSizeEvent(event)) {
return;
}
if (!this.input.disableTriggers) {
const triggerId = get(VIS_EVENT_TO_TRIGGER, event.name, VIS_EVENT_TO_TRIGGER.filter);
let context;
if (triggerId === VIS_EVENT_TO_TRIGGER.applyFilter) {
context = {
embeddable: this,
timeFieldName: this.vis.data.indexPattern?.timeFieldName!,
...event.data,
};
} else {
context = {
embeddable: this,
data: {
timeFieldName: this.vis.data.indexPattern?.timeFieldName!,
...event.data,
},
};
}
await getUiActions().getTrigger(triggerId).exec(context);
}
})
)
.subscribe()
);
if (this.vis.description) {
div.setAttribute('data-description', this.vis.description);
}
div.setAttribute('data-test-subj', 'visualizationLoader');
div.setAttribute('data-shared-item', '');
this.subscriptions.push(this.handler.loading$.subscribe(this.onContainerLoading));
this.subscriptions.push(this.handler.data$.subscribe(this.onContainerData));
this.subscriptions.push(this.handler.render$.subscribe(this.onContainerRender));
this.subscriptions.push(
this.getUpdated$().subscribe(() => {
const { error } = this.getOutput();
if (error) {
render(this.renderError(error), this.domNode);
}
})
);
await this.updateHandler();
}
private renderError(error: ErrorLike | string) {
const { core } = this.deps.start();
if (isFallbackDataView(this.vis.data.indexPattern)) {
return (
<KibanaRenderContextProvider {...core}>
<VisualizationMissedSavedObjectError
renderMode={this.input.renderMode ?? 'view'}
savedObjectMeta={{
savedObjectType: this.vis.data.savedSearchId ? 'search' : DATA_VIEW_SAVED_OBJECT_TYPE,
}}
application={getApplication()}
message={typeof error === 'string' ? error : error.message}
/>
</KibanaRenderContextProvider>
);
}
return (
<KibanaRenderContextProvider {...core}>
<VisualizationError error={error} />
</KibanaRenderContextProvider>
);
}
public destroy() {
super.destroy();
this.subscriptions.forEach((s) => s.unsubscribe());
this.vis.uiState.off('change', this.uiStateChangeHandler);
this.vis.uiState.off('reload', this.reload);
if (this.handler) {
this.handler.destroy();
this.handler.getElement().remove();
}
}
public reload = async () => {
await this.handleVisUpdate();
};
private getExecutionContext() {
const parentContext = this.parent?.getInput().executionContext || getExecutionContext().get();
const child: KibanaExecutionContext = {
type: 'agg_based',
name: this.vis.type.name,
id: this.vis.id ?? 'new',
description: this.vis.title || this.input.title || this.vis.type.name,
url: this.output.editUrl,
};
return {
...parentContext,
child,
};
}
private async updateHandler() {
const context = this.getExecutionContext();
this.expressionVariables = await this.vis.type.getExpressionVariables?.(
this.vis,
this.timefilter
);
this.expressionVariablesSubject.next(this.expressionVariables);
const expressionParams: IExpressionLoaderParams = {
searchContext: {
timeRange: this.timeRange,
query: this.input.query,
filters: this.input.filters,
disableWarningToasts: true,
},
variables: {
embeddableTitle: this.getTitle(),
...this.expressionVariables,
},
searchSessionId: this.input.searchSessionId,
syncColors: this.input.syncColors,
syncTooltips: this.input.syncTooltips,
syncCursor: this.input.syncCursor,
uiState: this.vis.uiState,
interactive: !this.input.disableTriggers,
inspectorAdapters: this.inspectorAdapters,
executionContext: context,
};
if (this.abortController) {
this.abortController.abort();
}
this.abortController = new AbortController();
const abortController = this.abortController;
try {
this.expression = await toExpressionAst(this.vis, {
timefilter: this.timefilter,
timeRange: this.timeRange,
abortSignal: this.abortController!.signal,
});
} catch (e) {
this.onContainerError(e);
}
if (this.handler && !abortController.signal.aborted) {
this.handler.update(this.expression, expressionParams);
}
}
private handleVisUpdate = async () => {
this.handleChanges();
await this.updateHandler();
};
private uiStateChangeHandler = () => {
this.updateInput({
...this.vis.uiState.toJSON(),
});
};
public supportedTriggers(): string[] {
return this.vis.type.getSupportedTriggers?.(this.vis.params) ?? [];
}
public getExpressionVariables$() {
return this.expressionVariablesSubject.asObservable();
}
public getExpressionVariables() {
return this.expressionVariables;
}
inputIsRefType = (input: VisualizeInput): input is VisualizeByReferenceInput => {
if (!this.attributeService) {
throw new Error('AttributeService must be defined for getInputAsRefType');
}
return this.attributeService.inputIsRefType(input as VisualizeByReferenceInput);
};
getInputAsValueType = async (): Promise<VisualizeByValueInput> => {
const input = {
savedVis: this.vis.serialize(),
};
delete input.savedVis.id;
_.unset(input, 'savedVis.title');
return new Promise<VisualizeByValueInput>((resolve) => {
resolve({ ...(input as VisualizeByValueInput) });
});
};
getInputAsRefType = async (): Promise<VisualizeByReferenceInput> => {
const { plugins, core } = this.deps.start();
const { data, spaces, savedObjectsTaggingOss } = plugins;
const savedVis = await getSavedVisualization({
search: data.search,
dataViews: data.dataViews,
spaces,
savedObjectsTagging: savedObjectsTaggingOss?.getTaggingApi(),
...core,
});
if (!savedVis) {
throw new Error('Error creating a saved vis object');
}
if (!this.attributeService) {
throw new Error('AttributeService must be defined for getInputAsRefType');
}
const saveModalTitle = this.getTitle()
? this.getTitle()
: i18n.translate('visualizations.embeddable.placeholderTitle', {
defaultMessage: 'Placeholder Title',
});
// @ts-ignore
const attributes: VisualizeSavedObjectAttributes = {
savedVis,
vis: this.vis,
title: this.vis.title,
};
return this.attributeService.getInputAsRefType(
{
id: this.id,
attributes,
},
{ showSaveModal: true, saveModalTitle }
);
};
}

View file

@ -28,7 +28,7 @@ import {
AttributeService,
} from '@kbn/embeddable-plugin/public';
import type { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { checkForDuplicateTitle } from '../utils/saved_objects_utils/check_for_duplicate_title';
import { checkForDuplicateTitle } from '../../utils/saved_objects_utils/check_for_duplicate_title';
import type {
VisualizeByReferenceInput,
VisualizeByValueInput,
@ -38,24 +38,24 @@ import type {
VisualizeSavedObjectAttributes,
} from './visualize_embeddable';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
import type { SerializedVis, Vis } from '../vis';
import { createVisAsync } from '../vis_async';
import { getCapabilities, getTypes } from '../services';
import { showNewVisModal } from '../wizard';
import type { SerializedVis, Vis } from '../../vis';
import { createVisAsync } from '../../vis_async';
import { getCapabilities, getTypes } from '../../services';
import { showNewVisModal } from '../../wizard';
import {
convertToSerializedVis,
getSavedVisualization,
saveVisualization,
getFullPath,
} from '../utils/saved_visualize_utils';
} from '../../utils/saved_visualize_utils';
import {
extractControlsReferences,
extractTimeSeriesReferences,
injectTimeSeriesReferences,
injectControlsReferences,
} from '../utils/saved_visualization_references';
} from '../../utils/saved_visualization_references';
import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
import type { VisualizationsStartDeps } from '../plugin';
import type { VisualizationsStartDeps } from '../../plugin';
interface VisualizationAttributes extends SavedObjectAttributes {
title: string;

View file

@ -126,7 +126,8 @@ import {
VisualizationSavedObjectAttributes,
} from '../common/content_management';
import { AddAggVisualizationPanelAction } from './actions/add_agg_vis_action';
import { VisualizeSerializedState } from './react_embeddable/types';
import type { VisualizeSerializedState } from './embeddable/types';
import { getVisualizeEmbeddableFactoryLazy } from './embeddable';
/**
* Interface for this plugin's returned setup/start contracts.
@ -308,7 +309,10 @@ export class VisualizationsPlugin
* this should be replaced to use only scoped history after moving legacy apps to browser routing
*/
const history = createHashHistory();
const { createVisEmbeddableFromObject } = await import('./embeddable');
const [{ createVisEmbeddableFromObject }, { renderApp }] = await Promise.all([
import('./legacy/embeddable'),
import('./visualize_app'),
]);
const services: VisualizeServices = {
...coreStart,
history,
@ -352,7 +356,6 @@ export class VisualizationsPlugin
};
params.element.classList.add('visAppWrapper');
const { renderApp } = await import('./visualize_app');
if (pluginsStart.screenshotMode.isScreenshotMode()) {
params.element.classList.add('visEditorScreenshotModeActive');
// @ts-expect-error TS error, cannot find type declaration for scss
@ -407,7 +410,7 @@ export class VisualizationsPlugin
plugins: { embeddable: embeddableStart, embeddableEnhanced: embeddableEnhancedStart },
} = start();
const { getVisualizeEmbeddableFactory } = await import('./react_embeddable');
const getVisualizeEmbeddableFactory = await getVisualizeEmbeddableFactoryLazy();
return getVisualizeEmbeddableFactory({ embeddableStart, embeddableEnhancedStart });
});
embeddable.registerReactEmbeddableSavedObject<VisualizationSavedObjectAttributes>({

View file

@ -1,549 +0,0 @@
/*
* 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 { EuiEmptyPrompt, EuiFlexGroup, EuiLoadingChart } from '@elastic/eui';
import { isChartSizeEvent } from '@kbn/chart-expressions-common';
import { APPLY_FILTER_TRIGGER } from '@kbn/data-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import { EmbeddableEnhancedPluginStart } from '@kbn/embeddable-enhanced-plugin/public';
import {
EmbeddableStart,
ReactEmbeddableFactory,
SELECT_RANGE_TRIGGER,
} from '@kbn/embeddable-plugin/public';
import { ExpressionRendererParams, useExpressionRenderer } from '@kbn/expressions-plugin/public';
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,
initializeTitles,
useStateFromPublishingSubject,
} from '@kbn/presentation-publishing';
import { apiPublishesSearchSession } from '@kbn/presentation-publishing/interfaces/fetch/publishes_search_session';
import { get, isEmpty, isEqual, isNil, omitBy } from 'lodash';
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 '../embeddable';
import { getCapabilities, 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';
import { saveToLibrary } from './save_to_library';
import { deserializeState, serializeState } from './state';
import {
ExtraSavedObjectProperties,
VisualizeApi,
VisualizeOutputState,
VisualizeRuntimeState,
VisualizeSerializedState,
isVisualizeSavedObjectState,
} from './types';
export const getVisualizeEmbeddableFactory: (deps: {
embeddableStart: EmbeddableStart;
embeddableEnhancedStart?: EmbeddableEnhancedPluginStart;
}) => ReactEmbeddableFactory<VisualizeSerializedState, VisualizeRuntimeState, VisualizeApi> = ({
embeddableStart,
embeddableEnhancedStart,
}) => ({
type: VISUALIZE_EMBEDDABLE_TYPE,
deserializeState,
buildEmbeddable: async (initialState: unknown, buildApi, uuid, parentApi) => {
// Handle state transfer from legacy visualize editor, which uses the legacy visualize embeddable and doesn't
// produce a snapshot state. If buildEmbeddable is passed only a savedObjectId in the state, this means deserializeState
// was never run, and it needs to be invoked manually
const state = isVisualizeSavedObjectState(initialState)
? await deserializeState({
rawState: initialState,
})
: (initialState as VisualizeRuntimeState);
// Initialize dynamic actions
const dynamicActionsApi = embeddableEnhancedStart?.initializeReactEmbeddableDynamicActions(
uuid,
() => titlesApi.panelTitle.getValue(),
state
);
// if it is provided, start the dynamic actions manager
const maybeStopDynamicActions = dynamicActionsApi?.startDynamicActions();
const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state);
// Count renders; mostly used for testing.
const renderCount$ = new BehaviorSubject<number>(0);
const hasRendered$ = new BehaviorSubject<boolean>(false);
// Track vis data and initialize it into a vis instance
const serializedVis$ = new BehaviorSubject<SerializedVis>(state.serializedVis);
const initialVisInstance = await createVisInstance(state.serializedVis);
const vis$ = new BehaviorSubject<Vis>(initialVisInstance);
// Track UI state
const onUiStateChange = () => serializedVis$.next(vis$.getValue().serialize());
initialVisInstance.uiState.on('change', onUiStateChange);
vis$.subscribe((vis) => vis.uiState.on('change', onUiStateChange));
// When the serialized vis changes, update the vis instance
serializedVis$
.pipe(
switchMap(async (serializedVis) => {
const currentVis = vis$.getValue();
if (currentVis) currentVis.uiState.off('change', onUiStateChange);
const vis = await createVisInstance(serializedVis);
const { params, abortController } = await getExpressionParams();
return { vis, params, abortController };
})
)
.subscribe(({ vis, params, abortController }) => {
vis$.next(vis);
if (params) expressionParams$.next(params);
expressionAbortController$.next(abortController);
});
// Track visualizations linked to a saved object in the library
const savedObjectId$ = new BehaviorSubject<string | undefined>(
state.savedObjectId ?? state.serializedVis.id
);
const savedObjectProperties$ = new BehaviorSubject<ExtraSavedObjectProperties | undefined>(
undefined
);
const linkedToLibrary$ = new BehaviorSubject<boolean | undefined>(state.linkedToLibrary);
// Track the vis expression
const expressionParams$ = new BehaviorSubject<ExpressionRendererParams>({
expression: '',
});
const expressionAbortController$ = new BehaviorSubject<AbortController>(new AbortController());
let getExpressionParams: () => ReturnType<typeof getExpressionRendererProps> = async () => ({
params: expressionParams$.getValue(),
abortController: expressionAbortController$.getValue(),
});
const {
api: customTimeRangeApi,
serialize: serializeCustomTimeRange,
comparators: customTimeRangeComparators,
} = initializeTimeRange(state);
const searchSessionId$ = new BehaviorSubject<string | undefined>('');
const viewMode$ = apiPublishesViewMode(parentApi)
? parentApi.viewMode
: new BehaviorSubject('view');
const executionContext = apiHasExecutionContext(parentApi)
? parentApi.executionContext
: undefined;
const disableTriggers = apiHasDisableTriggers(parentApi)
? parentApi.disableTriggers
: undefined;
const parentApiContext = apiHasAppContext(parentApi) ? parentApi.getAppContext() : undefined;
const inspectorAdapters$ = new BehaviorSubject<Record<string, unknown>>({});
// Track data views
let initialDataViews: DataView[] | undefined = [];
if (initialVisInstance.data.indexPattern)
initialDataViews = [initialVisInstance.data.indexPattern];
if (initialVisInstance.type.getUsedIndexPattern) {
initialDataViews = await initialVisInstance.type.getUsedIndexPattern(
initialVisInstance.params
);
}
const dataLoading$ = new BehaviorSubject<boolean | undefined>(true);
const defaultPanelTitle = new BehaviorSubject<string | undefined>(initialVisInstance.title);
const api = buildApi(
{
...customTimeRangeApi,
...titlesApi,
...(dynamicActionsApi?.dynamicActionsApi ?? {}),
defaultPanelTitle,
dataLoading: dataLoading$,
dataViews: new BehaviorSubject<DataView[] | undefined>(initialDataViews),
supportedTriggers: () => [
ACTION_CONVERT_TO_LENS,
APPLY_FILTER_TRIGGER,
SELECT_RANGE_TRIGGER,
],
serializeState: () => {
const savedObjectProperties = savedObjectProperties$.getValue();
return serializeState({
serializedVis: vis$.getValue().serialize(),
titles: serializeTitles(),
id: savedObjectId$.getValue(),
linkedToLibrary:
// In the visualize editor, linkedToLibrary should always be false to force the full state to be serialized,
// instead of just passing a reference to the linked saved object. Other contexts like dashboards should
// serialize the state with just the savedObjectId so that the current revision of the vis is always used
apiIsOfType(parentApi, VISUALIZE_APP_NAME) ? false : linkedToLibrary$.getValue(),
...(savedObjectProperties ? { savedObjectProperties } : {}),
...(dynamicActionsApi?.serializeDynamicActions?.() ?? {}),
...serializeCustomTimeRange(),
});
},
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);
},
updateVis: async (visUpdates) => {
const currentSerializedVis = vis$.getValue().serialize();
serializedVis$.next({
...currentSerializedVis,
...visUpdates,
params: {
...currentSerializedVis.params,
...visUpdates.params,
},
data: {
...currentSerializedVis.data,
...visUpdates.data,
},
} as SerializedVis);
if (visUpdates.title) {
titlesApi.setPanelTitle(visUpdates.title);
}
},
openInspector: () => {
const adapters = inspectorAdapters$.getValue();
if (!adapters) return;
const inspector = getInspector();
if (!inspector.isAvailable(adapters)) return;
return getInspector().open(adapters, {
title:
titlesApi.panelTitle?.getValue() ||
i18n.translate('visualizations.embeddable.inspectorTitle', {
defaultMessage: 'Inspector',
}),
});
},
// Library transforms
saveToLibrary: (newTitle: string) => {
titlesApi.setPanelTitle(newTitle);
const { rawState, references } = serializeState({
serializedVis: vis$.getValue().serialize(),
titles: {
...serializeTitles(),
title: newTitle,
},
});
return saveToLibrary({
uiState: vis$.getValue().uiState,
rawState: rawState as VisualizeOutputState,
references,
});
},
canLinkToLibrary: () => !state.linkedToLibrary,
canUnlinkFromLibrary: () => !!state.linkedToLibrary,
checkForDuplicateTitle: () => false, // Handled by saveToLibrary action
getByValueState: () => ({
savedVis: vis$.getValue().serialize(),
...serializeTitles(),
}),
getByReferenceState: (libraryId) =>
serializeState({
serializedVis: vis$.getValue().serialize(),
titles: serializeTitles(),
id: libraryId,
linkedToLibrary: true,
}).rawState,
},
{
...titleComparators,
...customTimeRangeComparators,
...(dynamicActionsApi?.dynamicActionsComparator ?? {
enhancements: getUnchangingComparator(),
}),
serializedVis: [
serializedVis$,
(value) => {
serializedVis$.next(value);
},
(a, b) => {
const visA = a
? {
...omitBy(a, isEmpty),
data: omitBy(a.data, isNil),
params: omitBy(a.params, isNil),
}
: {};
const visB = b
? {
...omitBy(b, isEmpty),
data: omitBy(b.data, isNil),
params: omitBy(b.params, isNil),
}
: {};
return isEqual(visA, visB);
},
],
savedObjectId: [
savedObjectId$,
(value) => savedObjectId$.next(value),
(a, b) => {
if (!a && !b) return true;
return a === b;
},
],
savedObjectProperties: getUnchangingComparator(),
linkedToLibrary: [linkedToLibrary$, (value) => linkedToLibrary$.next(value)],
}
);
const fetchSubscription = fetch$(api)
.pipe(
switchMap(async (data) => {
const unifiedSearch = apiPublishesUnifiedSearch(parentApi)
? {
query: data.query,
filters: data.filters,
}
: {};
const searchSessionId = apiPublishesSearchSession(parentApi) ? data.searchSessionId : '';
searchSessionId$.next(searchSessionId);
const settings = apiPublishesSettings(parentApi)
? {
syncColors: parentApi.settings.syncColors$.getValue(),
syncCursor: parentApi.settings.syncCursor$.getValue(),
syncTooltips: parentApi.settings.syncTooltips$.getValue(),
}
: {};
dataLoading$.next(true);
const timeslice = apiPublishesTimeslice(parentApi)
? parentApi.timeslice$.getValue()
: undefined;
const customTimeRange = customTimeRangeApi.timeRange$.getValue();
const parentTimeRange = apiPublishesTimeRange(parentApi) ? data.timeRange : undefined;
const timesliceTimeRange = timeslice
? {
from: new Date(timeslice[0]).toISOString(),
to: new Date(timeslice[1]).toISOString(),
mode: 'absolute' as 'absolute',
}
: undefined;
// Precedence should be:
// custom time range from state >
// timeslice time range >
// parent API time range from e.g. unified search
const timeRangeToRender = customTimeRange ?? timesliceTimeRange ?? parentTimeRange;
getExpressionParams = async () => {
return await getExpressionRendererProps({
unifiedSearch,
vis: vis$.getValue(),
settings,
disableTriggers,
searchSessionId,
parentExecutionContext: executionContext,
abortController: expressionAbortController$.getValue(),
timeRange: timeRangeToRender,
onRender: async (renderCount) => {
if (renderCount === renderCount$.getValue()) return;
renderCount$.next(renderCount);
const visInstance = vis$.getValue();
const visTypeName = visInstance.type.name;
let telemetryVisTypeName = visTypeName;
if (visTypeName === 'metrics') {
telemetryVisTypeName = 'legacy_metric';
}
if (visTypeName === 'pie' && visInstance.params.isDonut) {
telemetryVisTypeName = 'donut';
}
if (
visTypeName === 'area' &&
visInstance.params.seriesParams.some(
(seriesParams: { mode: string }) => seriesParams.mode === 'stacked'
)
) {
telemetryVisTypeName = 'area_stacked';
}
getUsageCollection().reportUiCounter(
executionContext?.type ?? '',
'count',
`render_agg_based_${telemetryVisTypeName}`
);
if (hasRendered$.getValue() === true) return;
hasRendered$.next(true);
hasRendered$.complete();
},
onEvent: async (event) => {
// Visualize doesn't respond to sizing events, so ignore.
if (isChartSizeEvent(event)) {
return;
}
const currentVis = vis$.getValue();
if (!disableTriggers) {
const triggerId = get(
VIS_EVENT_TO_TRIGGER,
event.name,
VIS_EVENT_TO_TRIGGER.filter
);
let context;
if (triggerId === VIS_EVENT_TO_TRIGGER.applyFilter) {
context = {
embeddable: api,
timeFieldName: currentVis.data.indexPattern?.timeFieldName!,
...event.data,
};
} else {
context = {
embeddable: api,
data: {
timeFieldName: currentVis.data.indexPattern?.timeFieldName!,
...event.data,
},
};
}
await getUiActions().getTrigger(triggerId).exec(context);
}
},
onData: (_, inspectorAdapters) => {
inspectorAdapters$.next(
typeof inspectorAdapters === 'function' ? inspectorAdapters() : inspectorAdapters
);
dataLoading$.next(false);
},
});
};
return await getExpressionParams();
})
)
.subscribe(({ params, abortController }) => {
if (params) expressionParams$.next(params);
expressionAbortController$.next(abortController);
});
return {
api,
Component: () => {
const expressionParams = useStateFromPublishingSubject(expressionParams$);
const renderCount = useStateFromPublishingSubject(renderCount$);
const hasRendered = useStateFromPublishingSubject(hasRendered$);
const domNode = useRef<HTMLDivElement>(null);
const { error, isLoading } = useExpressionRenderer(domNode, expressionParams);
useEffect(() => {
return () => {
fetchSubscription.unsubscribe();
maybeStopDynamicActions?.stopDynamicActions();
};
}, []);
useEffect(() => {
if (hasRendered && domNode.current) {
dispatchRenderComplete(domNode.current);
}
}, [hasRendered]);
return (
<div
style={{ width: '100%', height: '100%' }}
ref={domNode}
data-test-subj="visualizationLoader"
data-rendering-count={renderCount /* Used for functional tests */}
data-render-complete={hasRendered}
data-title={!api.hidePanelTitle?.getValue() ? api.panelTitle?.getValue() ?? '' : ''}
data-description={api.panelDescription?.getValue() ?? ''}
data-shared-item
>
{/* Replicate the loading state for the expression renderer to avoid FOUC */}
<EuiFlexGroup style={{ height: '100%' }} justifyContent="center" alignItems="center">
{isLoading && <EuiLoadingChart size="l" mono />}
{!isLoading && error && (
<EuiEmptyPrompt
iconType="error"
color="danger"
data-test-subj="embeddableError"
title={
<h2>
{i18n.translate('visualizations.embeddable.errorTitle', {
defaultMessage: 'Unable to load visualization ',
})}
</h2>
}
body={
<p>
{error.name}: {error.message}
</p>
}
/>
)}
</EuiFlexGroup>
</div>
);
},
};
},
});

View file

@ -15,7 +15,7 @@ import { confirmModalPromise } from './confirm_modal_promise';
import type { StartServices } from '../../types';
import { visualizationsClient } from '../../content_management';
import { VisualizationSavedObjectAttributes, VisualizationSavedObject } from '../../../common';
import { VisualizeOutputState } from '../../react_embeddable/types';
import { VisualizeOutputState } from '../../embeddable/types';
/**
* Attempts to create the current object using the serialized source. If an object already

View file

@ -16,7 +16,7 @@ import {
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/common';
import { isObject } from 'lodash';
import { Reference } from '../../../common/content_management';
import { VisualizeSavedVisInputState } from '../../react_embeddable/types';
import { VisualizeSavedVisInputState } from '../../embeddable/types';
import { SavedVisState, SerializedVis, VisSavedObject } from '../../types';
import type { SerializableAttributes } from '../../vis_types/vis_type_alias_registry';
import { extractControlsReferences, injectControlsReferences } from './controls_references';

View file

@ -53,7 +53,7 @@ import type {
} from '..';
import type { ListingViewRegistry, SavedVisState } from '../types';
import type { createVisEmbeddableFromObject } from '../embeddable';
import type { createVisEmbeddableFromObject } from '../legacy/embeddable';
import type { VisEditorsRegistry } from '../vis_editors_registry';
export interface VisualizeAppState {

View file

@ -16,7 +16,7 @@ import { createVisAsync } from '../../vis_async';
import { convertToSerializedVis, getSavedVisualization } from '../../utils/saved_visualize_utils';
import { SerializedVis, Vis, VisSavedObject, VisualizeEmbeddableContract } from '../..';
import type { VisInstance, VisualizeServices } from '../types';
import { VisualizeInput } from '../../embeddable';
import { VisualizeInput } from '../../legacy/embeddable';
function isErrorRelatedToRuntimeFields(error: ExpressionValueError['error']) {
const originalError = error.original || error;