mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Embeddable] Add unified error UI (#143367)
* Refactor embeddable error handler * Remove embeddable error handler from the visualization embeddable * Update Lens embeddable to handle errors correctly
This commit is contained in:
parent
4b617042f3
commit
0a72c67838
17 changed files with 195 additions and 140 deletions
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 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 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isFunction } from 'lodash';
|
||||
import React, { ReactNode, useEffect, useRef, useState } from 'react';
|
||||
import { isPromise } from '@kbn/std';
|
||||
import type { MaybePromise } from '@kbn/utility-types';
|
||||
import type { ErrorLike } from '@kbn/expressions-plugin/common';
|
||||
import type { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable';
|
||||
|
||||
type IReactEmbeddable = IEmbeddable<EmbeddableInput, EmbeddableOutput, MaybePromise<ReactNode>>;
|
||||
|
||||
interface EmbeddableErrorHandlerProps {
|
||||
children: IReactEmbeddable['catchError'];
|
||||
embeddable?: IReactEmbeddable;
|
||||
error: ErrorLike | string;
|
||||
}
|
||||
|
||||
export function EmbeddableErrorHandler({
|
||||
children,
|
||||
embeddable,
|
||||
error,
|
||||
}: EmbeddableErrorHandlerProps) {
|
||||
const [node, setNode] = useState<ReactNode>();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handler = embeddable?.catchError?.bind(embeddable) ?? children;
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
const renderedNode = handler(
|
||||
typeof error === 'string' ? { message: error, name: '' } : error,
|
||||
ref.current
|
||||
);
|
||||
if (isFunction(renderedNode)) {
|
||||
return renderedNode;
|
||||
}
|
||||
if (isPromise(renderedNode)) {
|
||||
renderedNode.then(setNode);
|
||||
} else {
|
||||
setNode(renderedNode);
|
||||
}
|
||||
}, [children, embeddable, error]);
|
||||
|
||||
return <div ref={ref}>{node}</div>;
|
||||
}
|
|
@ -12,6 +12,7 @@ import { EuiText } from '@elastic/eui';
|
|||
import { isPromise } from '@kbn/std';
|
||||
import { MaybePromise } from '@kbn/utility-types';
|
||||
import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable';
|
||||
import { EmbeddableErrorHandler } from './embeddable_error_handler';
|
||||
|
||||
interface Props {
|
||||
embeddable?: IEmbeddable<EmbeddableInput, EmbeddableOutput, MaybePromise<ReactNode>>;
|
||||
|
@ -91,7 +92,11 @@ export class EmbeddableRoot extends React.Component<Props, State> {
|
|||
<React.Fragment>
|
||||
<div ref={this.root}>{this.state.node}</div>
|
||||
{this.props.loading && <EuiLoadingSpinner data-test-subj="embedSpinner" />}
|
||||
{this.props.error && <EuiText data-test-subj="embedError">{this.props.error}</EuiText>}
|
||||
{this.props.error && (
|
||||
<EmbeddableErrorHandler embeddable={this.props.embeddable} error={this.props.error}>
|
||||
{({ message }) => <EuiText data-test-subj="embedError">{message}</EuiText>}
|
||||
</EmbeddableErrorHandler>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Markdown } from '@kbn/kibana-react-plugin/public';
|
||||
import { EmbeddablePanelError } from '../panel/embeddable_panel_error';
|
||||
import { Embeddable } from './embeddable';
|
||||
import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable';
|
||||
import { IContainer } from '../containers';
|
||||
|
@ -33,20 +32,8 @@ export class ErrorEmbeddable extends Embeddable<EmbeddableInput, EmbeddableOutpu
|
|||
public reload() {}
|
||||
|
||||
public render() {
|
||||
const title = typeof this.error === 'string' ? this.error : this.error.message;
|
||||
const body = (
|
||||
<Markdown markdown={title} openLinksInNewTab={true} data-test-subj="errorMessageMarkdown" />
|
||||
);
|
||||
const error = typeof this.error === 'string' ? { message: this.error, name: '' } : this.error;
|
||||
|
||||
return (
|
||||
<div className="embPanel__content" data-test-subj="embeddableStackError">
|
||||
<EuiEmptyPrompt
|
||||
className="embPanel__error"
|
||||
iconType="alert"
|
||||
iconColor="danger"
|
||||
body={body}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
return <EmbeddablePanelError embeddable={this} error={error} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
export type { EmbeddableOutput, EmbeddableInput, IEmbeddable } from './i_embeddable';
|
||||
export { isEmbeddable } from './is_embeddable';
|
||||
export { Embeddable } from './embeddable';
|
||||
export { EmbeddableErrorHandler } from './embeddable_error_handler';
|
||||
export * from './embeddable_factory';
|
||||
export * from './embeddable_factory_definition';
|
||||
export * from './default_embeddable_factory_provider';
|
||||
|
|
|
@ -29,13 +29,6 @@
|
|||
.embPanel__content--fullWidth {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.embPanel__content--error {
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HEADER
|
||||
|
@ -165,11 +158,12 @@
|
|||
}
|
||||
|
||||
.embPanel__error {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
padding: $euiSizeS;
|
||||
padding: $euiSizeL;
|
||||
|
||||
& > * {
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.embPanel__label {
|
||||
|
|
|
@ -6,7 +6,13 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EuiContextMenuPanelDescriptor, EuiPanel, htmlIdGenerator } from '@elastic/eui';
|
||||
import {
|
||||
EuiContextMenuPanelDescriptor,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
htmlIdGenerator,
|
||||
} from '@elastic/eui';
|
||||
import classNames from 'classnames';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
@ -27,11 +33,11 @@ import {
|
|||
contextMenuTrigger,
|
||||
} from '../triggers';
|
||||
import {
|
||||
IEmbeddable,
|
||||
EmbeddableOutput,
|
||||
EmbeddableError,
|
||||
EmbeddableErrorHandler,
|
||||
EmbeddableInput,
|
||||
} from '../embeddables/i_embeddable';
|
||||
EmbeddableOutput,
|
||||
IEmbeddable,
|
||||
} from '../embeddables';
|
||||
import { ViewMode } from '../types';
|
||||
|
||||
import { EmbeddablePanelError } from './embeddable_panel_error';
|
||||
|
@ -105,7 +111,7 @@ interface State {
|
|||
badges: Array<Action<EmbeddableContext>>;
|
||||
notifications: Array<Action<EmbeddableContext>>;
|
||||
loading?: boolean;
|
||||
error?: EmbeddableError;
|
||||
error?: Error;
|
||||
destroyError?(): void;
|
||||
node?: ReactNode;
|
||||
}
|
||||
|
@ -301,11 +307,24 @@ export class EmbeddablePanel extends React.Component<Props, State> {
|
|||
/>
|
||||
)}
|
||||
{this.state.error && (
|
||||
<EmbeddablePanelError
|
||||
editPanelAction={this.state.universalActions.editPanel}
|
||||
embeddable={this.props.embeddable}
|
||||
error={this.state.error}
|
||||
/>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
className="eui-fullHeight embPanel__error"
|
||||
data-test-subj="embeddableError"
|
||||
justifyContent="center"
|
||||
>
|
||||
<EuiFlexItem>
|
||||
<EmbeddableErrorHandler embeddable={this.props.embeddable} error={this.state.error}>
|
||||
{(error) => (
|
||||
<EmbeddablePanelError
|
||||
editPanelAction={this.state.universalActions.editPanel}
|
||||
embeddable={this.props.embeddable}
|
||||
error={error}
|
||||
/>
|
||||
)}
|
||||
</EmbeddableErrorHandler>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
<div className="embPanel__content" ref={this.embeddableRoot} {...contentAttrs}>
|
||||
{this.state.node}
|
||||
|
|
|
@ -6,16 +6,15 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isFunction } from 'lodash';
|
||||
import React, { ReactNode, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { EuiPanel } from '@elastic/eui';
|
||||
import React, { ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import { EuiButtonEmpty, EuiEmptyPrompt, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { isPromise } from '@kbn/std';
|
||||
import { Markdown } from '@kbn/kibana-react-plugin/public';
|
||||
import type { MaybePromise } from '@kbn/utility-types';
|
||||
import { ErrorLike } from '@kbn/expressions-plugin/common';
|
||||
import { distinctUntilChanged, merge, of, switchMap } from 'rxjs';
|
||||
import { EditPanelAction } from '../actions';
|
||||
import { EmbeddableInput, EmbeddableOutput, ErrorEmbeddable, IEmbeddable } from '../embeddables';
|
||||
import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from '../embeddables';
|
||||
|
||||
interface EmbeddablePanelErrorProps {
|
||||
editPanelAction?: EditPanelAction;
|
||||
|
@ -29,27 +28,25 @@ export function EmbeddablePanelError({
|
|||
error,
|
||||
}: EmbeddablePanelErrorProps) {
|
||||
const [isEditable, setEditable] = useState(false);
|
||||
const [node, setNode] = useState<ReactNode>();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const handleErrorClick = useMemo(
|
||||
() => (isEditable ? () => editPanelAction?.execute({ embeddable }) : undefined),
|
||||
[editPanelAction, embeddable, isEditable]
|
||||
);
|
||||
|
||||
const title = embeddable.getTitle();
|
||||
const actionDisplayName = useMemo(
|
||||
const label = useMemo(
|
||||
() => editPanelAction?.getDisplayName({ embeddable }),
|
||||
[editPanelAction, embeddable]
|
||||
);
|
||||
const title = useMemo(() => embeddable.getTitle(), [embeddable]);
|
||||
const ariaLabel = useMemo(
|
||||
() =>
|
||||
!title
|
||||
? actionDisplayName
|
||||
? label
|
||||
: i18n.translate('embeddableApi.panel.editPanel.displayName', {
|
||||
defaultMessage: 'Edit {value}',
|
||||
values: { value: title },
|
||||
}),
|
||||
[title, actionDisplayName]
|
||||
[label, title]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -62,42 +59,29 @@ export function EmbeddablePanelError({
|
|||
|
||||
return () => subscription.unsubscribe();
|
||||
}, [editPanelAction, embeddable]);
|
||||
useEffect(() => {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!embeddable.catchError) {
|
||||
const errorEmbeddable = new ErrorEmbeddable(error, { id: embeddable.id });
|
||||
setNode(errorEmbeddable.render());
|
||||
|
||||
return () => errorEmbeddable.destroy();
|
||||
}
|
||||
|
||||
const renderedNode = embeddable.catchError(error, ref.current);
|
||||
if (isFunction(renderedNode)) {
|
||||
return renderedNode;
|
||||
}
|
||||
if (isPromise(renderedNode)) {
|
||||
renderedNode.then(setNode);
|
||||
} else {
|
||||
setNode(renderedNode);
|
||||
}
|
||||
}, [embeddable, error]);
|
||||
|
||||
return (
|
||||
<EuiPanel
|
||||
element="div"
|
||||
className="embPanel__content embPanel__content--error"
|
||||
color="transparent"
|
||||
paddingSize="none"
|
||||
data-test-subj="embeddableError"
|
||||
panelRef={ref}
|
||||
role={isEditable ? 'button' : undefined}
|
||||
aria-label={isEditable ? ariaLabel : undefined}
|
||||
onClick={handleErrorClick}
|
||||
>
|
||||
{node}
|
||||
</EuiPanel>
|
||||
<EuiEmptyPrompt
|
||||
body={
|
||||
<EuiText size="s">
|
||||
<Markdown
|
||||
markdown={error.message}
|
||||
openLinksInNewTab={true}
|
||||
data-test-subj="errorMessageMarkdown"
|
||||
/>
|
||||
</EuiText>
|
||||
}
|
||||
data-test-subj="embeddableStackError"
|
||||
iconType="alert"
|
||||
iconColor="danger"
|
||||
layout="vertical"
|
||||
actions={
|
||||
isEditable && (
|
||||
<EuiButtonEmpty aria-label={ariaLabel} onClick={handleErrorClick} size="s">
|
||||
{label}
|
||||
</EuiButtonEmpty>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,28 +12,25 @@ import React from 'react';
|
|||
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
|
||||
import type { ApplicationStart } from '@kbn/core/public';
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-plugin/common';
|
||||
import type { ViewMode } from '@kbn/embeddable-plugin/common';
|
||||
import type { RenderMode } from '@kbn/expressions-plugin/common';
|
||||
|
||||
interface VisualizationMissedSavedObjectErrorProps {
|
||||
savedObjectMeta: {
|
||||
savedObjectType: typeof DATA_VIEW_SAVED_OBJECT_TYPE | 'search';
|
||||
savedObjectId?: string;
|
||||
};
|
||||
application: ApplicationStart;
|
||||
viewMode: ViewMode;
|
||||
message: string;
|
||||
renderMode: RenderMode;
|
||||
}
|
||||
|
||||
export const VisualizationMissedSavedObjectError = ({
|
||||
savedObjectMeta,
|
||||
application,
|
||||
viewMode,
|
||||
message,
|
||||
renderMode,
|
||||
}: VisualizationMissedSavedObjectErrorProps) => {
|
||||
const { management: isManagementEnabled } = application.capabilities.navLinks;
|
||||
const isIndexPatternManagementEnabled = application.capabilities.management.kibana.indexPatterns;
|
||||
const isEditVisEnabled = application.capabilities.visualize?.save;
|
||||
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
|
@ -59,33 +56,7 @@ export const VisualizationMissedSavedObjectError = ({
|
|||
</RedirectAppLinks>
|
||||
) : null
|
||||
}
|
||||
body={
|
||||
<>
|
||||
<p>
|
||||
{i18n.translate('visualizations.missedDataView.errorMessage', {
|
||||
defaultMessage: `Could not find the {type}: {id}`,
|
||||
values: {
|
||||
id: savedObjectMeta.savedObjectId ?? '-',
|
||||
type:
|
||||
savedObjectMeta.savedObjectType === 'search'
|
||||
? i18n.translate('visualizations.noSearch.label', {
|
||||
defaultMessage: 'search',
|
||||
})
|
||||
: i18n.translate('visualizations.noDataView.label', {
|
||||
defaultMessage: 'data view',
|
||||
}),
|
||||
},
|
||||
})}
|
||||
</p>
|
||||
{viewMode === 'edit' && renderMode !== 'edit' && isEditVisEnabled ? (
|
||||
<p>
|
||||
{i18n.translate('visualizations.missedDataView.editInVisualizeEditor', {
|
||||
defaultMessage: `Edit in Visualize editor to fix the error`,
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
</>
|
||||
}
|
||||
body={<p>{message}</p>}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -29,7 +29,6 @@ import {
|
|||
IContainer,
|
||||
ReferenceOrValueEmbeddable,
|
||||
SavedObjectEmbeddableInput,
|
||||
ViewMode,
|
||||
} from '@kbn/embeddable-plugin/public';
|
||||
import {
|
||||
ExpressionAstExpression,
|
||||
|
@ -401,6 +400,25 @@ export class VisualizeEmbeddable
|
|||
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,
|
||||
|
@ -503,7 +521,7 @@ export class VisualizeEmbeddable
|
|||
const { error } = this.getOutput();
|
||||
|
||||
if (error) {
|
||||
render(this.catchError(error), this.domNode);
|
||||
render(this.renderError(error), this.domNode);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -511,17 +529,16 @@ export class VisualizeEmbeddable
|
|||
await this.updateHandler();
|
||||
}
|
||||
|
||||
public catchError(error: ErrorLike | string) {
|
||||
private renderError(error: ErrorLike | string) {
|
||||
if (isFallbackDataView(this.vis.data.indexPattern)) {
|
||||
return (
|
||||
<VisualizationMissedSavedObjectError
|
||||
viewMode={this.input.viewMode ?? ViewMode.VIEW}
|
||||
renderMode={this.input.renderMode ?? 'view'}
|
||||
savedObjectMeta={{
|
||||
savedObjectId: this.vis.data.indexPattern.id,
|
||||
savedObjectType: this.vis.data.savedSearchId ? 'search' : DATA_VIEW_SAVED_OBJECT_TYPE,
|
||||
}}
|
||||
application={getApplication()}
|
||||
message={typeof error === 'string' ? error : error.message}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ export class VisualizeEmbeddableFactory
|
|||
|
||||
public getDisplayName() {
|
||||
return i18n.translate('visualizations.displayName', {
|
||||
defaultMessage: 'Visualization',
|
||||
defaultMessage: 'visualization',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -51,14 +51,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
// wrapping into own describe to make sure new tab is cleaned up even if test failed
|
||||
// see: https://github.com/elastic/kibana/pull/67280#discussion_r430528122
|
||||
describe('recreate index pattern link works', () => {
|
||||
it('recreate index pattern link works', async () => {
|
||||
describe('when the saved object is missing', () => {
|
||||
it('shows the missing data view error message', async () => {
|
||||
await PageObjects.dashboard.gotoDashboardLandingPage();
|
||||
await PageObjects.dashboard.loadSavedDashboard('dashboard with missing index pattern');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
const errorEmbeddable = await testSubjects.find('visualization-missed-data-view-error');
|
||||
const embeddableError = await testSubjects.find('embeddableError');
|
||||
const errorMessage = await embeddableError.getVisibleText();
|
||||
|
||||
expect(await errorEmbeddable.isDisplayed()).to.be(true);
|
||||
expect(errorMessage).to.contain('Could not find the data view');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -645,6 +645,25 @@ export class Embeddable
|
|||
}
|
||||
}
|
||||
|
||||
private getError(): Error | undefined {
|
||||
const message =
|
||||
typeof this.errors?.[0]?.longMessage === 'string'
|
||||
? this.errors[0].longMessage
|
||||
: this.errors?.[0]?.shortMessage;
|
||||
|
||||
if (message != null) {
|
||||
return new Error(message);
|
||||
}
|
||||
|
||||
if (!this.expression) {
|
||||
return new Error(
|
||||
i18n.translate('xpack.lens.embeddable.failure', {
|
||||
defaultMessage: "Visualization couldn't be displayed",
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLElement} domNode
|
||||
|
@ -665,7 +684,7 @@ export class Embeddable
|
|||
this.updateOutput({
|
||||
...this.getOutput(),
|
||||
loading: true,
|
||||
error: undefined,
|
||||
error: this.getError(),
|
||||
});
|
||||
this.renderComplete.dispatchInProgress();
|
||||
|
||||
|
@ -697,7 +716,8 @@ export class Embeddable
|
|||
style={input.style}
|
||||
executionContext={this.getExecutionContext()}
|
||||
canEdit={this.getIsEditable() && input.viewMode === 'edit'}
|
||||
onRuntimeError={() => {
|
||||
onRuntimeError={(message) => {
|
||||
this.updateOutput({ error: new Error(message) });
|
||||
this.logError('runtime');
|
||||
}}
|
||||
noPadding={this.visDisplayOptions?.noPadding}
|
||||
|
|
|
@ -79,7 +79,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
|
||||
getDisplayName() {
|
||||
return i18n.translate('xpack.lens.embeddableDisplayName', {
|
||||
defaultMessage: 'lens',
|
||||
defaultMessage: 'Lens',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ export interface ExpressionWrapperProps {
|
|||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
canEdit: boolean;
|
||||
onRuntimeError: () => void;
|
||||
onRuntimeError: (message?: string) => void;
|
||||
executionContext?: KibanaExecutionContext;
|
||||
lensInspector: LensInspector;
|
||||
noPadding?: boolean;
|
||||
|
@ -148,7 +148,9 @@ export function ExpressionWrapper({
|
|||
syncCursor={syncCursor}
|
||||
executionContext={executionContext}
|
||||
renderError={(errorMessage, error) => {
|
||||
onRuntimeError();
|
||||
const messages = getOriginalRequestErrorMessages(error);
|
||||
onRuntimeError(messages[0] ?? errorMessage);
|
||||
|
||||
return (
|
||||
<div data-test-subj="expression-renderer-error">
|
||||
<EuiFlexGroup direction="column" alignItems="center" justifyContent="center">
|
||||
|
@ -156,7 +158,7 @@ export function ExpressionWrapper({
|
|||
<EuiIcon type="alert" color="danger" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{(getOriginalRequestErrorMessages(error) || [errorMessage]).map((message) => (
|
||||
{messages.map((message) => (
|
||||
<EuiText size="s">{message}</EuiText>
|
||||
))}
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -6140,7 +6140,7 @@
|
|||
"visualizations.deprecatedTag": "Déclassé",
|
||||
"visualizations.disabledLabVisualizationLink": "Lire la documentation",
|
||||
"visualizations.disabledLabVisualizationMessage": "Veuillez activer le mode lab dans les paramètres avancés pour consulter les visualisations lab.",
|
||||
"visualizations.displayName": "Visualisation",
|
||||
"visualizations.displayName": "visualisation",
|
||||
"visualizations.editor.createBreadcrumb": "Créer",
|
||||
"visualizations.editor.defaultEditBreadcrumbText": "Modifier la visualisation",
|
||||
"visualizations.embeddable.inspectorTitle": "Inspecteur",
|
||||
|
@ -6175,7 +6175,6 @@
|
|||
"visualizations.listing.table.typeColumnName": "Type",
|
||||
"visualizations.listingPageTitle": "Bibliothèque Visualize",
|
||||
"visualizations.missedDataView.dataViewReconfigure": "Recréez-la dans la page de gestion des vues de données.",
|
||||
"visualizations.missedDataView.editInVisualizeEditor": "Effectuer des modifications dans l'éditeur Visualize pour corriger l'erreur",
|
||||
"visualizations.newChart.conditionalMessage.advancedSettingsLink": "Paramètres avancés.",
|
||||
"visualizations.newChart.libraryMode.new": "nouveau",
|
||||
"visualizations.newChart.libraryMode.old": "âge",
|
||||
|
@ -17450,7 +17449,7 @@
|
|||
"xpack.lens.embeddable.missingTimeRangeParam.longMessage": "La propriété timeRange est requise pour cette configuration.",
|
||||
"xpack.lens.embeddable.missingTimeRangeParam.shortMessage": "Propriété timeRange manquante",
|
||||
"xpack.lens.embeddable.moreErrors": "Effectuez des modifications dans l'éditeur Lens pour afficher plus d'erreurs",
|
||||
"xpack.lens.embeddableDisplayName": "lens",
|
||||
"xpack.lens.embeddableDisplayName": "Lens",
|
||||
"xpack.lens.endValue.nearest": "La plus proche",
|
||||
"xpack.lens.endValue.none": "Masquer",
|
||||
"xpack.lens.endValue.zero": "Zéro",
|
||||
|
|
|
@ -6169,7 +6169,6 @@
|
|||
"visualizations.listing.table.typeColumnName": "型",
|
||||
"visualizations.listingPageTitle": "Visualizeライブラリ",
|
||||
"visualizations.missedDataView.dataViewReconfigure": "データビュー管理ページで再作成",
|
||||
"visualizations.missedDataView.editInVisualizeEditor": "Visualizeエディターで編集し、エラーを修正",
|
||||
"visualizations.newChart.conditionalMessage.advancedSettingsLink": "高度な設定",
|
||||
"visualizations.newChart.libraryMode.new": "新規",
|
||||
"visualizations.newChart.libraryMode.old": "古",
|
||||
|
|
|
@ -6176,7 +6176,6 @@
|
|||
"visualizations.listing.table.typeColumnName": "类型",
|
||||
"visualizations.listingPageTitle": "Visualize 库",
|
||||
"visualizations.missedDataView.dataViewReconfigure": "在数据视图管理页面中重新创建",
|
||||
"visualizations.missedDataView.editInVisualizeEditor": "在 Visualize 编辑器中编辑以修复该错误",
|
||||
"visualizations.newChart.conditionalMessage.advancedSettingsLink": "免费的 API 密钥。",
|
||||
"visualizations.newChart.libraryMode.new": "新",
|
||||
"visualizations.newChart.libraryMode.old": "以前",
|
||||
|
@ -17458,7 +17457,7 @@
|
|||
"xpack.lens.embeddable.missingTimeRangeParam.longMessage": "给定配置需要包含 timeRange 属性",
|
||||
"xpack.lens.embeddable.missingTimeRangeParam.shortMessage": "缺少 timeRange 属性",
|
||||
"xpack.lens.embeddable.moreErrors": "在 Lens 编辑器中编辑以查看更多错误",
|
||||
"xpack.lens.embeddableDisplayName": "lens",
|
||||
"xpack.lens.embeddableDisplayName": "Lens",
|
||||
"xpack.lens.endValue.nearest": "最近",
|
||||
"xpack.lens.endValue.none": "隐藏",
|
||||
"xpack.lens.endValue.zero": "零",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue