Skip to content

Commit 45e237c

Browse files
committed
[Canvas] Disable datasource UI when expression contains an expression argument (#79369)
* Disable datasource UI when expression contains an expression argument * Removing unnecessary type coercion # Conflicts: # x-pack/plugins/canvas/storybook/webpack.config.js
1 parent 8b50cf3 commit 45e237c

File tree

6 files changed

+152
-32
lines changed

6 files changed

+152
-32
lines changed

x-pack/plugins/canvas/.storybook/webpack.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ module.exports = async ({ config }) => {
183183
new webpack.NormalModuleReplacementPlugin(/lib\/download_workpad/, path.resolve(__dirname, '../tasks/mocks/downloadWorkpad')),
184184
new webpack.NormalModuleReplacementPlugin(/(lib)?\/custom_element_service/, path.resolve(__dirname, '../tasks/mocks/customElementService')),
185185
new webpack.NormalModuleReplacementPlugin(/(lib)?\/ui_metric/, path.resolve(__dirname, '../tasks/mocks/uiMetric')),
186+
new webpack.NormalModuleReplacementPlugin(/lib\/es_service/,path.resolve(__dirname, '../tasks/mocks/esService')),
186187
);
187188

188189
// Tell Webpack about relevant extensions

x-pack/plugins/canvas/canvas_plugin_src/uis/datasources/demodata.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
*/
66

77
import React from 'react';
8-
import { EuiText } from '@elastic/eui';
8+
import { EuiCallOut, EuiText } from '@elastic/eui';
99
import { templateFromReactComponent } from '../../../public/lib/template_from_react_component';
1010
import { DataSourceStrings } from '../../../i18n';
1111

1212
const { DemoData: strings } = DataSourceStrings;
1313

1414
const DemodataDatasource = () => (
15-
<EuiText size="s">
16-
<p>{strings.getDescription()}</p>
17-
</EuiText>
15+
<EuiCallOut title={strings.getHeading()} iconType="iInCircle">
16+
<EuiText size="s">
17+
<p>{strings.getDescription()}</p>
18+
</EuiText>
19+
</EuiCallOut>
1820
);
1921

2022
export const demodata = () => ({

x-pack/plugins/canvas/i18n/components.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,11 @@ export const ComponentStrings = {
230230
i18n.translate('xpack.canvas.datasourceDatasourceComponent.changeButtonLabel', {
231231
defaultMessage: 'Change element data source',
232232
}),
233+
getExpressionArgDescription: () =>
234+
i18n.translate('xpack.canvas.datasourceDatasourceComponent.expressionArgDescription', {
235+
defaultMessage:
236+
'The datasource has an argument controlled by an expression. Use the expression editor to modify the datasource.',
237+
}),
233238
getPreviewButtonLabel: () =>
234239
i18n.translate('xpack.canvas.datasourceDatasourceComponent.previewButtonLabel', {
235240
defaultMessage: 'Preview data',
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { action } from '@storybook/addon-actions';
8+
import { storiesOf } from '@storybook/react';
9+
import { EuiCallOut, EuiText } from '@elastic/eui';
10+
import React from 'react';
11+
// @ts-expect-error untyped local
12+
import { DatasourceComponent } from '../datasource_component';
13+
import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component';
14+
// @ts-expect-error untyped local
15+
import { Datasource } from '../../../../public/expression_types/datasource';
16+
17+
const TestDatasource = ({ args }: any) => (
18+
<EuiCallOut title="My Test Data Source" iconType="iInCircle">
19+
<EuiText size="s">
20+
<p>Hello! I am a datasource with a query arg of: {args.query}</p>
21+
</EuiText>
22+
</EuiCallOut>
23+
);
24+
25+
const testDatasource = () => ({
26+
name: 'test',
27+
displayName: 'Test Datasource',
28+
help: 'This is a test data source',
29+
image: 'training',
30+
template: templateFromReactComponent(TestDatasource),
31+
});
32+
33+
const wrappedTestDatasource = new Datasource(testDatasource());
34+
35+
const args = {
36+
query: ['select * from kibana'],
37+
};
38+
39+
storiesOf('components/datasource/DatasourceComponent', module)
40+
.addParameters({
41+
info: {
42+
inline: true,
43+
styles: {
44+
infoBody: {
45+
margin: 20,
46+
},
47+
infoStory: {
48+
margin: '40px 60px',
49+
width: '320px',
50+
},
51+
},
52+
},
53+
})
54+
.add('simple datasource', () => (
55+
<DatasourceComponent
56+
args={args}
57+
datasources={[wrappedTestDatasource]}
58+
datasource={wrappedTestDatasource}
59+
datasourceDef={{}}
60+
stateArgs={args}
61+
stateDatasource={wrappedTestDatasource}
62+
selectDatasouce={action('selectDatasouce')}
63+
setDatasourceAst={action('setDatasourceAst')}
64+
updateArgs={action('updateArgs')}
65+
resetArgs={action('resetArgs')}
66+
selecting={false}
67+
setSelecting={action('setSelecting')}
68+
previewing={false}
69+
setPreviewing={action('setPreviewing')}
70+
isInvalid={false}
71+
setInvalid={action('setInvalid')}
72+
/>
73+
))
74+
.add('datasource with expression arguments', () => (
75+
<DatasourceComponent
76+
args={{ query: [{ name: 'expression' }] }}
77+
datasources={[wrappedTestDatasource]}
78+
datasource={wrappedTestDatasource}
79+
datasourceDef={{}}
80+
stateArgs={{ query: [{ name: 'expression' }] }}
81+
stateDatasource={wrappedTestDatasource}
82+
selectDatasouce={action('selectDatasouce')}
83+
setDatasourceAst={action('setDatasourceAst')}
84+
updateArgs={action('updateArgs')}
85+
resetArgs={action('resetArgs')}
86+
selecting={false}
87+
setSelecting={action('setSelecting')}
88+
previewing={false}
89+
setPreviewing={action('setPreviewing')}
90+
isInvalid={false}
91+
setInvalid={action('setInvalid')}
92+
/>
93+
));

x-pack/plugins/canvas/public/components/datasource/datasource_component.js

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@ import {
1717
EuiHorizontalRule,
1818
} from '@elastic/eui';
1919
import { isEqual } from 'lodash';
20-
import { ComponentStrings, DataSourceStrings } from '../../../i18n';
20+
import { ComponentStrings } from '../../../i18n';
2121
import { getDefaultIndex } from '../../lib/es_service';
2222
import { DatasourceSelector } from './datasource_selector';
2323
import { DatasourcePreview } from './datasource_preview';
2424

2525
const { DatasourceDatasourceComponent: strings } = ComponentStrings;
26-
const { DemoData: demoDataStrings } = DataSourceStrings;
2726

2827
export class DatasourceComponent extends PureComponent {
2928
static propTypes = {
@@ -133,14 +132,17 @@ export class DatasourceComponent extends PureComponent {
133132
/>
134133
) : null;
135134

136-
const datasourceRender = stateDatasource.render({
137-
args: stateArgs,
138-
updateArgs,
139-
datasourceDef,
140-
isInvalid,
141-
setInvalid,
142-
defaultIndex,
143-
});
135+
const datasourceRender = () =>
136+
stateDatasource.render({
137+
args: stateArgs,
138+
updateArgs,
139+
datasourceDef,
140+
isInvalid,
141+
setInvalid,
142+
defaultIndex,
143+
});
144+
145+
const hasExpressionArgs = Object.values(stateArgs).some((a) => a && typeof a[0] === 'object');
144146

145147
return (
146148
<Fragment>
@@ -157,26 +159,34 @@ export class DatasourceComponent extends PureComponent {
157159
{stateDatasource.displayName}
158160
</EuiButtonEmpty>
159161
<EuiSpacer size="s" />
160-
{stateDatasource.name === 'demodata' ? (
161-
<EuiCallOut title={demoDataStrings.getHeading()} iconType="iInCircle">
162-
{datasourceRender}
163-
</EuiCallOut>
162+
{!hasExpressionArgs ? (
163+
<>
164+
{datasourceRender()}
165+
<EuiHorizontalRule margin="m" />
166+
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s">
167+
<EuiFlexItem grow={false}>
168+
<EuiButtonEmpty size="s" onClick={() => setPreviewing(true)}>
169+
{strings.getPreviewButtonLabel()}
170+
</EuiButtonEmpty>
171+
</EuiFlexItem>
172+
<EuiFlexItem grow={false}>
173+
<EuiButton
174+
disabled={isInvalid}
175+
size="s"
176+
onClick={this.save}
177+
fill
178+
color="secondary"
179+
>
180+
{strings.getSaveButtonLabel()}
181+
</EuiButton>
182+
</EuiFlexItem>
183+
</EuiFlexGroup>
184+
</>
164185
) : (
165-
datasourceRender
186+
<EuiCallOut color="warning">
187+
<p>{strings.getExpressionArgDescription()}</p>
188+
</EuiCallOut>
166189
)}
167-
<EuiHorizontalRule margin="m" />
168-
<EuiFlexGroup justifyContent="flexEnd" gutterSize="s">
169-
<EuiFlexItem grow={false}>
170-
<EuiButtonEmpty size="s" onClick={() => setPreviewing(true)}>
171-
{strings.getPreviewButtonLabel()}
172-
</EuiButtonEmpty>
173-
</EuiFlexItem>
174-
<EuiFlexItem grow={false}>
175-
<EuiButton disabled={isInvalid} size="s" onClick={this.save} fill color="secondary">
176-
{strings.getSaveButtonLabel()}
177-
</EuiButton>
178-
</EuiFlexItem>
179-
</EuiFlexGroup>
180190
</div>
181191

182192
{datasourcePreview}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
export function getDefaultIndex() {
8+
return Promise.resolve('default-index');
9+
}

0 commit comments

Comments
 (0)