kibana/x-pack/plugins/canvas/public/components/datasource/datasource_component.js
Yaroslav Kuznietsov d54d0b5852
[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
2021-07-15 07:41:16 -04:00

214 lines
6.1 KiB
JavaScript

/*
* 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 React, { Fragment, PureComponent } from 'react';
import PropTypes from 'prop-types';
import {
EuiFlexGroup,
EuiFlexItem,
EuiButton,
EuiSpacer,
EuiIcon,
EuiCallOut,
EuiButtonEmpty,
EuiHorizontalRule,
} from '@elastic/eui';
import { isEqual } from 'lodash';
import { i18n } from '@kbn/i18n';
import { getDefaultIndex } from '../../lib/es_service';
import { DatasourceSelector } from './datasource_selector';
import { DatasourcePreview } from './datasource_preview';
const strings = {
getExpressionArgDescription: () =>
i18n.translate('xpack.canvas.datasourceDatasourceComponent.expressionArgDescription', {
defaultMessage:
'The datasource has an argument controlled by an expression. Use the expression editor to modify the datasource.',
}),
getPreviewButtonLabel: () =>
i18n.translate('xpack.canvas.datasourceDatasourceComponent.previewButtonLabel', {
defaultMessage: 'Preview data',
}),
getSaveButtonLabel: () =>
i18n.translate('xpack.canvas.datasourceDatasourceComponent.saveButtonLabel', {
defaultMessage: 'Save',
}),
};
export class DatasourceComponent extends PureComponent {
static propTypes = {
args: PropTypes.object.isRequired,
datasources: PropTypes.array.isRequired,
datasource: PropTypes.object.isRequired,
datasourceDef: PropTypes.object.isRequired,
stateDatasource: PropTypes.shape({
name: PropTypes.string.isRequired,
render: PropTypes.func.isRequired,
}).isRequired,
selectDatasource: PropTypes.func,
setDatasourceAst: PropTypes.func,
stateArgs: PropTypes.object.isRequired,
updateArgs: PropTypes.func,
resetArgs: PropTypes.func.isRequired,
selecting: PropTypes.bool,
setSelecting: PropTypes.func,
previewing: PropTypes.bool,
setPreviewing: PropTypes.func,
isInvalid: PropTypes.bool,
setInvalid: PropTypes.func,
renderError: PropTypes.func,
};
state = { defaultIndex: '' };
componentDidMount() {
getDefaultIndex().then((defaultIndex) => this.setState({ defaultIndex }));
}
componentDidUpdate(prevProps) {
const { args, resetArgs, datasource, selectDatasource } = this.props;
if (!isEqual(prevProps.args, args)) {
resetArgs();
}
if (!isEqual(prevProps.datasource, datasource)) {
selectDatasource(datasource);
}
}
getDatasourceFunctionNode = (name, args) => ({
arguments: args,
function: name,
type: 'function',
});
setSelectedDatasource = (value) => {
const {
datasource,
resetArgs,
updateArgs,
selectDatasource,
datasources,
setSelecting,
} = this.props;
if (datasource.name === value) {
// if selecting the current datasource, reset the arguments
resetArgs && resetArgs();
} else {
// otherwise, clear the arguments, the form will update them
updateArgs && updateArgs({});
}
selectDatasource && selectDatasource(datasources.find((d) => d.name === value));
setSelecting(false);
};
save = () => {
const { stateDatasource, stateArgs, setDatasourceAst } = this.props;
const datasourceAst = this.getDatasourceFunctionNode(stateDatasource.name, stateArgs);
setDatasourceAst && setDatasourceAst(datasourceAst);
};
render() {
const {
datasources,
datasourceDef,
stateDatasource,
stateArgs,
updateArgs,
selecting,
setSelecting,
previewing,
setPreviewing,
isInvalid,
setInvalid,
renderError,
} = this.props;
const { defaultIndex } = this.state;
if (selecting) {
return (
<DatasourceSelector
datasources={datasources}
onSelect={this.setSelectedDatasource}
current={stateDatasource.name}
/>
);
}
const datasourcePreview = previewing ? (
<DatasourcePreview
show={previewing}
done={() => setPreviewing(false)}
function={this.getDatasourceFunctionNode(stateDatasource.name, stateArgs)}
/>
) : null;
const datasourceRender = () =>
stateDatasource.render({
args: stateArgs,
updateArgs,
datasourceDef,
isInvalid,
setInvalid,
defaultIndex,
renderError,
});
const hasExpressionArgs = Object.values(stateArgs).some((a) => a && typeof a[0] === 'object');
return (
<Fragment>
<div className="canvasDataSource__section">
<EuiButtonEmpty
iconSide="right"
iconType="arrowRight"
onClick={() => setSelecting(!selecting)}
className="canvasDataSource__triggerButton"
flush="left"
size="s"
>
<EuiIcon type={stateDatasource.image} className="canvasDataSource__triggerButtonIcon" />
{stateDatasource.displayName}
</EuiButtonEmpty>
<EuiSpacer size="s" />
{!hasExpressionArgs ? (
<>
{datasourceRender()}
<EuiHorizontalRule margin="m" />
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiButtonEmpty size="s" onClick={() => setPreviewing(true)}>
{strings.getPreviewButtonLabel()}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
disabled={isInvalid}
size="s"
onClick={this.save}
fill
color="secondary"
>
{strings.getSaveButtonLabel()}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</>
) : (
<EuiCallOut color="warning">
<p>{strings.getExpressionArgDescription()}</p>
</EuiCallOut>
)}
</div>
{datasourcePreview}
</Fragment>
);
}
}