[Canvas] Expression error (#103048) (#105724)

* Basic setup of error plugin.

* Removed not used `function` files at `error` expression.

* Moved related components from canvas.

* Changed imports of components.

* Fixed renderer and storybook.

* Fixed types errors.

* Added limits.

* Removed useless translations and fixed .i18nrc.json

* added `done` handler call.

* Added more fixes fo i18nc.

* Added docs.

* More fixes of i18nrc.

* Fixed async functions.

Written current code, based on https://github.com/storybookjs/storybook/issues/7745

* Fixed one test with Expression input.

After changing the way of rendering in stories, all elements are mounting and componentDidMount is involved. The previous snapshot was without
mounted `monaco` editor.

* Fixed storybook error.

* More fixes.

* removed unused translations.

* Removed handlers and changed the way of handling `resize` and calling `done`.

* Fixed i18n error.

* Fixed storybook.

* Replaced Popover with EuiPopover.

* Moved `Popover` back to `canvas`

* Removed `Popover` export from presentation_utils components.

* Moved error_component and debug_component from presentation_util to expression_error.

* Fixed translations and imports.

* Moved `debug renderer` to `expression_error` plugin.

* Fixed error.

* Fixed lazy exports.

* Fixed imports

* Fixed storybook snapshot.

* Removed `.i18nrc.json`.

* Fixed color of `error`.

* Exported concrete elements from popover.

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
# Conflicts:
#	packages/kbn-optimizer/limits.yml
#	src/plugins/expression_error/public/components/debug/debug.tsx
This commit is contained in:
Yaroslav Kuznietsov 2021-07-15 14:41:16 +03:00 committed by GitHub
parent 125eee0e7d
commit d54d0b5852
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 538 additions and 218 deletions

View file

@ -16,6 +16,7 @@
"esUi": "src/plugins/es_ui_shared",
"devTools": "src/plugins/dev_tools",
"expressions": "src/plugins/expressions",
"expressionError": "src/plugins/expression_error",
"expressionRevealImage": "src/plugins/expression_reveal_image",
"inputControl": "src/plugins/input_control_vis",
"inspector": "src/plugins/inspector",

View file

@ -70,6 +70,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a
|WARNING: Missing README.
|{kib-repo}blob/{branch}/src/plugins/expression_error/README.md[expressionError]
|Expression Error plugin adds an error renderer to the expression plugin. The renderer will display the error image.
|{kib-repo}blob/{branch}/src/plugins/expression_reveal_image/README.md[expressionRevealImage]
|Expression Reveal Image plugin adds a revealImage function to the expression plugin and an associated renderer. The renderer will display the given percentage of a given image.

View file

@ -112,3 +112,4 @@ pageLoadAssetSize:
visTypePie: 35583
expressionRevealImage: 25675
cases: 144442
expressionError: 22127

View file

@ -17,6 +17,7 @@ export const storybookAliases = {
dashboard_enhanced: 'x-pack/plugins/dashboard_enhanced/.storybook',
data_enhanced: 'x-pack/plugins/data_enhanced/.storybook',
embeddable: 'src/plugins/embeddable/.storybook',
expression_error: 'src/plugins/expression_error/.storybook',
expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook',
infra: 'x-pack/plugins/infra/.storybook',
security_solution: 'x-pack/plugins/security_solution/.storybook',

View file

@ -0,0 +1,10 @@
/*
* 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.
*/
// eslint-disable-next-line import/no-commonjs
module.exports = require('@kbn/storybook').defaultConfig;

View file

@ -0,0 +1,9 @@
# expressionRevealImage
Expression Error plugin adds an `error` renderer to the expression plugin. The renderer will display the error image.
---
## Development
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.

View file

@ -0,0 +1,11 @@
/*
* 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.
*/
export const PLUGIN_ID = 'expressionError';
export const PLUGIN_NAME = 'expressionError';
export const JSON = 'JSON';

View file

@ -0,0 +1,9 @@
/*
* 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.
*/
export * from './constants';

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 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.
*/
export type OriginString = 'bottom' | 'left' | 'top' | 'right';
export interface ErrorRendererConfig {
error: Error;
}
export interface NodeDimensions {
width: number;
height: number;
}

View file

@ -0,0 +1,9 @@
/*
* 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.
*/
export * from './expression_renderers';

View file

@ -0,0 +1,13 @@
/*
* 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.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/src/plugins/expression_error'],
};

View file

@ -0,0 +1,10 @@
{
"id": "expressionError",
"version": "1.0.0",
"kibanaVersion": "kibana",
"server": false,
"ui": true,
"requiredPlugins": ["expressions", "presentationUtil"],
"optionalPlugins": [],
"requiredBundles": []
}

View file

@ -1,8 +1,9 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 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 React from 'react';

View file

@ -1,8 +1,9 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 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.
*/
export const largePayload = {

View file

@ -1,13 +1,14 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 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 React from 'react';
import PropTypes from 'prop-types';
import { EuiCode } from '@elastic/eui';
import './debug.scss';
const LimitRows = (key: string, value: any) => {
if (key === 'rows') {
@ -16,14 +17,10 @@ const LimitRows = (key: string, value: any) => {
return value;
};
export const Debug = ({ payload }: any) => (
export const Debug = ({ payload }: { payload: unknown }) => (
<EuiCode className="canvasDebug">
<pre className="canvasDebug__content" data-test-subj="canvasDebug__content">
{JSON.stringify(payload, LimitRows, 2)}
</pre>
</EuiCode>
);
Debug.propTypes = {
payload: PropTypes.object.isRequired,
};

View file

@ -0,0 +1,10 @@
/*
* 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.
*/
// eslint-disable-next-line import/no-default-export
export { Debug as default } from './debug';

View file

@ -0,0 +1,52 @@
/*
* 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 React, { useState, useEffect, useCallback } from 'react';
import { useResizeObserver } from '@elastic/eui';
import { IInterpreterRenderHandlers } from '../../../expressions';
import { withSuspense } from '../../../presentation_util/public';
import { NodeDimensions } from '../../common/types';
import { LazyDebugComponent } from '.';
const Debug = withSuspense(LazyDebugComponent);
interface DebugComponentProps {
onLoaded: IInterpreterRenderHandlers['done'];
parentNode: HTMLElement;
payload: any;
}
function DebugComponent({ onLoaded, parentNode, payload }: DebugComponentProps) {
const parentNodeDimensions = useResizeObserver(parentNode);
const [dimensions, setDimensions] = useState<NodeDimensions>({
width: parentNode.offsetWidth,
height: parentNode.offsetHeight,
});
const updateDebugView = useCallback(() => {
setDimensions({
width: parentNode.offsetWidth,
height: parentNode.offsetHeight,
});
onLoaded();
}, [parentNode, onLoaded]);
useEffect(() => {
updateDebugView();
}, [parentNodeDimensions, updateDebugView]);
return (
<div style={dimensions}>
<Debug payload={payload} />
</div>
);
}
// default export required for React.Lazy
// eslint-disable-next-line import/no-default-export
export { DebugComponent as default };

View file

@ -1,28 +1,28 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 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 React, { FC } from 'react';
import PropTypes from 'prop-types';
import { EuiCallOut } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
import { ShowDebugging } from './show_debugging';
const strings = {
getDescription: () =>
i18n.translate('xpack.canvas.errorComponent.description', {
i18n.translate('expressionError.errorComponent.description', {
defaultMessage: 'Expression failed with the message:',
}),
getTitle: () =>
i18n.translate('xpack.canvas.errorComponent.title', {
i18n.translate('expressionError.errorComponent.title', {
defaultMessage: 'Whoops! Expression failed',
}),
};
export interface Props {
payload: {
error: Error;
@ -46,7 +46,3 @@ export const Error: FC<Props> = ({ payload }) => {
</EuiCallOut>
);
};
Error.propTypes = {
payload: PropTypes.object.isRequired,
};

View file

@ -0,0 +1,10 @@
/*
* 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.
*/
// eslint-disable-next-line import/no-default-export
export { Error as default } from './error';

View file

@ -1,14 +1,14 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 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 React, { FC, useState } from 'react';
import PropTypes from 'prop-types';
import { EuiButtonEmpty } from '@elastic/eui';
import { Debug } from '../debug';
import Debug from '../debug';
import { Props } from './error';
export const ShowDebugging: FC<Props> = ({ payload }) => {
@ -30,7 +30,3 @@ export const ShowDebugging: FC<Props> = ({ payload }) => {
</div>
);
};
ShowDebugging.propTypes = {
payload: PropTypes.object.isRequired,
};

View file

@ -0,0 +1,67 @@
/*
* 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 React, { useState, useEffect, useCallback } from 'react';
import { EuiIcon, useResizeObserver, EuiPopover } from '@elastic/eui';
import { IInterpreterRenderHandlers } from '../../../expressions';
import { withSuspense } from '../../../presentation_util/public';
import { ErrorRendererConfig } from '../../common/types';
import { LazyErrorComponent } from '.';
const Error = withSuspense(LazyErrorComponent);
interface ErrorComponentProps extends ErrorRendererConfig {
onLoaded: IInterpreterRenderHandlers['done'];
parentNode: HTMLElement;
}
function ErrorComponent({ onLoaded, parentNode, error }: ErrorComponentProps) {
const getButtonSize = (node: HTMLElement) => Math.min(node.clientHeight, node.clientWidth);
const parentNodeDimensions = useResizeObserver(parentNode);
const [buttonSize, setButtonSize] = useState<number>(getButtonSize(parentNode));
const [isPopoverOpen, setPopoverOpen] = useState<boolean>(false);
const handlePopoverClick = () => setPopoverOpen(!isPopoverOpen);
const closePopover = () => setPopoverOpen(false);
const updateErrorView = useCallback(() => {
setButtonSize(getButtonSize(parentNode));
onLoaded();
}, [parentNode, onLoaded]);
useEffect(() => {
updateErrorView();
}, [parentNodeDimensions, updateErrorView]);
return (
<div className="canvasRenderError">
<EuiPopover
closePopover={closePopover}
button={
<EuiIcon
className="canvasRenderError__icon"
onClick={handlePopoverClick}
style={{
height: buttonSize,
width: buttonSize,
}}
type="alert"
/>
}
isOpen={isPopoverOpen}
>
<Error payload={{ error }} />
</EuiPopover>
</div>
);
}
// default export required for React.Lazy
// eslint-disable-next-line import/no-default-export
export { ErrorComponent as default };

View file

@ -0,0 +1,14 @@
/*
* 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 { lazy } from 'react';
export const LazyErrorComponent = lazy(() => import('./error'));
export const LazyDebugComponent = lazy(() => import('./debug'));
export const LazyErrorRenderComponent = lazy(() => import('./error_component'));
export const LazyDebugRenderComponent = lazy(() => import('./debug_component'));

View file

@ -1,19 +1,20 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
* 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 React from 'react';
import { storiesOf } from '@storybook/react';
import { error } from '../';
import { Render } from '../../__stories__/render';
import { errorRenderer } from '../error_renderer';
import { Render } from '../../../../presentation_util/public/__stories__';
storiesOf('renderers/error', module).add('default', () => {
const thrownError = new Error('There was an error');
const config = {
error: thrownError,
};
return <Render renderer={error} config={config} />;
return <Render renderer={errorRenderer} config={config} />;
});

View file

@ -0,0 +1,42 @@
/*
* 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 { render, unmountComponentAtNode } from 'react-dom';
import React from 'react';
import { ExpressionRenderDefinition } from 'src/plugins/expressions/common';
import { i18n } from '@kbn/i18n';
import { withSuspense } from '../../../../../src/plugins/presentation_util/public';
import { LazyDebugRenderComponent } from '../components';
import { JSON } from '../../common';
const Debug = withSuspense(LazyDebugRenderComponent);
const strings = {
getDisplayName: () =>
i18n.translate('expressionError.renderer.debug.displayName', {
defaultMessage: 'Debug',
}),
getHelpDescription: () =>
i18n.translate('expressionError.renderer.debug.helpDescription', {
defaultMessage: 'Render debug output as formatted {JSON}',
values: {
JSON,
},
}),
};
export const debugRenderer = (): ExpressionRenderDefinition<any> => ({
name: 'debug',
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render(domNode, config, handlers) {
handlers.onDestroy(() => unmountComponentAtNode(domNode));
render(<Debug parentNode={domNode} payload={config} onLoaded={handlers.done} />, domNode);
},
});

View file

@ -0,0 +1,51 @@
/*
* 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 React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { withSuspense } from '../../../presentation_util/public';
import { ErrorRendererConfig } from '../../common/types';
import { LazyErrorRenderComponent } from '../components';
const errorStrings = {
getDisplayName: () =>
i18n.translate('expressionError.renderer.error.displayName', {
defaultMessage: 'Error information',
}),
getHelpDescription: () =>
i18n.translate('expressionError.renderer.error.helpDescription', {
defaultMessage: 'Render error data in a way that is helpful to users',
}),
};
const ErrorComponent = withSuspense(LazyErrorRenderComponent);
export const errorRenderer = (): ExpressionRenderDefinition<ErrorRendererConfig> => ({
name: 'error',
displayName: errorStrings.getDisplayName(),
help: errorStrings.getHelpDescription(),
reuseDomNode: true,
render: async (
domNode: HTMLElement,
config: ErrorRendererConfig,
handlers: IInterpreterRenderHandlers
) => {
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
render(
<I18nProvider>
<ErrorComponent onLoaded={handlers.done} {...config} parentNode={domNode} />
</I18nProvider>,
domNode
);
},
});

View file

@ -0,0 +1,14 @@
/*
* 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 { errorRenderer } from './error_renderer';
import { debugRenderer } from './debug_renderer';
export const renderers = [errorRenderer, debugRenderer];
export { errorRenderer, debugRenderer };

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 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 { ExpressionErrorPlugin } from './plugin';
export type { ExpressionErrorPluginSetup, ExpressionErrorPluginStart } from './plugin';
export function plugin() {
return new ExpressionErrorPlugin();
}
export * from './expression_renderers';
export { LazyDebugComponent, LazyErrorComponent } from './components';

View file

@ -0,0 +1,34 @@
/*
* 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 { CoreSetup, CoreStart, Plugin } from '../../../core/public';
import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public';
import { errorRenderer, debugRenderer } from './expression_renderers';
interface SetupDeps {
expressions: ExpressionsSetup;
}
interface StartDeps {
expression: ExpressionsStart;
}
export type ExpressionErrorPluginSetup = void;
export type ExpressionErrorPluginStart = void;
export class ExpressionErrorPlugin
implements Plugin<ExpressionErrorPluginSetup, ExpressionErrorPluginStart, SetupDeps, StartDeps> {
public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionErrorPluginSetup {
expressions.registerRenderer(errorRenderer);
expressions.registerRenderer(debugRenderer);
}
public start(core: CoreStart): ExpressionErrorPluginStart {}
public stop() {}
}

View file

@ -0,0 +1,21 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"outDir": "./target/types",
"emitDeclarationOnly": true,
"declaration": true,
"declarationMap": true,
"isolatedModules": true
},
"include": [
"common/**/*",
"public/**/*",
"server/**/*",
],
"references": [
{ "path": "../../core/tsconfig.json" },
{ "path": "../presentation_util/tsconfig.json" },
{ "path": "../expressions/tsconfig.json" },
]
}

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { debug } from './debug';
import { error } from './error';
import { image } from './image';
import { markdown } from './markdown';
import { metric } from './metric';
@ -19,8 +17,6 @@ import { table } from './table';
import { text } from './text';
export const renderFunctions = [
debug,
error,
image,
markdown,
metric,

View file

@ -1,38 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import ReactDOM from 'react-dom';
import React from 'react';
import { Debug } from '../../public/components/debug';
import { RendererStrings } from '../../i18n';
import { RendererFactory } from '../../types';
const { debug: strings } = RendererStrings;
export const debug: RendererFactory<any> = () => ({
name: 'debug',
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render(domNode, config, handlers) {
const renderDebug = () => (
<div style={{ width: domNode.offsetWidth, height: domNode.offsetHeight }}>
<Debug payload={config} />
</div>
);
ReactDOM.render(renderDebug(), domNode, () => handlers.done());
if (handlers.onResize) {
handlers.onResize(() => {
ReactDOM.render(renderDebug(), domNode, () => handlers.done());
});
}
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
},
});

View file

@ -1,16 +0,0 @@
.canvasRenderError {
display: flex;
justify-content: center;
align-items: center;
.canvasRenderError__icon {
opacity: .4;
stroke: $euiColorEmptyShade;
stroke-width: .2px;
&:hover {
opacity: .6;
cursor: pointer;
}
}
}

View file

@ -1,58 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import ReactDOM from 'react-dom';
import React, { MouseEventHandler } from 'react';
import { EuiIcon } from '@elastic/eui';
import { Error } from '../../../public/components/error';
import { Popover } from '../../../public/components/popover';
import { RendererStrings } from '../../../i18n';
import { RendererFactory } from '../../../types';
export interface Config {
error: Error;
}
const { error: strings } = RendererStrings;
export const error: RendererFactory<Config> = () => ({
name: 'error',
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render(domNode, config, handlers) {
const draw = () => {
const buttonSize = Math.min(domNode.clientHeight, domNode.clientWidth);
const button = (handleClick: MouseEventHandler<any>) => (
<EuiIcon
className="canvasRenderError__icon"
onClick={handleClick}
style={{
height: buttonSize,
width: buttonSize,
}}
type="alert"
/>
);
ReactDOM.render(
<div className="canvasRenderError">
<Popover button={button}>{() => <Error payload={config} />}</Popover>
</div>,
domNode,
() => handlers.done()
);
};
draw();
handlers.onResize(draw);
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
},
});

View file

@ -6,6 +6,7 @@
*/
import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public';
import { errorRenderer, debugRenderer } from '../../../../../src/plugins/expression_error/public';
export const renderFunctions = [revealImageRenderer];
export const renderFunctions = [revealImageRenderer, errorRenderer, debugRenderer];
export const renderFunctionFactories = [];

View file

@ -55,16 +55,6 @@ export const RendererStrings = {
defaultMessage: 'Renders an embeddable Saved Object from other parts of Kibana',
}),
},
error: {
getDisplayName: () =>
i18n.translate('xpack.canvas.renderer.error.displayName', {
defaultMessage: 'Error information',
}),
getHelpDescription: () =>
i18n.translate('xpack.canvas.renderer.error.helpDescription', {
defaultMessage: 'Render error data in a way that is helpful to users',
}),
},
image: {
getDisplayName: () =>
i18n.translate('xpack.canvas.renderer.image.displayName', {

View file

@ -10,6 +10,7 @@
"charts",
"data",
"embeddable",
"expressionError",
"expressionRevealImage",
"expressions",
"features",

View file

@ -9,9 +9,7 @@ import React, { MouseEventHandler, FC } from 'react';
import PropTypes from 'prop-types';
import { EuiButtonIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
// @ts-expect-error untyped local
import { Popover, PopoverChildrenProps } from '../popover';
import { Popover } from '../popover';
import { ArgAdd } from '../arg_add';
// @ts-expect-error untyped local
import { Arg } from '../../expression_types/arg';
@ -49,7 +47,7 @@ export const ArgAddPopover: FC<Props> = ({ options }) => {
panelPaddingSize="none"
button={button}
>
{({ closePopover }: PopoverChildrenProps) =>
{({ closePopover }) =>
options.map((opt) => (
<ArgAdd
key={`${opt.arg.name}-add`}

View file

@ -9,10 +9,9 @@ import React, { FC } from 'react';
import PropTypes from 'prop-types';
import { EuiLink, PopoverAnchorPosition } from '@elastic/eui';
import tinycolor from 'tinycolor2';
import { Popover } from '../popover';
import { ColorDot } from '../color_dot';
import { ColorPicker, Props as ColorPickerProps } from '../color_picker';
import { Popover } from '../popover';
export interface Props extends ColorPickerProps {
anchorPosition?: PopoverAnchorPosition;

View file

@ -70,6 +70,7 @@ storiesOf('components/datasource/DatasourceComponent', module)
setPreviewing={action('setPreviewing')}
isInvalid={false}
setInvalid={action('setInvalid')}
renderError={action('renderError')}
/>
))
.add('datasource with expression arguments', () => (
@ -90,5 +91,6 @@ storiesOf('components/datasource/DatasourceComponent', module)
setPreviewing={action('setPreviewing')}
isInvalid={false}
setInvalid={action('setInvalid')}
renderError={action('renderError')}
/>
));

View file

@ -60,6 +60,7 @@ export class DatasourceComponent extends PureComponent {
setPreviewing: PropTypes.func,
isInvalid: PropTypes.bool,
setInvalid: PropTypes.func,
renderError: PropTypes.func,
};
state = { defaultIndex: '' };
@ -125,6 +126,7 @@ export class DatasourceComponent extends PureComponent {
setPreviewing,
isInvalid,
setInvalid,
renderError,
} = this.props;
const { defaultIndex } = this.state;
@ -155,6 +157,7 @@ export class DatasourceComponent extends PureComponent {
isInvalid,
setInvalid,
defaultIndex,
renderError,
});
const hasExpressionArgs = Object.values(stateArgs).some((a) => a && typeof a[0] === 'object');

View file

@ -20,8 +20,11 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { withSuspense } from '../../../../../../../src/plugins/presentation_util/public';
import { LazyErrorComponent } from '../../../../../../../src/plugins/expression_error/public';
import { Datatable } from '../../datatable';
import { Error } from '../../error';
const Error = withSuspense(LazyErrorComponent);
const strings = {
getEmptyFirstLineDescription: () =>

View file

@ -1,8 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { Debug } from './debug';

View file

@ -1,8 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { Error } from './error';

View file

@ -16,7 +16,18 @@ exports[`Storyshots components/ExpressionInput default 1`] = `
id="generated-id"
onBlur={[Function]}
onFocus={[Function]}
/>
>
<div
className="react-monaco-editor-container"
style={
Object {
"height": "100%",
"width": "100%",
}
}
/>
<div />
</div>
</div>
</div>
</div>

View file

@ -123,9 +123,22 @@ exports[`Storyshots Home Home Page 1`] = `
<div
className="euiPageContentBody euiPage--paddingLarge euiPage--restrictWidth-default"
>
<span
className="euiLoadingSpinner euiLoadingSpinner--medium"
/>
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentSpaceAround euiFlexGroup--directionRow euiFlexGroup--responsive"
style={
Object {
"minHeight": 600,
}
}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<span
className="euiLoadingSpinner euiLoadingSpinner--xLarge"
/>
</div>
</div>
</div>
</div>
</div>

View file

@ -5,7 +5,6 @@
* 2.0.
*/
/* eslint react/no-did-mount-set-state: 0, react/forbid-elements: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiPopover, EuiToolTip } from '@elastic/eui';

View file

@ -9,10 +9,9 @@ import React, { Fragment, FunctionComponent, useState } from 'react';
import PropTypes from 'prop-types';
import { EuiButtonEmpty, EuiContextMenu, EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Popover, ClosePopoverFn } from '../../popover';
import { ShortcutStrings } from '../../../../i18n/shortcuts';
import { flattenPanelTree } from '../../../lib/flatten_panel_tree';
import { Popover, ClosePopoverFn } from '../../popover';
import { CustomElementModal } from '../../custom_element_modal';
import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib/constants';
import { PositionedElement } from '../../../../types';

View file

@ -15,12 +15,11 @@ import {
EuiContextMenuPanelItemDescriptor,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Popover, ClosePopoverFn } from '../../popover';
import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib';
import { ElementSpec } from '../../../../types';
import { flattenPanelTree } from '../../../lib/flatten_panel_tree';
import { getId } from '../../../lib/get_id';
import { Popover, ClosePopoverFn } from '../../popover';
import { AssetManager } from '../../asset_manager';
import { SavedElementsModal } from '../../saved_elements_modal';

View file

@ -9,12 +9,11 @@ import React, { FunctionComponent, useState } from 'react';
import PropTypes from 'prop-types';
import { EuiButtonEmpty, EuiContextMenu, EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Popover, ClosePopoverFn } from '../../popover';
import { ReportingStart } from '../../../../../reporting/public';
import { PDF, JSON } from '../../../../i18n/constants';
import { flattenPanelTree } from '../../../lib/flatten_panel_tree';
import { usePlatformService } from '../../../services';
import { ClosePopoverFn, Popover } from '../../popover';
import { ShareWebsiteFlyout } from './flyout';
import { CanvasWorkpadSharingData, getPdfJobParams } from './utils';

View file

@ -14,7 +14,7 @@ import {
EuiContextMenuPanelItemDescriptor,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Popover, ClosePopoverFn } from '../../popover';
import {
MAX_ZOOM_LEVEL,
MIN_ZOOM_LEVEL,
@ -22,7 +22,6 @@ import {
} from '../../../../common/lib/constants';
import { flattenPanelTree } from '../../../lib/flatten_panel_tree';
import { Popover, ClosePopoverFn } from '../../popover';
import { AutoRefreshControls } from './auto_refresh_controls';
import { KioskControls } from './kiosk_controls';

View file

@ -19,7 +19,6 @@
@import '../components/datasource/datasource';
@import '../components/datasource/datasource_preview/datasource_preview';
@import '../components/datatable/datatable';
@import '../components/debug/debug';
@import '../components/dom_preview/dom_preview';
@import '../components/element_card/element_card';
@import '../components/element_content/element_content';

View file

@ -5,8 +5,6 @@
* 2.0.
*/
import { debug } from '../canvas_plugin_src/renderers/debug';
import { error } from '../canvas_plugin_src/renderers/error';
import { image } from '../canvas_plugin_src/renderers/image';
import { repeatImage } from '../canvas_plugin_src/renderers/repeat_image';
import { markdown } from '../canvas_plugin_src/renderers/markdown';
@ -18,6 +16,10 @@ import { shape } from '../canvas_plugin_src/renderers/shape';
import { table } from '../canvas_plugin_src/renderers/table';
import { text } from '../canvas_plugin_src/renderers/text';
import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public';
import {
errorRenderer as error,
debugRenderer as debug,
} from '../../../../src/plugins/expression_error/public';
/**
* This is a collection of renderers which are bundled with the runtime. If

View file

@ -6,13 +6,15 @@
*/
import fs from 'fs';
import { ReactChildren } from 'react';
import { ReactChildren, createElement } from 'react';
import path from 'path';
import moment from 'moment';
import 'moment-timezone';
import ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import { create, act } from 'react-test-renderer';
import initStoryshots, { multiSnapshotWithOptions } from '@storybook/addon-storyshots';
import initStoryshots, { Stories2SnapsConverter } from '@storybook/addon-storyshots';
// @ts-expect-error untyped library
import styleSheetSerializer from 'jest-styled-components/src/styleSheetSerializer';
import { addSerializer } from 'jest-specific-snapshot';
@ -114,11 +116,24 @@ jest.mock('../public/lib/es_service', () => ({
addSerializer(styleSheetSerializer);
const converter = new Stories2SnapsConverter();
// Initialize Storyshots and build the Jest Snapshots
initStoryshots({
configPath: path.resolve(__dirname),
framework: 'react',
test: multiSnapshotWithOptions(),
// test: multiSnapshotWithOptions({}),
asyncJest: true,
test: async ({ story, context, done }) => {
const renderer = create(createElement(story.render));
// wait until the element will perform all renders and resolve all promises (lazy loading, especially)
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));
// save each snapshot to a different file (similar to "multiSnapshotWithOptions")
const snapshotFileName = converter.getSnapshotFileName(context);
expect(renderer).toMatchSpecificSnapshot(snapshotFileName);
done?.();
},
// Don't snapshot tests that start with 'redux'
storyNameRegex: /^((?!.*?redux).)*$/,
renderer: shallow,
});

View file

@ -31,6 +31,7 @@
{ "path": "../../../src/plugins/discover/tsconfig.json" },
{ "path": "../../../src/plugins/embeddable/tsconfig.json" },
{ "path": "../../../src/plugins/expressions/tsconfig.json" },
{ "path": "../../../src/plugins/expression_error/tsconfig.json" },
{ "path": "../../../src/plugins/expression_reveal_image/tsconfig.json" },
{ "path": "../../../src/plugins/home/tsconfig.json" },
{ "path": "../../../src/plugins/inspector/tsconfig.json" },

View file

@ -6109,9 +6109,8 @@
"xpack.canvas.error.useImportWorkpad.missingPropertiesErrorMessage": "{CANVAS} ワークパッドに必要なプロパティの一部が欠けています。 {JSON} ファイルを編集して正しいプロパティ値を入力し、再試行してください。",
"xpack.canvas.error.workpadRoutes.createFailureErrorMessage": "ワークパッドを作成できませんでした",
"xpack.canvas.error.workpadRoutes.loadFailureErrorMessage": "ID でワークパッドを読み込めませんでした",
"xpack.canvas.errorComponent.description": "表現が失敗し次のメッセージが返されました:",
"xpack.canvas.errorComponent.title": "おっと!表現が失敗しました",
"xpack.canvas.errors.useImportWorkpad.fileUploadFileWithFileNameErrorMessage": "「{fileName}」をアップロードできませんでした",
"expressionError.errorComponent.description": "表現が失敗し次のメッセージが返されました:",
"expressionError.errorComponent.title": "おっと!表現が失敗しました",
"xpack.canvas.expression.cancelButtonLabel": "キャンセル",
"xpack.canvas.expression.closeButtonLabel": "閉じる",
"xpack.canvas.expression.learnLinkText": "表現構文の詳細",
@ -6523,15 +6522,15 @@
"xpack.canvas.renderer.advancedFilter.displayName": "高度なフィルター",
"xpack.canvas.renderer.advancedFilter.helpDescription": "Canvas フィルター表現をレンダリングします。",
"xpack.canvas.renderer.advancedFilter.inputPlaceholder": "フィルター表現を入力",
"xpack.canvas.renderer.debug.displayName": "デバッグ",
"xpack.canvas.renderer.debug.helpDescription": "デバッグアウトプットをフォーマットされた {JSON} としてレンダリングします",
"expressionError.renderer.debug.displayName": "デバッグ",
"expressionError.renderer.debug.helpDescription": "デバッグアウトプットをフォーマットされた {JSON} としてレンダリングします",
"xpack.canvas.renderer.dropdownFilter.displayName": "ドロップダウンフィルター",
"xpack.canvas.renderer.dropdownFilter.helpDescription": "「{exactly}」フィルターの値を選択できるドロップダウンです",
"xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "すべて",
"xpack.canvas.renderer.embeddable.displayName": "埋め込み可能",
"xpack.canvas.renderer.embeddable.helpDescription": "Kibana の他の部分から埋め込み可能な保存済みオブジェクトをレンダリングします",
"xpack.canvas.renderer.error.displayName": "エラー情報",
"xpack.canvas.renderer.error.helpDescription": "エラーデータをユーザーにわかるようにレンダリングします",
"expressionError.renderer.error.displayName": "エラー情報",
"expressionError.renderer.error.helpDescription": "エラーデータをユーザーにわかるようにレンダリングします",
"xpack.canvas.renderer.image.displayName": "画像",
"xpack.canvas.renderer.image.helpDescription": "画像をレンダリングします",
"xpack.canvas.renderer.markdown.displayName": "マークダウン",

View file

@ -6148,9 +6148,8 @@
"xpack.canvas.error.useImportWorkpad.missingPropertiesErrorMessage": "{CANVAS} Workpad 所需的某些属性缺失。 编辑 {JSON} 文件以提供正确的属性值,然后重试。",
"xpack.canvas.error.workpadRoutes.createFailureErrorMessage": "无法创建 Workpad",
"xpack.canvas.error.workpadRoutes.loadFailureErrorMessage": "无法加载具有以下 ID 的 Workpad",
"xpack.canvas.errorComponent.description": "表达式失败,并显示消息:",
"xpack.canvas.errorComponent.title": "哎哟!表达式失败",
"xpack.canvas.errors.useImportWorkpad.fileUploadFileWithFileNameErrorMessage": "无法上传“{fileName}”",
"expressionError.errorComponent.description": "表达式失败,并显示消息:",
"expressionError.errorComponent.title": "哎哟!表达式失败",
"xpack.canvas.expression.cancelButtonLabel": "取消",
"xpack.canvas.expression.closeButtonLabel": "关闭",
"xpack.canvas.expression.learnLinkText": "学习表达式语法",
@ -6563,15 +6562,15 @@
"xpack.canvas.renderer.advancedFilter.displayName": "高级筛选",
"xpack.canvas.renderer.advancedFilter.helpDescription": "呈现 Canvas 筛选表达式",
"xpack.canvas.renderer.advancedFilter.inputPlaceholder": "输入筛选表达式",
"xpack.canvas.renderer.debug.displayName": "故障排查",
"xpack.canvas.renderer.debug.helpDescription": "将故障排查输出呈现为带格式的 {JSON}",
"expressionError.renderer.debug.displayName": "故障排查",
"expressionError.renderer.debug.helpDescription": "将故障排查输出呈现为带格式的 {JSON}",
"xpack.canvas.renderer.dropdownFilter.displayName": "下拉列表筛选",
"xpack.canvas.renderer.dropdownFilter.helpDescription": "可以从其中为“{exactly}”筛选选择值的下拉列表",
"xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "任意",
"xpack.canvas.renderer.embeddable.displayName": "可嵌入",
"xpack.canvas.renderer.embeddable.helpDescription": "从 Kibana 的其他部分呈现可嵌入的已保存对象",
"xpack.canvas.renderer.error.displayName": "错误信息",
"xpack.canvas.renderer.error.helpDescription": "以用户友好的方式呈现错误数据",
"expressionError.renderer.error.displayName": "错误信息",
"expressionError.renderer.error.helpDescription": "以用户友好的方式呈现错误数据",
"xpack.canvas.renderer.image.displayName": "图像",
"xpack.canvas.renderer.image.helpDescription": "呈现图像",
"xpack.canvas.renderer.markdown.displayName": "Markdown",